github.com/cilium/cilium@v1.16.2/operator/pkg/model/ingestion/gateway.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package ingestion 5 6 import ( 7 "fmt" 8 "strings" 9 "time" 10 11 corev1 "k8s.io/api/core/v1" 12 gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" 13 gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" 14 gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" 15 mcsapiv1alpha1 "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" 16 17 "github.com/cilium/cilium/operator/pkg/gateway-api/helpers" 18 "github.com/cilium/cilium/operator/pkg/model" 19 ) 20 21 const ( 22 allHosts = "*" 23 ) 24 25 // Input is the input for GatewayAPI. 26 type Input struct { 27 GatewayClass gatewayv1.GatewayClass 28 Gateway gatewayv1.Gateway 29 HTTPRoutes []gatewayv1.HTTPRoute 30 TLSRoutes []gatewayv1alpha2.TLSRoute 31 GRPCRoutes []gatewayv1.GRPCRoute 32 ReferenceGrants []gatewayv1beta1.ReferenceGrant 33 Services []corev1.Service 34 ServiceImports []mcsapiv1alpha1.ServiceImport 35 } 36 37 // GatewayAPI translates Gateway API resources into a model. 38 // TODO(tam): Support GatewayClass 39 func GatewayAPI(input Input) ([]model.HTTPListener, []model.TLSPassthroughListener) { 40 var resHTTP []model.HTTPListener 41 var resTLSPassthrough []model.TLSPassthroughListener 42 43 var labels, annotations map[string]string 44 if input.Gateway.Spec.Infrastructure != nil { 45 labels = toMapString(input.Gateway.Spec.Infrastructure.Labels) 46 annotations = toMapString(input.Gateway.Spec.Infrastructure.Annotations) 47 } 48 49 var infra *model.Infrastructure 50 if labels != nil || annotations != nil { 51 infra = &model.Infrastructure{ 52 Labels: labels, 53 Annotations: annotations, 54 } 55 } 56 57 // Find all the listener host names, so that we can match them with the routes 58 // Gateway API spec guarantees that the hostnames are unique across all listeners 59 var allListenerHostNames []string 60 for _, l := range input.Gateway.Spec.Listeners { 61 if l.Hostname != nil { 62 allListenerHostNames = append(allListenerHostNames, toHostname(l.Hostname)) 63 } 64 } 65 66 for _, l := range input.Gateway.Spec.Listeners { 67 if l.Protocol != gatewayv1.HTTPProtocolType && 68 l.Protocol != gatewayv1.HTTPSProtocolType && 69 l.Protocol != gatewayv1.TLSProtocolType { 70 continue 71 } 72 73 var httpRoutes []model.HTTPRoute 74 httpRoutes = append(httpRoutes, toHTTPRoutes(l, allListenerHostNames, input.HTTPRoutes, input.Services, input.ServiceImports, input.ReferenceGrants)...) 75 httpRoutes = append(httpRoutes, toGRPCRoutes(l, allListenerHostNames, input.GRPCRoutes, input.Services, input.ServiceImports, input.ReferenceGrants)...) 76 resHTTP = append(resHTTP, model.HTTPListener{ 77 Name: string(l.Name), 78 Sources: []model.FullyQualifiedResource{ 79 { 80 Name: input.Gateway.GetName(), 81 Namespace: input.Gateway.GetNamespace(), 82 Group: input.Gateway.GroupVersionKind().Group, 83 Version: input.Gateway.GroupVersionKind().Version, 84 Kind: input.Gateway.GroupVersionKind().Kind, 85 UID: string(input.Gateway.GetUID()), 86 }, 87 }, 88 Port: uint32(l.Port), 89 Hostname: toHostname(l.Hostname), 90 TLS: toTLS(l.TLS, input.ReferenceGrants, input.Gateway.GetNamespace()), 91 Routes: httpRoutes, 92 Infrastructure: infra, 93 }) 94 95 resTLSPassthrough = append(resTLSPassthrough, model.TLSPassthroughListener{ 96 Name: string(l.Name), 97 Sources: []model.FullyQualifiedResource{ 98 { 99 Name: input.Gateway.GetName(), 100 Namespace: input.Gateway.GetNamespace(), 101 Group: input.Gateway.GroupVersionKind().Group, 102 Version: input.Gateway.GroupVersionKind().Version, 103 Kind: input.Gateway.GroupVersionKind().Kind, 104 UID: string(input.Gateway.GetUID()), 105 }, 106 }, 107 Port: uint32(l.Port), 108 Hostname: toHostname(l.Hostname), 109 Routes: toTLSRoutes(l, allListenerHostNames, input.TLSRoutes, input.Services, input.ServiceImports, input.ReferenceGrants), 110 Infrastructure: infra, 111 }) 112 } 113 114 return resHTTP, resTLSPassthrough 115 } 116 117 func getBackendServiceName(namespace string, services []corev1.Service, serviceImports []mcsapiv1alpha1.ServiceImport, backendObjectReference gatewayv1.BackendObjectReference) (string, error) { 118 svcName := string(backendObjectReference.Name) 119 120 switch { 121 case helpers.IsService(backendObjectReference): 122 // We don't have to do anything here 123 case helpers.IsServiceImport(backendObjectReference): 124 svcImport := getServiceImport(string(backendObjectReference.Name), namespace, serviceImports) 125 if svcImport == nil { 126 return "", fmt.Errorf("Service Import %s/%s does not exists", string(backendObjectReference.Name), namespace) 127 } 128 129 var err error 130 svcName, err = helpers.GetServiceName(svcImport) 131 if err != nil { 132 return "", err 133 } 134 135 default: 136 return "", fmt.Errorf("Unsupported backend kind %s", *backendObjectReference.Kind) 137 } 138 139 svc := getServiceSpec(svcName, namespace, services) 140 if svc == nil { 141 return "", fmt.Errorf("Service %s/%s does not exist", svcName, namespace) 142 } 143 return svcName, nil 144 } 145 146 func toHTTPRoutes(listener gatewayv1.Listener, 147 allListenerHostNames []string, 148 input []gatewayv1.HTTPRoute, 149 services []corev1.Service, 150 serviceImports []mcsapiv1alpha1.ServiceImport, 151 grants []gatewayv1beta1.ReferenceGrant) []model.HTTPRoute { 152 var httpRoutes []model.HTTPRoute 153 for _, r := range input { 154 listenerIsParent := false 155 // Check parents to see if r can attach to them. 156 // We have to consider _both_ SectionName and Port 157 for _, parent := range r.Spec.ParentRefs { 158 // First, if both SectionName and Port are unset, attach 159 if parent.SectionName == nil && parent.Port == nil { 160 listenerIsParent = true 161 break 162 } 163 164 // Then, if SectionName is set, check combinations with Port. 165 if parent.SectionName != nil { 166 if *parent.SectionName != listener.Name { 167 // If SectionName is set but not equal, no other settings 168 // matter, so check the next parent. 169 continue 170 } 171 172 if parent.Port != nil && *parent.Port != listener.Port { 173 // If SectionName is set and equal, but Port is set and _unequal_, 174 continue 175 } 176 177 listenerIsParent = true 178 break 179 } 180 181 if parent.Port != nil { 182 if *parent.Port != listener.Port { 183 // If Port is set but not equal, no other settings 184 // matter, check the next parent. 185 continue 186 } 187 188 listenerIsParent = true 189 break 190 } 191 192 } 193 194 if !listenerIsParent { 195 continue 196 } 197 198 computedHost := model.ComputeHosts(toStringSlice(r.Spec.Hostnames), (*string)(listener.Hostname), allListenerHostNames) 199 // No matching host, skip this route 200 if len(computedHost) == 0 { 201 continue 202 } 203 204 if len(computedHost) == 1 && computedHost[0] == allHosts { 205 computedHost = nil 206 } 207 208 httpRoutes = append(httpRoutes, extractRoutes(int32(listener.Port), computedHost, r, services, serviceImports, grants)...) 209 210 } 211 return httpRoutes 212 } 213 214 func extractRoutes(listenerPort int32, hostnames []string, hr gatewayv1.HTTPRoute, services []corev1.Service, serviceImports []mcsapiv1alpha1.ServiceImport, grants []gatewayv1beta1.ReferenceGrant) []model.HTTPRoute { 215 var httpRoutes []model.HTTPRoute 216 for _, rule := range hr.Spec.Rules { 217 var backendHTTPFilters []*model.BackendHTTPFilter 218 bes := make([]model.Backend, 0, len(rule.BackendRefs)) 219 for _, be := range rule.BackendRefs { 220 if !helpers.IsBackendReferenceAllowed(hr.GetNamespace(), be.BackendRef, gatewayv1.SchemeGroupVersion.WithKind("HTTPRoute"), grants) { 221 continue 222 } 223 svcName, err := getBackendServiceName(helpers.NamespaceDerefOr(be.Namespace, hr.Namespace), services, serviceImports, be.BackendObjectReference) 224 if err != nil { 225 continue 226 } 227 if svcName != string(be.Name) { 228 be = *be.DeepCopy() 229 be.BackendRef.BackendObjectReference = gatewayv1beta1.BackendObjectReference{ 230 Name: gatewayv1beta1.ObjectName(svcName), 231 Port: be.Port, 232 Namespace: be.Namespace, 233 } 234 } 235 if be.BackendRef.Port == nil { 236 // must have port for Service reference 237 continue 238 } 239 svc := getServiceSpec(string(be.Name), helpers.NamespaceDerefOr(be.Namespace, hr.Namespace), services) 240 if svc != nil { 241 bes = append(bes, backendToModelBackend(*svc, be.BackendRef, hr.Namespace)) 242 for _, f := range be.Filters { 243 switch f.Type { 244 case gatewayv1.HTTPRouteFilterRequestHeaderModifier: 245 backendHTTPFilters = append(backendHTTPFilters, &model.BackendHTTPFilter{ 246 Name: fmt.Sprintf("%s:%s:%d", helpers.NamespaceDerefOr(be.Namespace, hr.Namespace), be.Name, uint32(*be.Port)), 247 RequestHeaderFilter: &model.HTTPHeaderFilter{ 248 HeadersToAdd: toHTTPHeaders(f.RequestHeaderModifier.Add), 249 HeadersToSet: toHTTPHeaders(f.RequestHeaderModifier.Set), 250 HeadersToRemove: f.RequestHeaderModifier.Remove, 251 }, 252 }) 253 case gatewayv1.HTTPRouteFilterResponseHeaderModifier: 254 backendHTTPFilters = append(backendHTTPFilters, &model.BackendHTTPFilter{ 255 Name: fmt.Sprintf("%s:%s:%d", helpers.NamespaceDerefOr(be.Namespace, hr.Namespace), be.Name, uint32(*be.Port)), 256 ResponseHeaderModifier: &model.HTTPHeaderFilter{ 257 HeadersToAdd: toHTTPHeaders(f.ResponseHeaderModifier.Add), 258 HeadersToSet: toHTTPHeaders(f.ResponseHeaderModifier.Set), 259 HeadersToRemove: f.ResponseHeaderModifier.Remove, 260 }, 261 }) 262 } 263 } 264 } 265 } 266 267 var dr *model.DirectResponse 268 if len(bes) == 0 { 269 dr = &model.DirectResponse{ 270 StatusCode: 500, 271 } 272 } 273 274 var requestHeaderFilter *model.HTTPHeaderFilter 275 var responseHeaderFilter *model.HTTPHeaderFilter 276 var requestRedirectFilter *model.HTTPRequestRedirectFilter 277 var rewriteFilter *model.HTTPURLRewriteFilter 278 var requestMirrors []*model.HTTPRequestMirror 279 280 for _, f := range rule.Filters { 281 switch f.Type { 282 case gatewayv1.HTTPRouteFilterRequestHeaderModifier: 283 requestHeaderFilter = &model.HTTPHeaderFilter{ 284 HeadersToAdd: toHTTPHeaders(f.RequestHeaderModifier.Add), 285 HeadersToSet: toHTTPHeaders(f.RequestHeaderModifier.Set), 286 HeadersToRemove: f.RequestHeaderModifier.Remove, 287 } 288 case gatewayv1.HTTPRouteFilterResponseHeaderModifier: 289 responseHeaderFilter = &model.HTTPHeaderFilter{ 290 HeadersToAdd: toHTTPHeaders(f.ResponseHeaderModifier.Add), 291 HeadersToSet: toHTTPHeaders(f.ResponseHeaderModifier.Set), 292 HeadersToRemove: f.ResponseHeaderModifier.Remove, 293 } 294 case gatewayv1.HTTPRouteFilterRequestRedirect: 295 requestRedirectFilter = toHTTPRequestRedirectFilter(listenerPort, f.RequestRedirect) 296 case gatewayv1.HTTPRouteFilterURLRewrite: 297 rewriteFilter = toHTTPRewriteFilter(f.URLRewrite) 298 case gatewayv1.HTTPRouteFilterRequestMirror: 299 svc := getServiceSpec(string(f.RequestMirror.BackendRef.Name), helpers.NamespaceDerefOr(f.RequestMirror.BackendRef.Namespace, hr.Namespace), services) 300 if svc != nil { 301 requestMirrors = append(requestMirrors, toHTTPRequestMirror(*svc, f.RequestMirror, hr.Namespace)) 302 } 303 } 304 } 305 306 if len(rule.Matches) == 0 { 307 httpRoutes = append(httpRoutes, model.HTTPRoute{ 308 Hostnames: hostnames, 309 Backends: bes, 310 BackendHTTPFilters: backendHTTPFilters, 311 DirectResponse: dr, 312 RequestHeaderFilter: requestHeaderFilter, 313 ResponseHeaderModifier: responseHeaderFilter, 314 RequestRedirect: requestRedirectFilter, 315 Rewrite: rewriteFilter, 316 RequestMirrors: requestMirrors, 317 Timeout: toTimeout(rule.Timeouts), 318 }) 319 } 320 321 for _, match := range rule.Matches { 322 httpRoutes = append(httpRoutes, model.HTTPRoute{ 323 Hostnames: hostnames, 324 PathMatch: toPathMatch(match), 325 HeadersMatch: toHeaderMatch(match), 326 QueryParamsMatch: toQueryMatch(match), 327 Method: (*string)(match.Method), 328 Backends: bes, 329 BackendHTTPFilters: backendHTTPFilters, 330 DirectResponse: dr, 331 RequestHeaderFilter: requestHeaderFilter, 332 ResponseHeaderModifier: responseHeaderFilter, 333 RequestRedirect: requestRedirectFilter, 334 Rewrite: rewriteFilter, 335 RequestMirrors: requestMirrors, 336 Timeout: toTimeout(rule.Timeouts), 337 }) 338 } 339 } 340 return httpRoutes 341 } 342 343 func toTimeout(timeouts *gatewayv1.HTTPRouteTimeouts) model.Timeout { 344 res := model.Timeout{} 345 if timeouts == nil { 346 return res 347 } 348 if timeouts.BackendRequest != nil { 349 if duration, err := time.ParseDuration(string(*timeouts.BackendRequest)); err == nil { 350 res.Backend = model.AddressOf(duration) 351 } 352 } 353 if timeouts.Request != nil { 354 if duration, err := time.ParseDuration(string(*timeouts.Request)); err == nil { 355 res.Request = model.AddressOf(duration) 356 } 357 } 358 return res 359 } 360 361 func toGRPCRoutes(listener gatewayv1beta1.Listener, 362 allListenerHostNames []string, 363 input []gatewayv1.GRPCRoute, 364 services []corev1.Service, 365 serviceImports []mcsapiv1alpha1.ServiceImport, 366 grants []gatewayv1beta1.ReferenceGrant) []model.HTTPRoute { 367 var grpcRoutes []model.HTTPRoute 368 for _, r := range input { 369 isListener := false 370 for _, parent := range r.Spec.ParentRefs { 371 if parent.SectionName == nil || *parent.SectionName == listener.Name { 372 isListener = true 373 break 374 } 375 } 376 if !isListener { 377 continue 378 } 379 380 computedHost := model.ComputeHosts(toStringSlice(r.Spec.Hostnames), (*string)(listener.Hostname), allListenerHostNames) 381 // No matching host, skip this route 382 if len(computedHost) == 0 { 383 continue 384 } 385 386 if len(computedHost) == 1 && computedHost[0] == allHosts { 387 computedHost = nil 388 } 389 390 for _, rule := range r.Spec.Rules { 391 bes := make([]model.Backend, 0, len(rule.BackendRefs)) 392 for _, be := range rule.BackendRefs { 393 if !helpers.IsBackendReferenceAllowed(r.GetNamespace(), be.BackendRef, gatewayv1beta1.SchemeGroupVersion.WithKind("GRPCRoute"), grants) { 394 continue 395 } 396 svcName, err := getBackendServiceName(helpers.NamespaceDerefOr(be.Namespace, r.Namespace), services, serviceImports, be.BackendObjectReference) 397 if err != nil { 398 continue 399 } 400 if svcName != string(be.Name) { 401 be = *be.DeepCopy() 402 be.BackendObjectReference = gatewayv1beta1.BackendObjectReference{ 403 Name: gatewayv1beta1.ObjectName(svcName), 404 Port: be.Port, 405 Namespace: be.Namespace, 406 } 407 } 408 if be.BackendRef.Port == nil { 409 // must have port for Service reference 410 continue 411 } 412 svc := getServiceSpec(string(be.Name), helpers.NamespaceDerefOr(be.Namespace, r.Namespace), services) 413 if svc != nil { 414 bes = append(bes, backendToModelBackend(*svc, be.BackendRef, r.Namespace)) 415 } 416 } 417 418 var dr *model.DirectResponse 419 if len(bes) == 0 { 420 dr = &model.DirectResponse{ 421 StatusCode: 500, 422 } 423 } 424 425 var requestHeaderFilter *model.HTTPHeaderFilter 426 var responseHeaderFilter *model.HTTPHeaderFilter 427 var requestMirrors []*model.HTTPRequestMirror 428 429 for _, f := range rule.Filters { 430 switch f.Type { 431 case gatewayv1.GRPCRouteFilterRequestHeaderModifier: 432 requestHeaderFilter = &model.HTTPHeaderFilter{ 433 HeadersToAdd: toHTTPHeaders(f.RequestHeaderModifier.Add), 434 HeadersToSet: toHTTPHeaders(f.RequestHeaderModifier.Set), 435 HeadersToRemove: f.RequestHeaderModifier.Remove, 436 } 437 case gatewayv1.GRPCRouteFilterResponseHeaderModifier: 438 responseHeaderFilter = &model.HTTPHeaderFilter{ 439 HeadersToAdd: toHTTPHeaders(f.ResponseHeaderModifier.Add), 440 HeadersToSet: toHTTPHeaders(f.ResponseHeaderModifier.Set), 441 HeadersToRemove: f.ResponseHeaderModifier.Remove, 442 } 443 case gatewayv1.GRPCRouteFilterRequestMirror: 444 svc := getServiceSpec(string(f.RequestMirror.BackendRef.Name), helpers.NamespaceDerefOr(f.RequestMirror.BackendRef.Namespace, r.Namespace), services) 445 if svc != nil { 446 requestMirrors = append(requestMirrors, toHTTPRequestMirror(*svc, f.RequestMirror, r.Namespace)) 447 } 448 } 449 } 450 451 if len(rule.Matches) == 0 { 452 grpcRoutes = append(grpcRoutes, model.HTTPRoute{ 453 Hostnames: computedHost, 454 Backends: bes, 455 DirectResponse: dr, 456 RequestHeaderFilter: requestHeaderFilter, 457 ResponseHeaderModifier: responseHeaderFilter, 458 RequestMirrors: requestMirrors, 459 }) 460 } 461 462 for _, match := range rule.Matches { 463 grpcRoutes = append(grpcRoutes, model.HTTPRoute{ 464 Hostnames: computedHost, 465 PathMatch: toGRPCPathMatch(match), 466 HeadersMatch: toGRPCHeaderMatch(match), 467 Backends: bes, 468 DirectResponse: dr, 469 RequestHeaderFilter: requestHeaderFilter, 470 ResponseHeaderModifier: responseHeaderFilter, 471 RequestMirrors: requestMirrors, 472 IsGRPC: true, 473 }) 474 } 475 } 476 } 477 return grpcRoutes 478 } 479 480 func toTLSRoutes(listener gatewayv1beta1.Listener, allListenerHostNames []string, input []gatewayv1alpha2.TLSRoute, services []corev1.Service, serviceImports []mcsapiv1alpha1.ServiceImport, grants []gatewayv1beta1.ReferenceGrant) []model.TLSPassthroughRoute { 481 var tlsRoutes []model.TLSPassthroughRoute 482 for _, r := range input { 483 isListener := false 484 for _, parent := range r.Spec.ParentRefs { 485 if parent.SectionName == nil || *parent.SectionName == listener.Name { 486 isListener = true 487 break 488 } 489 } 490 if !isListener { 491 continue 492 } 493 494 computedHost := model.ComputeHosts(toStringSlice(r.Spec.Hostnames), (*string)(listener.Hostname), allListenerHostNames) 495 // No matching host, skip this route 496 if len(computedHost) == 0 { 497 continue 498 } 499 500 if len(computedHost) == 1 && computedHost[0] == allHosts { 501 computedHost = nil 502 } 503 504 for _, rule := range r.Spec.Rules { 505 bes := make([]model.Backend, 0, len(rule.BackendRefs)) 506 for _, be := range rule.BackendRefs { 507 if !helpers.IsBackendReferenceAllowed(r.GetNamespace(), be, gatewayv1alpha2.SchemeGroupVersion.WithKind("TLSRoute"), grants) { 508 continue 509 } 510 svcName, err := getBackendServiceName(helpers.NamespaceDerefOr(be.Namespace, r.Namespace), services, serviceImports, be.BackendObjectReference) 511 if err != nil { 512 continue 513 } 514 if svcName != string(be.Name) { 515 be = *be.DeepCopy() 516 be.BackendObjectReference = gatewayv1beta1.BackendObjectReference{ 517 Name: gatewayv1beta1.ObjectName(svcName), 518 Port: be.Port, 519 Namespace: be.Namespace, 520 } 521 } 522 svc := getServiceSpec(string(be.Name), helpers.NamespaceDerefOr(be.Namespace, r.Namespace), services) 523 if svc != nil { 524 bes = append(bes, backendToModelBackend(*svc, be, r.Namespace)) 525 } 526 } 527 528 tlsRoutes = append(tlsRoutes, model.TLSPassthroughRoute{ 529 Hostnames: computedHost, 530 Backends: bes, 531 }) 532 533 } 534 } 535 return tlsRoutes 536 } 537 538 func toHTTPRequestRedirectFilter(listenerPort int32, redirect *gatewayv1.HTTPRequestRedirectFilter) *model.HTTPRequestRedirectFilter { 539 if redirect == nil { 540 return nil 541 } 542 var pathModifier *model.StringMatch 543 if redirect.Path != nil { 544 pathModifier = &model.StringMatch{} 545 546 switch redirect.Path.Type { 547 case gatewayv1.FullPathHTTPPathModifier: 548 pathModifier.Exact = *redirect.Path.ReplaceFullPath 549 case gatewayv1.PrefixMatchHTTPPathModifier: 550 pathModifier.Prefix = *redirect.Path.ReplacePrefixMatch 551 } 552 } 553 var redirectPort *int32 554 if redirect.Port == nil { 555 if redirect.Scheme == nil { 556 // If redirect scheme is empty, the redirect port MUST be the Gateway 557 // Listener port. 558 // Refer to: https://github.com/kubernetes-sigs/gateway-api/blob/35fe25d1384a41c9b89dd5af7ae3214c431f008c/apis/v1/httproute_types.go#L1040-L1041 559 redirectPort = model.AddressOf(listenerPort) 560 } 561 } else { 562 redirectPort = (*int32)(redirect.Port) 563 } 564 return &model.HTTPRequestRedirectFilter{ 565 Scheme: redirect.Scheme, 566 Hostname: (*string)(redirect.Hostname), 567 Path: pathModifier, 568 Port: redirectPort, 569 StatusCode: redirect.StatusCode, 570 } 571 } 572 573 func toHTTPRewriteFilter(rewrite *gatewayv1.HTTPURLRewriteFilter) *model.HTTPURLRewriteFilter { 574 if rewrite == nil { 575 return nil 576 } 577 var path *model.StringMatch 578 if rewrite.Path != nil { 579 switch rewrite.Path.Type { 580 case gatewayv1.FullPathHTTPPathModifier: 581 if rewrite.Path.ReplaceFullPath != nil { 582 path = &model.StringMatch{ 583 Exact: *rewrite.Path.ReplaceFullPath, 584 } 585 } 586 case gatewayv1.PrefixMatchHTTPPathModifier: 587 if rewrite.Path.ReplacePrefixMatch != nil { 588 path = &model.StringMatch{ 589 // a trailing `/` is ignored 590 Prefix: strings.TrimSuffix(*rewrite.Path.ReplacePrefixMatch, "/"), 591 } 592 } 593 } 594 } 595 return &model.HTTPURLRewriteFilter{ 596 HostName: (*string)(rewrite.Hostname), 597 Path: path, 598 } 599 } 600 601 func toHTTPRequestMirror(svc corev1.Service, mirror *gatewayv1.HTTPRequestMirrorFilter, ns string) *model.HTTPRequestMirror { 602 return &model.HTTPRequestMirror{ 603 Backend: model.AddressOf(backendRefToModelBackend(svc, mirror.BackendRef, ns)), 604 } 605 } 606 607 func toHostname(hostname *gatewayv1.Hostname) string { 608 if hostname != nil { 609 return (string)(*hostname) 610 } 611 return allHosts 612 } 613 614 func getServiceSpec(svcName, svcNamespace string, services []corev1.Service) *corev1.Service { 615 for _, svc := range services { 616 if svc.GetName() == svcName && svc.GetNamespace() == svcNamespace { 617 return &svc 618 } 619 } 620 return nil 621 } 622 623 func getServiceImport(svcName, svcNamespace string, serviceImports []mcsapiv1alpha1.ServiceImport) *mcsapiv1alpha1.ServiceImport { 624 for _, svc := range serviceImports { 625 if svc.GetName() == svcName && svc.GetNamespace() == svcNamespace { 626 return &svc 627 } 628 } 629 return nil 630 } 631 632 func backendToModelBackend(svc corev1.Service, be gatewayv1.BackendRef, defaultNamespace string) model.Backend { 633 res := backendRefToModelBackend(svc, be.BackendObjectReference, defaultNamespace) 634 res.Weight = be.Weight 635 return res 636 } 637 638 func backendRefToModelBackend(svc corev1.Service, be gatewayv1.BackendObjectReference, defaultNamespace string) model.Backend { 639 ns := helpers.NamespaceDerefOr(be.Namespace, defaultNamespace) 640 var port *model.BackendPort 641 var appProtocol *string 642 643 if be.Port != nil { 644 backendPort := uint32(*be.Port) 645 appProtocol = backendRefToAppProtocol(svc, int32(*be.Port)) 646 647 port = &model.BackendPort{ 648 Port: backendPort, 649 } 650 } 651 652 return model.Backend{ 653 Name: string(be.Name), 654 Namespace: ns, 655 Port: port, 656 AppProtocol: appProtocol, 657 } 658 } 659 660 func backendRefToAppProtocol(svc corev1.Service, backendPort int32) *string { 661 for _, portSpec := range svc.Spec.Ports { 662 if backendPort == portSpec.Port { 663 return portSpec.AppProtocol 664 } 665 } 666 667 return nil 668 } 669 670 func toPathMatch(match gatewayv1.HTTPRouteMatch) model.StringMatch { 671 if match.Path == nil { 672 return model.StringMatch{} 673 } 674 675 switch *match.Path.Type { 676 case gatewayv1.PathMatchExact: 677 return model.StringMatch{ 678 Exact: *match.Path.Value, 679 } 680 case gatewayv1.PathMatchPathPrefix: 681 return model.StringMatch{ 682 Prefix: *match.Path.Value, 683 } 684 case gatewayv1.PathMatchRegularExpression: 685 return model.StringMatch{ 686 Regex: *match.Path.Value, 687 } 688 } 689 return model.StringMatch{} 690 } 691 692 func toGRPCPathMatch(match gatewayv1.GRPCRouteMatch) model.StringMatch { 693 if match.Method == nil || match.Method.Service == nil { 694 return model.StringMatch{} 695 } 696 697 t := gatewayv1.GRPCMethodMatchExact 698 if match.Method.Type != nil { 699 t = *match.Method.Type 700 } 701 702 path := "" 703 if match.Method.Service != nil { 704 path = path + "/" + *match.Method.Service 705 } 706 707 if match.Method.Method != nil { 708 path = path + "/" + *match.Method.Method 709 } 710 711 switch t { 712 case gatewayv1.GRPCMethodMatchExact: 713 return model.StringMatch{ 714 Exact: path, 715 } 716 case gatewayv1.GRPCMethodMatchRegularExpression: 717 return model.StringMatch{ 718 Regex: path, 719 } 720 } 721 return model.StringMatch{} 722 } 723 724 func toHeaderMatch(match gatewayv1.HTTPRouteMatch) []model.KeyValueMatch { 725 if len(match.Headers) == 0 { 726 return nil 727 } 728 res := make([]model.KeyValueMatch, 0, len(match.Headers)) 729 for _, h := range match.Headers { 730 t := gatewayv1.HeaderMatchExact 731 if h.Type != nil { 732 t = *h.Type 733 } 734 switch t { 735 case gatewayv1.HeaderMatchExact: 736 res = append(res, model.KeyValueMatch{ 737 Key: string(h.Name), 738 Match: model.StringMatch{ 739 Exact: h.Value, 740 }, 741 }) 742 case gatewayv1.HeaderMatchRegularExpression: 743 res = append(res, model.KeyValueMatch{ 744 Key: string(h.Name), 745 Match: model.StringMatch{ 746 Regex: h.Value, 747 }, 748 }) 749 } 750 } 751 return res 752 } 753 754 func toGRPCHeaderMatch(match gatewayv1.GRPCRouteMatch) []model.KeyValueMatch { 755 if len(match.Headers) == 0 { 756 return nil 757 } 758 res := make([]model.KeyValueMatch, 0, len(match.Headers)) 759 for _, h := range match.Headers { 760 t := gatewayv1.HeaderMatchExact 761 if h.Type != nil { 762 t = *h.Type 763 } 764 switch t { 765 case gatewayv1.HeaderMatchExact: 766 res = append(res, model.KeyValueMatch{ 767 Key: string(h.Name), 768 Match: model.StringMatch{ 769 Exact: h.Value, 770 }, 771 }) 772 case gatewayv1.HeaderMatchRegularExpression: 773 res = append(res, model.KeyValueMatch{ 774 Key: string(h.Name), 775 Match: model.StringMatch{ 776 Regex: h.Value, 777 }, 778 }) 779 } 780 } 781 return res 782 } 783 784 func toQueryMatch(match gatewayv1.HTTPRouteMatch) []model.KeyValueMatch { 785 if len(match.QueryParams) == 0 { 786 return nil 787 } 788 res := make([]model.KeyValueMatch, 0, len(match.QueryParams)) 789 for _, h := range match.QueryParams { 790 t := gatewayv1.QueryParamMatchExact 791 if h.Type != nil { 792 t = *h.Type 793 } 794 switch t { 795 case gatewayv1.QueryParamMatchExact: 796 res = append(res, model.KeyValueMatch{ 797 Key: string(h.Name), 798 Match: model.StringMatch{ 799 Exact: h.Value, 800 }, 801 }) 802 case gatewayv1.QueryParamMatchRegularExpression: 803 res = append(res, model.KeyValueMatch{ 804 Key: string(h.Name), 805 Match: model.StringMatch{ 806 Regex: h.Value, 807 }, 808 }) 809 } 810 } 811 return res 812 } 813 814 func toTLS(tls *gatewayv1.GatewayTLSConfig, grants []gatewayv1beta1.ReferenceGrant, defaultNamespace string) []model.TLSSecret { 815 if tls == nil { 816 return nil 817 } 818 819 res := make([]model.TLSSecret, 0, len(tls.CertificateRefs)) 820 for _, cert := range tls.CertificateRefs { 821 if !helpers.IsSecretReferenceAllowed(defaultNamespace, cert, gatewayv1.SchemeGroupVersion.WithKind("Gateway"), grants) { 822 // not allowed to be referred to, skipping 823 continue 824 } 825 res = append(res, model.TLSSecret{ 826 Name: string(cert.Name), 827 Namespace: helpers.NamespaceDerefOr(cert.Namespace, defaultNamespace), 828 }) 829 } 830 return res 831 } 832 833 func toHTTPHeaders(headers []gatewayv1.HTTPHeader) []model.Header { 834 if len(headers) == 0 { 835 return nil 836 } 837 res := make([]model.Header, 0, len(headers)) 838 for _, h := range headers { 839 res = append(res, model.Header{ 840 Name: string(h.Name), 841 Value: h.Value, 842 }) 843 } 844 return res 845 } 846 847 func toMapString(in map[gatewayv1.AnnotationKey]gatewayv1.AnnotationValue) map[string]string { 848 out := make(map[string]string, len(in)) 849 for k, v := range in { 850 out[string(k)] = string(v) 851 } 852 return out 853 } 854 855 func toStringSlice(s []gatewayv1.Hostname) []string { 856 res := make([]string, 0, len(s)) 857 for _, h := range s { 858 res = append(res, string(h)) 859 } 860 return res 861 }