github.com/nginxinc/kubernetes-ingress@v1.12.5/internal/configs/ingress_test.go (about) 1 package configs 2 3 import ( 4 "errors" 5 "reflect" 6 "strings" 7 "testing" 8 9 "github.com/google/go-cmp/cmp" 10 "github.com/nginxinc/kubernetes-ingress/internal/k8s/secrets" 11 v1 "k8s.io/api/core/v1" 12 networking "k8s.io/api/networking/v1beta1" 13 meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 15 "k8s.io/apimachinery/pkg/util/intstr" 16 17 "github.com/nginxinc/kubernetes-ingress/internal/configs/version1" 18 ) 19 20 func TestGenerateNginxCfg(t *testing.T) { 21 cafeIngressEx := createCafeIngressEx() 22 configParams := NewDefaultConfigParams() 23 24 isPlus := false 25 expected := createExpectedConfigForCafeIngressEx(isPlus) 26 27 apRes := AppProtectResources{} 28 result, warnings := generateNginxCfg(&cafeIngressEx, apRes, false, configParams, false, false, &StaticConfigParams{}, false) 29 30 if diff := cmp.Diff(expected, result); diff != "" { 31 t.Errorf("generateNginxCfg() returned unexpected result (-want +got):\n%s", diff) 32 } 33 if len(warnings) != 0 { 34 t.Errorf("generateNginxCfg() returned warnings: %v", warnings) 35 } 36 } 37 38 func TestGenerateNginxCfgForJWT(t *testing.T) { 39 cafeIngressEx := createCafeIngressEx() 40 cafeIngressEx.Ingress.Annotations["nginx.com/jwt-key"] = "cafe-jwk" 41 cafeIngressEx.Ingress.Annotations["nginx.com/jwt-realm"] = "Cafe App" 42 cafeIngressEx.Ingress.Annotations["nginx.com/jwt-token"] = "$cookie_auth_token" 43 cafeIngressEx.Ingress.Annotations["nginx.com/jwt-login-url"] = "https://login.example.com" 44 cafeIngressEx.SecretRefs["cafe-jwk"] = &secrets.SecretReference{ 45 Secret: &v1.Secret{ 46 Type: secrets.SecretTypeJWK, 47 }, 48 Path: "/etc/nginx/secrets/default-cafe-jwk", 49 } 50 51 configParams := NewDefaultConfigParams() 52 53 isPlus := true 54 55 expected := createExpectedConfigForCafeIngressEx(isPlus) 56 expected.Servers[0].JWTAuth = &version1.JWTAuth{ 57 Key: "/etc/nginx/secrets/default-cafe-jwk", 58 Realm: "Cafe App", 59 Token: "$cookie_auth_token", 60 RedirectLocationName: "@login_url_default-cafe-ingress", 61 } 62 expected.Servers[0].JWTRedirectLocations = []version1.JWTRedirectLocation{ 63 { 64 Name: "@login_url_default-cafe-ingress", 65 LoginURL: "https://login.example.com", 66 }, 67 } 68 69 apRes := AppProtectResources{} 70 result, warnings := generateNginxCfg(&cafeIngressEx, apRes, false, configParams, true, false, &StaticConfigParams{}, false) 71 72 if !reflect.DeepEqual(result.Servers[0].JWTAuth, expected.Servers[0].JWTAuth) { 73 t.Errorf("generateNginxCfg returned \n%v, but expected \n%v", result.Servers[0].JWTAuth, expected.Servers[0].JWTAuth) 74 } 75 if !reflect.DeepEqual(result.Servers[0].JWTRedirectLocations, expected.Servers[0].JWTRedirectLocations) { 76 t.Errorf("generateNginxCfg returned \n%v, but expected \n%v", result.Servers[0].JWTRedirectLocations, expected.Servers[0].JWTRedirectLocations) 77 } 78 if len(warnings) != 0 { 79 t.Errorf("generateNginxCfg returned warnings: %v", warnings) 80 } 81 } 82 83 func TestGenerateNginxCfgWithMissingTLSSecret(t *testing.T) { 84 cafeIngressEx := createCafeIngressEx() 85 cafeIngressEx.SecretRefs["cafe-secret"].Error = errors.New("secret doesn't exist") 86 configParams := NewDefaultConfigParams() 87 88 apRes := AppProtectResources{} 89 result, resultWarnings := generateNginxCfg(&cafeIngressEx, apRes, false, configParams, false, false, &StaticConfigParams{}, false) 90 91 expectedSSLRejectHandshake := true 92 expectedWarnings := Warnings{ 93 cafeIngressEx.Ingress: { 94 "TLS secret cafe-secret is invalid: secret doesn't exist", 95 }, 96 } 97 98 resultSSLRejectHandshake := result.Servers[0].SSLRejectHandshake 99 if !reflect.DeepEqual(resultSSLRejectHandshake, expectedSSLRejectHandshake) { 100 t.Errorf("generateNginxCfg returned SSLRejectHandshake %v, but expected %v", resultSSLRejectHandshake, expectedSSLRejectHandshake) 101 } 102 if diff := cmp.Diff(expectedWarnings, resultWarnings); diff != "" { 103 t.Errorf("generateNginxCfg returned unexpected result (-want +got):\n%s", diff) 104 } 105 } 106 107 func TestGenerateNginxCfgWithWildcardTLSSecret(t *testing.T) { 108 cafeIngressEx := createCafeIngressEx() 109 cafeIngressEx.Ingress.Spec.TLS[0].SecretName = "" 110 configParams := NewDefaultConfigParams() 111 112 apRes := AppProtectResources{} 113 result, warnings := generateNginxCfg(&cafeIngressEx, apRes, false, configParams, false, false, &StaticConfigParams{}, true) 114 115 resultServer := result.Servers[0] 116 if !reflect.DeepEqual(resultServer.SSLCertificate, pemFileNameForWildcardTLSSecret) { 117 t.Errorf("generateNginxCfg returned SSLCertificate %v, but expected %v", resultServer.SSLCertificate, pemFileNameForWildcardTLSSecret) 118 } 119 if !reflect.DeepEqual(resultServer.SSLCertificateKey, pemFileNameForWildcardTLSSecret) { 120 t.Errorf("generateNginxCfg returned SSLCertificateKey %v, but expected %v", resultServer.SSLCertificateKey, pemFileNameForWildcardTLSSecret) 121 } 122 if len(warnings) != 0 { 123 t.Errorf("generateNginxCfg returned warnings: %v", warnings) 124 } 125 } 126 127 func TestPathOrDefaultReturnDefault(t *testing.T) { 128 path := "" 129 expected := "/" 130 if pathOrDefault(path) != expected { 131 t.Errorf("pathOrDefault(%q) should return %q", path, expected) 132 } 133 } 134 135 func TestPathOrDefaultReturnActual(t *testing.T) { 136 path := "/path/to/resource" 137 if pathOrDefault(path) != path { 138 t.Errorf("pathOrDefault(%q) should return %q", path, path) 139 } 140 } 141 142 func TestGenerateIngressPath(t *testing.T) { 143 exact := networking.PathTypeExact 144 prefix := networking.PathTypePrefix 145 impSpec := networking.PathTypeImplementationSpecific 146 tests := []struct { 147 pathType *networking.PathType 148 path string 149 expected string 150 }{ 151 { 152 pathType: &exact, 153 path: "/path/to/resource", 154 expected: "= /path/to/resource", 155 }, 156 { 157 pathType: &prefix, 158 path: "/path/to/resource", 159 expected: "/path/to/resource", 160 }, 161 { 162 pathType: &impSpec, 163 path: "/path/to/resource", 164 expected: "/path/to/resource", 165 }, 166 { 167 pathType: nil, 168 path: "/path/to/resource", 169 expected: "/path/to/resource", 170 }, 171 } 172 for _, test := range tests { 173 result := generateIngressPath(test.path, test.pathType) 174 if result != test.expected { 175 t.Errorf("generateIngressPath(%v, %v) returned %v, but expected %v", test.path, test.pathType, result, test.expected) 176 } 177 } 178 } 179 180 func createExpectedConfigForCafeIngressEx(isPlus bool) version1.IngressNginxConfig { 181 coffeeUpstream := version1.Upstream{ 182 Name: "default-cafe-ingress-cafe.example.com-coffee-svc-80", 183 LBMethod: "random two least_conn", 184 UpstreamZoneSize: "256k", 185 UpstreamServers: []version1.UpstreamServer{ 186 { 187 Address: "10.0.0.1", 188 Port: "80", 189 MaxFails: 1, 190 MaxConns: 0, 191 FailTimeout: "10s", 192 }, 193 }, 194 } 195 if isPlus { 196 coffeeUpstream.UpstreamLabels = version1.UpstreamLabels{ 197 Service: "coffee-svc", 198 ResourceType: "ingress", 199 ResourceName: "cafe-ingress", 200 ResourceNamespace: "default", 201 } 202 } 203 204 teaUpstream := version1.Upstream{ 205 Name: "default-cafe-ingress-cafe.example.com-tea-svc-80", 206 LBMethod: "random two least_conn", 207 UpstreamZoneSize: "256k", 208 UpstreamServers: []version1.UpstreamServer{ 209 { 210 Address: "10.0.0.2", 211 Port: "80", 212 MaxFails: 1, 213 MaxConns: 0, 214 FailTimeout: "10s", 215 }, 216 }, 217 } 218 if isPlus { 219 teaUpstream.UpstreamLabels = version1.UpstreamLabels{ 220 Service: "tea-svc", 221 ResourceType: "ingress", 222 ResourceName: "cafe-ingress", 223 ResourceNamespace: "default", 224 } 225 } 226 227 expected := version1.IngressNginxConfig{ 228 Upstreams: []version1.Upstream{ 229 coffeeUpstream, 230 teaUpstream, 231 }, 232 Servers: []version1.Server{ 233 { 234 Name: "cafe.example.com", 235 ServerTokens: "on", 236 Locations: []version1.Location{ 237 { 238 Path: "/coffee", 239 ServiceName: "coffee-svc", 240 Upstream: coffeeUpstream, 241 ProxyConnectTimeout: "60s", 242 ProxyReadTimeout: "60s", 243 ProxySendTimeout: "60s", 244 ClientMaxBodySize: "1m", 245 ProxyBuffering: true, 246 ProxySSLName: "coffee-svc.default.svc", 247 }, 248 { 249 Path: "/tea", 250 ServiceName: "tea-svc", 251 Upstream: teaUpstream, 252 ProxyConnectTimeout: "60s", 253 ProxyReadTimeout: "60s", 254 ProxySendTimeout: "60s", 255 ClientMaxBodySize: "1m", 256 ProxyBuffering: true, 257 ProxySSLName: "tea-svc.default.svc", 258 }, 259 }, 260 SSL: true, 261 SSLCertificate: "/etc/nginx/secrets/default-cafe-secret", 262 SSLCertificateKey: "/etc/nginx/secrets/default-cafe-secret", 263 StatusZone: "cafe.example.com", 264 HSTSMaxAge: 2592000, 265 Ports: []int{80}, 266 SSLPorts: []int{443}, 267 SSLRedirect: true, 268 HealthChecks: make(map[string]version1.HealthCheck), 269 }, 270 }, 271 Ingress: version1.Ingress{ 272 Name: "cafe-ingress", 273 Namespace: "default", 274 Annotations: map[string]string{ 275 "kubernetes.io/ingress.class": "nginx", 276 }, 277 }, 278 } 279 return expected 280 } 281 282 func createCafeIngressEx() IngressEx { 283 cafeIngress := networking.Ingress{ 284 ObjectMeta: meta_v1.ObjectMeta{ 285 Name: "cafe-ingress", 286 Namespace: "default", 287 Annotations: map[string]string{ 288 "kubernetes.io/ingress.class": "nginx", 289 }, 290 }, 291 Spec: networking.IngressSpec{ 292 TLS: []networking.IngressTLS{ 293 { 294 Hosts: []string{"cafe.example.com"}, 295 SecretName: "cafe-secret", 296 }, 297 }, 298 Rules: []networking.IngressRule{ 299 { 300 Host: "cafe.example.com", 301 IngressRuleValue: networking.IngressRuleValue{ 302 HTTP: &networking.HTTPIngressRuleValue{ 303 Paths: []networking.HTTPIngressPath{ 304 { 305 Path: "/coffee", 306 Backend: networking.IngressBackend{ 307 ServiceName: "coffee-svc", 308 ServicePort: intstr.FromString("80"), 309 }, 310 }, 311 { 312 Path: "/tea", 313 Backend: networking.IngressBackend{ 314 ServiceName: "tea-svc", 315 ServicePort: intstr.FromString("80"), 316 }, 317 }, 318 }, 319 }, 320 }, 321 }, 322 }, 323 }, 324 } 325 cafeIngressEx := IngressEx{ 326 Ingress: &cafeIngress, 327 Endpoints: map[string][]string{ 328 "coffee-svc80": {"10.0.0.1:80"}, 329 "tea-svc80": {"10.0.0.2:80"}, 330 }, 331 ExternalNameSvcs: map[string]bool{}, 332 ValidHosts: map[string]bool{ 333 "cafe.example.com": true, 334 }, 335 SecretRefs: map[string]*secrets.SecretReference{ 336 "cafe-secret": { 337 Secret: &v1.Secret{ 338 Type: v1.SecretTypeTLS, 339 }, 340 Path: "/etc/nginx/secrets/default-cafe-secret", 341 }, 342 }, 343 } 344 return cafeIngressEx 345 } 346 347 func TestGenerateNginxCfgForMergeableIngresses(t *testing.T) { 348 mergeableIngresses := createMergeableCafeIngress() 349 350 isPlus := false 351 expected := createExpectedConfigForMergeableCafeIngress(isPlus) 352 353 configParams := NewDefaultConfigParams() 354 355 masterApRes := AppProtectResources{} 356 result, warnings := generateNginxCfgForMergeableIngresses(mergeableIngresses, masterApRes, configParams, false, false, &StaticConfigParams{}, false) 357 358 if diff := cmp.Diff(expected, result); diff != "" { 359 t.Errorf("generateNginxCfgForMergeableIngresses() returned unexpected result (-want +got):\n%s", diff) 360 } 361 if len(warnings) != 0 { 362 t.Errorf("generateNginxCfgForMergeableIngresses() returned warnings: %v", warnings) 363 } 364 } 365 366 func TestGenerateNginxConfigForCrossNamespaceMergeableIngresses(t *testing.T) { 367 mergeableIngresses := createMergeableCafeIngress() 368 // change the namespaces of the minions to be coffee and tea 369 for i, m := range mergeableIngresses.Minions { 370 if strings.Contains(m.Ingress.Name, "coffee") { 371 mergeableIngresses.Minions[i].Ingress.Namespace = "coffee" 372 } else { 373 mergeableIngresses.Minions[i].Ingress.Namespace = "tea" 374 } 375 } 376 377 expected := createExpectedConfigForCrossNamespaceMergeableCafeIngress() 378 configParams := NewDefaultConfigParams() 379 380 emptyApResources := AppProtectResources{} 381 result, warnings := generateNginxCfgForMergeableIngresses(mergeableIngresses, emptyApResources, configParams, false, false, &StaticConfigParams{}, false) 382 383 if diff := cmp.Diff(expected, result); diff != "" { 384 t.Errorf("generateNginxCfgForMergeableIngresses() returned unexpected result (-want +got):\n%s", diff) 385 } 386 if len(warnings) != 0 { 387 t.Errorf("generateNginxCfgForMergeableIngresses() returned warnings: %v", warnings) 388 } 389 } 390 391 func TestGenerateNginxCfgForMergeableIngressesForJWT(t *testing.T) { 392 mergeableIngresses := createMergeableCafeIngress() 393 mergeableIngresses.Master.Ingress.Annotations["nginx.com/jwt-key"] = "cafe-jwk" 394 mergeableIngresses.Master.Ingress.Annotations["nginx.com/jwt-realm"] = "Cafe" 395 mergeableIngresses.Master.Ingress.Annotations["nginx.com/jwt-token"] = "$cookie_auth_token" 396 mergeableIngresses.Master.Ingress.Annotations["nginx.com/jwt-login-url"] = "https://login.example.com" 397 mergeableIngresses.Master.SecretRefs["cafe-jwk"] = &secrets.SecretReference{ 398 Secret: &v1.Secret{ 399 Type: secrets.SecretTypeJWK, 400 }, 401 Path: "/etc/nginx/secrets/default-cafe-jwk", 402 } 403 404 mergeableIngresses.Minions[0].Ingress.Annotations["nginx.com/jwt-key"] = "coffee-jwk" 405 mergeableIngresses.Minions[0].Ingress.Annotations["nginx.com/jwt-realm"] = "Coffee" 406 mergeableIngresses.Minions[0].Ingress.Annotations["nginx.com/jwt-token"] = "$cookie_auth_token_coffee" 407 mergeableIngresses.Minions[0].Ingress.Annotations["nginx.com/jwt-login-url"] = "https://login.cofee.example.com" 408 mergeableIngresses.Minions[0].SecretRefs["coffee-jwk"] = &secrets.SecretReference{ 409 Secret: &v1.Secret{ 410 Type: secrets.SecretTypeJWK, 411 }, 412 Path: "/etc/nginx/secrets/default-coffee-jwk", 413 } 414 415 isPlus := true 416 417 expected := createExpectedConfigForMergeableCafeIngress(isPlus) 418 expected.Servers[0].JWTAuth = &version1.JWTAuth{ 419 Key: "/etc/nginx/secrets/default-cafe-jwk", 420 Realm: "Cafe", 421 Token: "$cookie_auth_token", 422 RedirectLocationName: "@login_url_default-cafe-ingress-master", 423 } 424 expected.Servers[0].Locations[0].JWTAuth = &version1.JWTAuth{ 425 Key: "/etc/nginx/secrets/default-coffee-jwk", 426 Realm: "Coffee", 427 Token: "$cookie_auth_token_coffee", 428 RedirectLocationName: "@login_url_default-cafe-ingress-coffee-minion", 429 } 430 expected.Servers[0].JWTRedirectLocations = []version1.JWTRedirectLocation{ 431 { 432 Name: "@login_url_default-cafe-ingress-master", 433 LoginURL: "https://login.example.com", 434 }, 435 { 436 Name: "@login_url_default-cafe-ingress-coffee-minion", 437 LoginURL: "https://login.cofee.example.com", 438 }, 439 } 440 441 minionJwtKeyFileNames := make(map[string]string) 442 minionJwtKeyFileNames[objectMetaToFileName(&mergeableIngresses.Minions[0].Ingress.ObjectMeta)] = "/etc/nginx/secrets/default-coffee-jwk" 443 configParams := NewDefaultConfigParams() 444 445 masterApRes := AppProtectResources{} 446 result, warnings := generateNginxCfgForMergeableIngresses(mergeableIngresses, masterApRes, configParams, isPlus, false, &StaticConfigParams{}, false) 447 448 if !reflect.DeepEqual(result.Servers[0].JWTAuth, expected.Servers[0].JWTAuth) { 449 t.Errorf("generateNginxCfgForMergeableIngresses returned \n%v, but expected \n%v", result.Servers[0].JWTAuth, expected.Servers[0].JWTAuth) 450 } 451 if !reflect.DeepEqual(result.Servers[0].Locations[0].JWTAuth, expected.Servers[0].Locations[0].JWTAuth) { 452 t.Errorf("generateNginxCfgForMergeableIngresses returned \n%v, but expected \n%v", result.Servers[0].Locations[0].JWTAuth, expected.Servers[0].Locations[0].JWTAuth) 453 } 454 if !reflect.DeepEqual(result.Servers[0].JWTRedirectLocations, expected.Servers[0].JWTRedirectLocations) { 455 t.Errorf("generateNginxCfgForMergeableIngresses returned \n%v, but expected \n%v", result.Servers[0].JWTRedirectLocations, expected.Servers[0].JWTRedirectLocations) 456 } 457 if len(warnings) != 0 { 458 t.Errorf("generateNginxCfgForMergeableIngresses returned warnings: %v", warnings) 459 } 460 } 461 462 func createMergeableCafeIngress() *MergeableIngresses { 463 master := networking.Ingress{ 464 ObjectMeta: meta_v1.ObjectMeta{ 465 Name: "cafe-ingress-master", 466 Namespace: "default", 467 Annotations: map[string]string{ 468 "kubernetes.io/ingress.class": "nginx", 469 "nginx.org/mergeable-ingress-type": "master", 470 }, 471 }, 472 Spec: networking.IngressSpec{ 473 TLS: []networking.IngressTLS{ 474 { 475 Hosts: []string{"cafe.example.com"}, 476 SecretName: "cafe-secret", 477 }, 478 }, 479 Rules: []networking.IngressRule{ 480 { 481 Host: "cafe.example.com", 482 IngressRuleValue: networking.IngressRuleValue{ 483 HTTP: &networking.HTTPIngressRuleValue{ // HTTP must not be nil for Master 484 Paths: []networking.HTTPIngressPath{}, 485 }, 486 }, 487 }, 488 }, 489 }, 490 } 491 492 coffeeMinion := networking.Ingress{ 493 ObjectMeta: meta_v1.ObjectMeta{ 494 Name: "cafe-ingress-coffee-minion", 495 Namespace: "default", 496 Annotations: map[string]string{ 497 "kubernetes.io/ingress.class": "nginx", 498 "nginx.org/mergeable-ingress-type": "minion", 499 }, 500 }, 501 Spec: networking.IngressSpec{ 502 Rules: []networking.IngressRule{ 503 { 504 Host: "cafe.example.com", 505 IngressRuleValue: networking.IngressRuleValue{ 506 HTTP: &networking.HTTPIngressRuleValue{ 507 Paths: []networking.HTTPIngressPath{ 508 { 509 Path: "/coffee", 510 Backend: networking.IngressBackend{ 511 ServiceName: "coffee-svc", 512 ServicePort: intstr.FromString("80"), 513 }, 514 }, 515 }, 516 }, 517 }, 518 }, 519 }, 520 }, 521 } 522 523 teaMinion := networking.Ingress{ 524 ObjectMeta: meta_v1.ObjectMeta{ 525 Name: "cafe-ingress-tea-minion", 526 Namespace: "default", 527 Annotations: map[string]string{ 528 "kubernetes.io/ingress.class": "nginx", 529 "nginx.org/mergeable-ingress-type": "minion", 530 }, 531 }, 532 Spec: networking.IngressSpec{ 533 Rules: []networking.IngressRule{ 534 { 535 Host: "cafe.example.com", 536 IngressRuleValue: networking.IngressRuleValue{ 537 HTTP: &networking.HTTPIngressRuleValue{ 538 Paths: []networking.HTTPIngressPath{ 539 { 540 Path: "/tea", 541 Backend: networking.IngressBackend{ 542 ServiceName: "tea-svc", 543 ServicePort: intstr.FromString("80"), 544 }, 545 }, 546 }, 547 }, 548 }, 549 }, 550 }, 551 }, 552 } 553 554 mergeableIngresses := &MergeableIngresses{ 555 Master: &IngressEx{ 556 Ingress: &master, 557 Endpoints: map[string][]string{ 558 "coffee-svc80": {"10.0.0.1:80"}, 559 "tea-svc80": {"10.0.0.2:80"}, 560 }, 561 ValidHosts: map[string]bool{ 562 "cafe.example.com": true, 563 }, 564 SecretRefs: map[string]*secrets.SecretReference{ 565 "cafe-secret": { 566 Secret: &v1.Secret{ 567 Type: v1.SecretTypeTLS, 568 }, 569 Path: "/etc/nginx/secrets/default-cafe-secret", 570 Error: nil, 571 }, 572 }, 573 }, 574 Minions: []*IngressEx{ 575 { 576 Ingress: &coffeeMinion, 577 Endpoints: map[string][]string{ 578 "coffee-svc80": {"10.0.0.1:80"}, 579 }, 580 ValidHosts: map[string]bool{ 581 "cafe.example.com": true, 582 }, 583 ValidMinionPaths: map[string]bool{ 584 "/coffee": true, 585 }, 586 SecretRefs: map[string]*secrets.SecretReference{}, 587 }, 588 { 589 Ingress: &teaMinion, 590 Endpoints: map[string][]string{ 591 "tea-svc80": {"10.0.0.2:80"}, 592 }, 593 ValidHosts: map[string]bool{ 594 "cafe.example.com": true, 595 }, 596 ValidMinionPaths: map[string]bool{ 597 "/tea": true, 598 }, 599 SecretRefs: map[string]*secrets.SecretReference{}, 600 }}, 601 } 602 603 return mergeableIngresses 604 } 605 606 func createExpectedConfigForMergeableCafeIngress(isPlus bool) version1.IngressNginxConfig { 607 coffeeUpstream := version1.Upstream{ 608 Name: "default-cafe-ingress-coffee-minion-cafe.example.com-coffee-svc-80", 609 LBMethod: "random two least_conn", 610 UpstreamZoneSize: "256k", 611 UpstreamServers: []version1.UpstreamServer{ 612 { 613 Address: "10.0.0.1", 614 Port: "80", 615 MaxFails: 1, 616 MaxConns: 0, 617 FailTimeout: "10s", 618 }, 619 }, 620 } 621 if isPlus { 622 coffeeUpstream.UpstreamLabels = version1.UpstreamLabels{ 623 Service: "coffee-svc", 624 ResourceType: "ingress", 625 ResourceName: "cafe-ingress-coffee-minion", 626 ResourceNamespace: "default", 627 } 628 } 629 630 teaUpstream := version1.Upstream{ 631 Name: "default-cafe-ingress-tea-minion-cafe.example.com-tea-svc-80", 632 LBMethod: "random two least_conn", 633 UpstreamZoneSize: "256k", 634 UpstreamServers: []version1.UpstreamServer{ 635 { 636 Address: "10.0.0.2", 637 Port: "80", 638 MaxFails: 1, 639 MaxConns: 0, 640 FailTimeout: "10s", 641 }, 642 }, 643 } 644 if isPlus { 645 teaUpstream.UpstreamLabels = version1.UpstreamLabels{ 646 Service: "tea-svc", 647 ResourceType: "ingress", 648 ResourceName: "cafe-ingress-tea-minion", 649 ResourceNamespace: "default", 650 } 651 } 652 653 expected := version1.IngressNginxConfig{ 654 Upstreams: []version1.Upstream{ 655 coffeeUpstream, 656 teaUpstream, 657 }, 658 Servers: []version1.Server{ 659 { 660 Name: "cafe.example.com", 661 ServerTokens: "on", 662 Locations: []version1.Location{ 663 { 664 Path: "/coffee", 665 ServiceName: "coffee-svc", 666 Upstream: coffeeUpstream, 667 ProxyConnectTimeout: "60s", 668 ProxyReadTimeout: "60s", 669 ProxySendTimeout: "60s", 670 ClientMaxBodySize: "1m", 671 ProxyBuffering: true, 672 MinionIngress: &version1.Ingress{ 673 Name: "cafe-ingress-coffee-minion", 674 Namespace: "default", 675 Annotations: map[string]string{ 676 "kubernetes.io/ingress.class": "nginx", 677 "nginx.org/mergeable-ingress-type": "minion", 678 }, 679 }, 680 ProxySSLName: "coffee-svc.default.svc", 681 }, 682 { 683 Path: "/tea", 684 ServiceName: "tea-svc", 685 Upstream: teaUpstream, 686 ProxyConnectTimeout: "60s", 687 ProxyReadTimeout: "60s", 688 ProxySendTimeout: "60s", 689 ClientMaxBodySize: "1m", 690 ProxyBuffering: true, 691 MinionIngress: &version1.Ingress{ 692 Name: "cafe-ingress-tea-minion", 693 Namespace: "default", 694 Annotations: map[string]string{ 695 "kubernetes.io/ingress.class": "nginx", 696 "nginx.org/mergeable-ingress-type": "minion", 697 }, 698 }, 699 ProxySSLName: "tea-svc.default.svc", 700 }, 701 }, 702 SSL: true, 703 SSLCertificate: "/etc/nginx/secrets/default-cafe-secret", 704 SSLCertificateKey: "/etc/nginx/secrets/default-cafe-secret", 705 StatusZone: "cafe.example.com", 706 HSTSMaxAge: 2592000, 707 Ports: []int{80}, 708 SSLPorts: []int{443}, 709 SSLRedirect: true, 710 HealthChecks: make(map[string]version1.HealthCheck), 711 }, 712 }, 713 Ingress: version1.Ingress{ 714 Name: "cafe-ingress-master", 715 Namespace: "default", 716 Annotations: map[string]string{ 717 "kubernetes.io/ingress.class": "nginx", 718 "nginx.org/mergeable-ingress-type": "master", 719 }, 720 }, 721 } 722 723 return expected 724 } 725 726 func createExpectedConfigForCrossNamespaceMergeableCafeIngress() version1.IngressNginxConfig { 727 coffeeUpstream := version1.Upstream{ 728 Name: "coffee-cafe-ingress-coffee-minion-cafe.example.com-coffee-svc-80", 729 LBMethod: "random two least_conn", 730 UpstreamZoneSize: "256k", 731 UpstreamServers: []version1.UpstreamServer{ 732 { 733 Address: "10.0.0.1", 734 Port: "80", 735 MaxFails: 1, 736 MaxConns: 0, 737 FailTimeout: "10s", 738 }, 739 }, 740 } 741 teaUpstream := version1.Upstream{ 742 Name: "tea-cafe-ingress-tea-minion-cafe.example.com-tea-svc-80", 743 LBMethod: "random two least_conn", 744 UpstreamZoneSize: "256k", 745 UpstreamServers: []version1.UpstreamServer{ 746 { 747 Address: "10.0.0.2", 748 Port: "80", 749 MaxFails: 1, 750 MaxConns: 0, 751 FailTimeout: "10s", 752 }, 753 }, 754 } 755 expected := version1.IngressNginxConfig{ 756 Upstreams: []version1.Upstream{ 757 coffeeUpstream, 758 teaUpstream, 759 }, 760 Servers: []version1.Server{ 761 { 762 Name: "cafe.example.com", 763 ServerTokens: "on", 764 Locations: []version1.Location{ 765 { 766 Path: "/coffee", 767 ServiceName: "coffee-svc", 768 Upstream: coffeeUpstream, 769 ProxyConnectTimeout: "60s", 770 ProxyReadTimeout: "60s", 771 ProxySendTimeout: "60s", 772 ClientMaxBodySize: "1m", 773 ProxyBuffering: true, 774 MinionIngress: &version1.Ingress{ 775 Name: "cafe-ingress-coffee-minion", 776 Namespace: "coffee", 777 Annotations: map[string]string{ 778 "kubernetes.io/ingress.class": "nginx", 779 "nginx.org/mergeable-ingress-type": "minion", 780 }, 781 }, 782 ProxySSLName: "coffee-svc.coffee.svc", 783 }, 784 { 785 Path: "/tea", 786 ServiceName: "tea-svc", 787 Upstream: teaUpstream, 788 ProxyConnectTimeout: "60s", 789 ProxyReadTimeout: "60s", 790 ProxySendTimeout: "60s", 791 ClientMaxBodySize: "1m", 792 ProxyBuffering: true, 793 MinionIngress: &version1.Ingress{ 794 Name: "cafe-ingress-tea-minion", 795 Namespace: "tea", 796 Annotations: map[string]string{ 797 "kubernetes.io/ingress.class": "nginx", 798 "nginx.org/mergeable-ingress-type": "minion", 799 }, 800 }, 801 ProxySSLName: "tea-svc.tea.svc", 802 }, 803 }, 804 SSL: true, 805 SSLCertificate: "/etc/nginx/secrets/default-cafe-secret", 806 SSLCertificateKey: "/etc/nginx/secrets/default-cafe-secret", 807 StatusZone: "cafe.example.com", 808 HSTSMaxAge: 2592000, 809 Ports: []int{80}, 810 SSLPorts: []int{443}, 811 SSLRedirect: true, 812 HealthChecks: make(map[string]version1.HealthCheck), 813 }, 814 }, 815 Ingress: version1.Ingress{ 816 Name: "cafe-ingress-master", 817 Namespace: "default", 818 Annotations: map[string]string{ 819 "kubernetes.io/ingress.class": "nginx", 820 "nginx.org/mergeable-ingress-type": "master", 821 }, 822 }, 823 } 824 825 return expected 826 } 827 828 func TestGenerateNginxCfgForSpiffe(t *testing.T) { 829 cafeIngressEx := createCafeIngressEx() 830 configParams := NewDefaultConfigParams() 831 832 isPlus := false 833 834 expected := createExpectedConfigForCafeIngressEx(isPlus) 835 expected.SpiffeClientCerts = true 836 for i := range expected.Servers[0].Locations { 837 expected.Servers[0].Locations[i].SSL = true 838 } 839 840 apResources := AppProtectResources{} 841 result, warnings := generateNginxCfg(&cafeIngressEx, apResources, false, configParams, false, false, 842 &StaticConfigParams{NginxServiceMesh: true}, false) 843 844 if diff := cmp.Diff(expected, result); diff != "" { 845 t.Errorf("generateNginxCfg() returned unexpected result (-want +got):\n%s", diff) 846 } 847 if len(warnings) != 0 { 848 t.Errorf("generateNginxCfg() returned warnings: %v", warnings) 849 } 850 } 851 852 func TestGenerateNginxCfgForInternalRoute(t *testing.T) { 853 internalRouteAnnotation := "nsm.nginx.com/internal-route" 854 cafeIngressEx := createCafeIngressEx() 855 cafeIngressEx.Ingress.Annotations[internalRouteAnnotation] = "true" 856 configParams := NewDefaultConfigParams() 857 858 isPlus := false 859 860 expected := createExpectedConfigForCafeIngressEx(isPlus) 861 expected.Servers[0].SpiffeCerts = true 862 expected.Ingress.Annotations[internalRouteAnnotation] = "true" 863 864 apResources := AppProtectResources{} 865 result, warnings := generateNginxCfg(&cafeIngressEx, apResources, false, configParams, false, false, 866 &StaticConfigParams{NginxServiceMesh: true, EnableInternalRoutes: true}, false) 867 868 if diff := cmp.Diff(expected, result); diff != "" { 869 t.Errorf("generateNginxCfg() returned unexpected result (-want +got):\n%s", diff) 870 } 871 if len(warnings) != 0 { 872 t.Errorf("generateNginxCfg() returned warnings: %v", warnings) 873 } 874 } 875 876 func TestIsSSLEnabled(t *testing.T) { 877 type testCase struct { 878 IsSSLService, 879 SpiffeServerCerts, 880 NginxServiceMesh, 881 Expected bool 882 } 883 var testCases = []testCase{ 884 { 885 IsSSLService: false, 886 SpiffeServerCerts: false, 887 NginxServiceMesh: false, 888 Expected: false, 889 }, 890 { 891 IsSSLService: false, 892 SpiffeServerCerts: true, 893 NginxServiceMesh: true, 894 Expected: false, 895 }, 896 { 897 IsSSLService: false, 898 SpiffeServerCerts: false, 899 NginxServiceMesh: true, 900 Expected: true, 901 }, 902 { 903 IsSSLService: false, 904 SpiffeServerCerts: true, 905 NginxServiceMesh: false, 906 Expected: false, 907 }, 908 { 909 IsSSLService: true, 910 SpiffeServerCerts: true, 911 NginxServiceMesh: true, 912 Expected: true, 913 }, 914 { 915 IsSSLService: true, 916 SpiffeServerCerts: false, 917 NginxServiceMesh: true, 918 Expected: true, 919 }, 920 { 921 IsSSLService: true, 922 SpiffeServerCerts: true, 923 NginxServiceMesh: false, 924 Expected: true, 925 }, 926 { 927 IsSSLService: true, 928 SpiffeServerCerts: false, 929 NginxServiceMesh: false, 930 Expected: true, 931 }, 932 } 933 for i, tc := range testCases { 934 actual := isSSLEnabled(tc.IsSSLService, ConfigParams{SpiffeServerCerts: tc.SpiffeServerCerts}, &StaticConfigParams{NginxServiceMesh: tc.NginxServiceMesh}) 935 if actual != tc.Expected { 936 t.Errorf("isSSLEnabled returned %v but expected %v for the case %v", actual, tc.Expected, i) 937 } 938 } 939 } 940 941 func TestAddSSLConfig(t *testing.T) { 942 tests := []struct { 943 host string 944 tls []networking.IngressTLS 945 secretRefs map[string]*secrets.SecretReference 946 isWildcardEnabled bool 947 expectedServer version1.Server 948 expectedWarnings Warnings 949 msg string 950 }{ 951 { 952 host: "some.example.com", 953 tls: []networking.IngressTLS{ 954 { 955 Hosts: []string{"cafe.example.com"}, 956 SecretName: "cafe-secret", 957 }, 958 }, 959 secretRefs: map[string]*secrets.SecretReference{ 960 "cafe-secret": { 961 Secret: &v1.Secret{ 962 Type: v1.SecretTypeTLS, 963 }, 964 Path: "/etc/nginx/secrets/default-cafe-secret", 965 }, 966 }, 967 isWildcardEnabled: false, 968 expectedServer: version1.Server{}, 969 expectedWarnings: Warnings{}, 970 msg: "TLS termination for different host", 971 }, 972 { 973 host: "cafe.example.com", 974 tls: []networking.IngressTLS{ 975 { 976 Hosts: []string{"cafe.example.com"}, 977 SecretName: "cafe-secret", 978 }, 979 }, 980 secretRefs: map[string]*secrets.SecretReference{ 981 "cafe-secret": { 982 Secret: &v1.Secret{ 983 Type: v1.SecretTypeTLS, 984 }, 985 Path: "/etc/nginx/secrets/default-cafe-secret", 986 }, 987 }, 988 isWildcardEnabled: false, 989 expectedServer: version1.Server{ 990 SSL: true, 991 SSLCertificate: "/etc/nginx/secrets/default-cafe-secret", 992 SSLCertificateKey: "/etc/nginx/secrets/default-cafe-secret", 993 }, 994 expectedWarnings: Warnings{}, 995 msg: "TLS termination", 996 }, 997 { 998 host: "cafe.example.com", 999 tls: []networking.IngressTLS{ 1000 { 1001 Hosts: []string{"cafe.example.com"}, 1002 SecretName: "cafe-secret", 1003 }, 1004 }, 1005 secretRefs: map[string]*secrets.SecretReference{ 1006 "cafe-secret": { 1007 Secret: &v1.Secret{ 1008 Type: v1.SecretTypeTLS, 1009 }, 1010 Error: errors.New("invalid secret"), 1011 }, 1012 }, 1013 isWildcardEnabled: false, 1014 expectedServer: version1.Server{ 1015 SSL: true, 1016 SSLRejectHandshake: true, 1017 }, 1018 expectedWarnings: Warnings{ 1019 nil: { 1020 "TLS secret cafe-secret is invalid: invalid secret", 1021 }, 1022 }, 1023 msg: "invalid secret", 1024 }, 1025 { 1026 host: "cafe.example.com", 1027 tls: []networking.IngressTLS{ 1028 { 1029 Hosts: []string{"cafe.example.com"}, 1030 SecretName: "cafe-secret", 1031 }, 1032 }, 1033 secretRefs: map[string]*secrets.SecretReference{ 1034 "cafe-secret": { 1035 Secret: &v1.Secret{ 1036 Type: secrets.SecretTypeCA, 1037 }, 1038 Path: "/etc/nginx/secrets/default-cafe-secret", 1039 }, 1040 }, 1041 isWildcardEnabled: false, 1042 expectedServer: version1.Server{ 1043 SSL: true, 1044 SSLRejectHandshake: true, 1045 }, 1046 expectedWarnings: Warnings{ 1047 nil: { 1048 "TLS secret cafe-secret is of a wrong type 'nginx.org/ca', must be 'kubernetes.io/tls'", 1049 }, 1050 }, 1051 msg: "secret of wrong type without error", 1052 }, 1053 { 1054 host: "cafe.example.com", 1055 tls: []networking.IngressTLS{ 1056 { 1057 Hosts: []string{"cafe.example.com"}, 1058 SecretName: "cafe-secret", 1059 }, 1060 }, 1061 secretRefs: map[string]*secrets.SecretReference{ 1062 "cafe-secret": { 1063 Secret: &v1.Secret{ 1064 Type: secrets.SecretTypeCA, 1065 }, 1066 Path: "", 1067 Error: errors.New("CA secret must have the data field ca.crt"), 1068 }, 1069 }, 1070 isWildcardEnabled: false, 1071 expectedServer: version1.Server{ 1072 SSL: true, 1073 SSLRejectHandshake: true, 1074 }, 1075 expectedWarnings: Warnings{ 1076 nil: { 1077 "TLS secret cafe-secret is of a wrong type 'nginx.org/ca', must be 'kubernetes.io/tls'", 1078 }, 1079 }, 1080 msg: "secret of wrong type with error", 1081 }, 1082 { 1083 host: "cafe.example.com", 1084 tls: []networking.IngressTLS{ 1085 { 1086 Hosts: []string{"cafe.example.com"}, 1087 SecretName: "", 1088 }, 1089 }, 1090 isWildcardEnabled: true, 1091 expectedServer: version1.Server{ 1092 SSL: true, 1093 SSLCertificate: pemFileNameForWildcardTLSSecret, 1094 SSLCertificateKey: pemFileNameForWildcardTLSSecret, 1095 }, 1096 expectedWarnings: Warnings{}, 1097 msg: "no secret name with wildcard enabled", 1098 }, 1099 { 1100 host: "cafe.example.com", 1101 tls: []networking.IngressTLS{ 1102 { 1103 Hosts: []string{"cafe.example.com"}, 1104 SecretName: "", 1105 }, 1106 }, 1107 isWildcardEnabled: false, 1108 expectedServer: version1.Server{ 1109 SSL: true, 1110 SSLRejectHandshake: true, 1111 }, 1112 expectedWarnings: Warnings{ 1113 nil: { 1114 "TLS termination for host 'cafe.example.com' requires specifying a TLS secret or configuring a global wildcard TLS secret", 1115 }, 1116 }, 1117 msg: "no secret name with wildcard disabled", 1118 }, 1119 } 1120 1121 for _, test := range tests { 1122 var server version1.Server 1123 1124 // it is ok to use nil as the owner 1125 warnings := addSSLConfig(&server, nil, test.host, test.tls, test.secretRefs, test.isWildcardEnabled) 1126 1127 if diff := cmp.Diff(test.expectedServer, server); diff != "" { 1128 t.Errorf("addSSLConfig() '%s' mismatch (-want +got):\n%s", test.msg, diff) 1129 } 1130 if !reflect.DeepEqual(test.expectedWarnings, warnings) { 1131 t.Errorf("addSSLConfig() returned %v but expected %v for the case of %s", warnings, test.expectedWarnings, test.msg) 1132 } 1133 } 1134 } 1135 1136 func TestGenerateJWTConfig(t *testing.T) { 1137 tests := []struct { 1138 secretRefs map[string]*secrets.SecretReference 1139 cfgParams *ConfigParams 1140 redirectLocationName string 1141 expectedJWTAuth *version1.JWTAuth 1142 expectedRedirectLocation *version1.JWTRedirectLocation 1143 expectedWarnings Warnings 1144 msg string 1145 }{ 1146 { 1147 secretRefs: map[string]*secrets.SecretReference{ 1148 "cafe-jwk": { 1149 Secret: &v1.Secret{ 1150 Type: secrets.SecretTypeJWK, 1151 }, 1152 Path: "/etc/nginx/secrets/default-cafe-jwk", 1153 }, 1154 }, 1155 cfgParams: &ConfigParams{ 1156 JWTKey: "cafe-jwk", 1157 JWTRealm: "cafe", 1158 JWTToken: "$http_token", 1159 }, 1160 redirectLocationName: "@loc", 1161 expectedJWTAuth: &version1.JWTAuth{ 1162 Key: "/etc/nginx/secrets/default-cafe-jwk", 1163 Realm: "cafe", 1164 Token: "$http_token", 1165 }, 1166 expectedRedirectLocation: nil, 1167 expectedWarnings: Warnings{}, 1168 msg: "normal case", 1169 }, 1170 { 1171 secretRefs: map[string]*secrets.SecretReference{ 1172 "cafe-jwk": { 1173 Secret: &v1.Secret{ 1174 Type: secrets.SecretTypeJWK, 1175 }, 1176 Path: "/etc/nginx/secrets/default-cafe-jwk", 1177 }, 1178 }, 1179 cfgParams: &ConfigParams{ 1180 JWTKey: "cafe-jwk", 1181 JWTRealm: "cafe", 1182 JWTToken: "$http_token", 1183 JWTLoginURL: "http://cafe.example.com/login", 1184 }, 1185 redirectLocationName: "@loc", 1186 expectedJWTAuth: &version1.JWTAuth{ 1187 Key: "/etc/nginx/secrets/default-cafe-jwk", 1188 Realm: "cafe", 1189 Token: "$http_token", 1190 RedirectLocationName: "@loc", 1191 }, 1192 expectedRedirectLocation: &version1.JWTRedirectLocation{ 1193 Name: "@loc", 1194 LoginURL: "http://cafe.example.com/login", 1195 }, 1196 expectedWarnings: Warnings{}, 1197 msg: "normal case with login url", 1198 }, 1199 { 1200 secretRefs: map[string]*secrets.SecretReference{ 1201 "cafe-jwk": { 1202 Secret: &v1.Secret{ 1203 Type: secrets.SecretTypeJWK, 1204 }, 1205 Path: "/etc/nginx/secrets/default-cafe-jwk", 1206 Error: errors.New("invalid secret"), 1207 }, 1208 }, 1209 cfgParams: &ConfigParams{ 1210 JWTKey: "cafe-jwk", 1211 JWTRealm: "cafe", 1212 JWTToken: "$http_token", 1213 }, 1214 redirectLocationName: "@loc", 1215 expectedJWTAuth: &version1.JWTAuth{ 1216 Key: "/etc/nginx/secrets/default-cafe-jwk", 1217 Realm: "cafe", 1218 Token: "$http_token", 1219 }, 1220 expectedRedirectLocation: nil, 1221 expectedWarnings: Warnings{ 1222 nil: { 1223 "JWK secret cafe-jwk is invalid: invalid secret", 1224 }, 1225 }, 1226 msg: "invalid secret", 1227 }, 1228 { 1229 secretRefs: map[string]*secrets.SecretReference{ 1230 "cafe-jwk": { 1231 Secret: &v1.Secret{ 1232 Type: secrets.SecretTypeCA, 1233 }, 1234 Path: "/etc/nginx/secrets/default-cafe-jwk", 1235 }, 1236 }, 1237 cfgParams: &ConfigParams{ 1238 JWTKey: "cafe-jwk", 1239 JWTRealm: "cafe", 1240 JWTToken: "$http_token", 1241 }, 1242 redirectLocationName: "@loc", 1243 expectedJWTAuth: &version1.JWTAuth{ 1244 Key: "/etc/nginx/secrets/default-cafe-jwk", 1245 Realm: "cafe", 1246 Token: "$http_token", 1247 }, 1248 expectedRedirectLocation: nil, 1249 expectedWarnings: Warnings{ 1250 nil: { 1251 "JWK secret cafe-jwk is of a wrong type 'nginx.org/ca', must be 'nginx.org/jwk'", 1252 }, 1253 }, 1254 msg: "secret of wrong type without error", 1255 }, 1256 { 1257 secretRefs: map[string]*secrets.SecretReference{ 1258 "cafe-jwk": { 1259 Secret: &v1.Secret{ 1260 Type: secrets.SecretTypeCA, 1261 }, 1262 Path: "", 1263 Error: errors.New("CA secret must have the data field ca.crt"), 1264 }, 1265 }, 1266 cfgParams: &ConfigParams{ 1267 JWTKey: "cafe-jwk", 1268 JWTRealm: "cafe", 1269 JWTToken: "$http_token", 1270 }, 1271 redirectLocationName: "@loc", 1272 expectedJWTAuth: &version1.JWTAuth{ 1273 Key: "", 1274 Realm: "cafe", 1275 Token: "$http_token", 1276 }, 1277 expectedRedirectLocation: nil, 1278 expectedWarnings: Warnings{ 1279 nil: { 1280 "JWK secret cafe-jwk is of a wrong type 'nginx.org/ca', must be 'nginx.org/jwk'", 1281 }, 1282 }, 1283 msg: "secret of wrong type with error", 1284 }, 1285 } 1286 1287 for _, test := range tests { 1288 jwtAuth, redirectLocation, warnings := generateJWTConfig(nil, test.secretRefs, test.cfgParams, test.redirectLocationName) 1289 1290 if diff := cmp.Diff(test.expectedJWTAuth, jwtAuth); diff != "" { 1291 t.Errorf("generateJWTConfig() '%s' mismatch for jwtAuth (-want +got):\n%s", test.msg, diff) 1292 } 1293 if diff := cmp.Diff(test.expectedRedirectLocation, redirectLocation); diff != "" { 1294 t.Errorf("generateJWTConfig() '%s' mismatch for redirectLocation (-want +got):\n%s", test.msg, diff) 1295 } 1296 if !reflect.DeepEqual(test.expectedWarnings, warnings) { 1297 t.Errorf("generateJWTConfig() returned %v but expected %v for the case of %s", warnings, test.expectedWarnings, test.msg) 1298 } 1299 } 1300 } 1301 1302 func TestGenerateNginxCfgForAppProtect(t *testing.T) { 1303 cafeIngressEx := createCafeIngressEx() 1304 cafeIngressEx.Ingress.Annotations["appprotect.f5.com/app-protect-enable"] = "True" 1305 cafeIngressEx.Ingress.Annotations["appprotect.f5.com/app-protect-security-log-enable"] = "True" 1306 cafeIngressEx.AppProtectPolicy = &unstructured.Unstructured{ 1307 Object: map[string]interface{}{ 1308 "metadata": map[string]interface{}{ 1309 "namespace": "default", 1310 "name": "dataguard-alarm", 1311 }, 1312 }, 1313 } 1314 cafeIngressEx.AppProtectLogs = []AppProtectLog{ 1315 { 1316 LogConf: &unstructured.Unstructured{ 1317 Object: map[string]interface{}{ 1318 "metadata": map[string]interface{}{ 1319 "namespace": "default", 1320 "name": "logconf", 1321 }, 1322 }, 1323 }, 1324 }, 1325 } 1326 1327 configParams := NewDefaultConfigParams() 1328 apRes := AppProtectResources{ 1329 AppProtectPolicy: "/etc/nginx/waf/nac-policies/default_dataguard-alarm", 1330 AppProtectLogconfs: []string{"/etc/nginx/waf/nac-logconfs/default_logconf syslog:server=127.0.0.1:514"}, 1331 } 1332 staticCfgParams := &StaticConfigParams{ 1333 MainAppProtectLoadModule: true, 1334 } 1335 1336 isPlus := true 1337 1338 expected := createExpectedConfigForCafeIngressEx(isPlus) 1339 expected.Servers[0].AppProtectEnable = "on" 1340 expected.Servers[0].AppProtectPolicy = "/etc/nginx/waf/nac-policies/default_dataguard-alarm" 1341 expected.Servers[0].AppProtectLogConfs = []string{"/etc/nginx/waf/nac-logconfs/default_logconf syslog:server=127.0.0.1:514"} 1342 expected.Servers[0].AppProtectLogEnable = "on" 1343 expected.Ingress.Annotations = cafeIngressEx.Ingress.Annotations 1344 1345 result, warnings := generateNginxCfg(&cafeIngressEx, apRes, false, configParams, isPlus, false, staticCfgParams, false) 1346 if diff := cmp.Diff(expected, result); diff != "" { 1347 t.Errorf("generateNginxCfg() returned unexpected result (-want +got):\n%s", diff) 1348 } 1349 if len(warnings) != 0 { 1350 t.Errorf("generateNginxCfg() returned warnings: %v", warnings) 1351 } 1352 } 1353 1354 func TestGenerateNginxCfgForMergeableIngressesForAppProtect(t *testing.T) { 1355 mergeableIngresses := createMergeableCafeIngress() 1356 mergeableIngresses.Master.Ingress.Annotations["appprotect.f5.com/app-protect-enable"] = "True" 1357 mergeableIngresses.Master.Ingress.Annotations["appprotect.f5.com/app-protect-security-log-enable"] = "True" 1358 mergeableIngresses.Master.AppProtectPolicy = &unstructured.Unstructured{ 1359 Object: map[string]interface{}{ 1360 "metadata": map[string]interface{}{ 1361 "namespace": "default", 1362 "name": "dataguard-alarm", 1363 }, 1364 }, 1365 } 1366 mergeableIngresses.Master.AppProtectLogs = []AppProtectLog{ 1367 { 1368 LogConf: &unstructured.Unstructured{ 1369 Object: map[string]interface{}{ 1370 "metadata": map[string]interface{}{ 1371 "namespace": "default", 1372 "name": "logconf", 1373 }, 1374 }, 1375 }, 1376 }, 1377 } 1378 1379 configParams := NewDefaultConfigParams() 1380 apRes := AppProtectResources{ 1381 AppProtectPolicy: "/etc/nginx/waf/nac-policies/default_dataguard-alarm", 1382 AppProtectLogconfs: []string{"/etc/nginx/waf/nac-logconfs/default_logconf syslog:server=127.0.0.1:514"}, 1383 } 1384 staticCfgParams := &StaticConfigParams{ 1385 MainAppProtectLoadModule: true, 1386 } 1387 1388 isPlus := true 1389 1390 expected := createExpectedConfigForMergeableCafeIngress(isPlus) 1391 expected.Servers[0].AppProtectEnable = "on" 1392 expected.Servers[0].AppProtectPolicy = "/etc/nginx/waf/nac-policies/default_dataguard-alarm" 1393 expected.Servers[0].AppProtectLogConfs = []string{"/etc/nginx/waf/nac-logconfs/default_logconf syslog:server=127.0.0.1:514"} 1394 expected.Servers[0].AppProtectLogEnable = "on" 1395 expected.Ingress.Annotations = mergeableIngresses.Master.Ingress.Annotations 1396 1397 result, warnings := generateNginxCfgForMergeableIngresses(mergeableIngresses, apRes, configParams, isPlus, false, staticCfgParams, false) 1398 if diff := cmp.Diff(expected, result); diff != "" { 1399 t.Errorf("generateNginxCfgForMergeableIngresses() returned unexpected result (-want +got):\n%s", diff) 1400 } 1401 if len(warnings) != 0 { 1402 t.Errorf("generateNginxCfgForMergeableIngresses() returned warnings: %v", warnings) 1403 } 1404 }