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  }