blob: 34615b9f55c9244ff069d6c25573628ad75c4a24 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package controller
import (
"cmp"
"context"
"fmt"
"github.com/go-logr/logr"
"github.com/pkg/errors"
"golang.org/x/exp/slices"
corev1 "k8s.io/api/core/v1"
discoveryv1 "k8s.io/api/discovery/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
k8stypes "k8s.io/apimachinery/pkg/types"
"k8s.io/utils/ptr"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
"sigs.k8s.io/gateway-api/apis/v1alpha2"
"sigs.k8s.io/gateway-api/apis/v1beta1"
"github.com/apache/apisix-ingress-controller/api/v1alpha1"
"github.com/apache/apisix-ingress-controller/internal/controller/indexer"
"github.com/apache/apisix-ingress-controller/internal/controller/status"
"github.com/apache/apisix-ingress-controller/internal/manager/readiness"
"github.com/apache/apisix-ingress-controller/internal/provider"
"github.com/apache/apisix-ingress-controller/internal/types"
"github.com/apache/apisix-ingress-controller/internal/utils"
)
// HTTPRouteReconciler reconciles a GatewayClass object.
type HTTPRouteReconciler struct { //nolint:revive
client.Client
Scheme *runtime.Scheme
Log logr.Logger
Provider provider.Provider
genericEvent chan event.GenericEvent
Updater status.Updater
Readier readiness.ReadinessManager
}
// SetupWithManager sets up the controller with the Manager.
func (r *HTTPRouteReconciler) SetupWithManager(mgr ctrl.Manager) error {
r.genericEvent = make(chan event.GenericEvent, 100)
bdr := ctrl.NewControllerManagedBy(mgr).
For(&gatewayv1.HTTPRoute{}).
WithEventFilter(predicate.GenerationChangedPredicate{}).
Watches(&discoveryv1.EndpointSlice{},
handler.EnqueueRequestsFromMapFunc(r.listHTTPRoutesByServiceRef),
).
Watches(&v1alpha1.PluginConfig{},
handler.EnqueueRequestsFromMapFunc(r.listHTTPRoutesByExtensionRef),
).
Watches(&gatewayv1.Gateway{},
handler.EnqueueRequestsFromMapFunc(r.listHTTPRoutesForGateway),
builder.WithPredicates(
predicate.Funcs{
GenericFunc: func(e event.GenericEvent) bool {
return false
},
DeleteFunc: func(e event.DeleteEvent) bool {
return false
},
CreateFunc: func(e event.CreateEvent) bool {
return true
},
UpdateFunc: func(e event.UpdateEvent) bool {
return true
},
},
),
).
Watches(&v1alpha1.BackendTrafficPolicy{},
handler.EnqueueRequestsFromMapFunc(r.listHTTPRoutesForBackendTrafficPolicy),
builder.WithPredicates(
BackendTrafficPolicyPredicateFunc(r.genericEvent),
),
).
Watches(&v1alpha1.HTTPRoutePolicy{},
handler.EnqueueRequestsFromMapFunc(r.listHTTPRouteByHTTPRoutePolicy),
builder.WithPredicates(httpRoutePolicyPredicateFuncs(r.genericEvent)),
).
Watches(&v1alpha1.GatewayProxy{},
handler.EnqueueRequestsFromMapFunc(r.listHTTPRoutesForGatewayProxy),
).
WatchesRawSource(
source.Channel(
r.genericEvent,
handler.EnqueueRequestsFromMapFunc(r.listHTTPRouteForGenericEvent),
),
)
if GetEnableReferenceGrant() {
bdr.Watches(&v1beta1.ReferenceGrant{},
handler.EnqueueRequestsFromMapFunc(r.listHTTPRoutesForReferenceGrant),
builder.WithPredicates(referenceGrantPredicates(KindHTTPRoute)),
)
}
return bdr.Complete(r)
}
func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
defer r.Readier.Done(&gatewayv1.HTTPRoute{}, req.NamespacedName)
hr := new(gatewayv1.HTTPRoute)
if err := r.Get(ctx, req.NamespacedName, hr); err != nil {
if client.IgnoreNotFound(err) == nil {
if err := r.updateHTTPRoutePolicyStatusOnDeleting(ctx, req.NamespacedName); err != nil {
return ctrl.Result{}, err
}
hr.Namespace = req.Namespace
hr.Name = req.Name
hr.TypeMeta = metav1.TypeMeta{
Kind: KindHTTPRoute,
APIVersion: gatewayv1.GroupVersion.String(),
}
if err := r.Provider.Delete(ctx, hr); err != nil {
r.Log.Error(err, "failed to delete httproute", "httproute", hr)
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
return ctrl.Result{}, err
}
type ResourceStatus struct {
status bool
msg string
}
// Only keep acceptStatus since we're using error objects directly now
acceptStatus := ResourceStatus{
status: true,
msg: "Route is accepted",
}
gateways, err := ParseRouteParentRefs(ctx, r.Client, r.Log, hr, hr.Spec.ParentRefs)
if err != nil {
return ctrl.Result{}, err
}
if len(gateways) == 0 {
return ctrl.Result{}, nil
}
tctx := provider.NewDefaultTranslateContext(ctx)
tctx.RouteParentRefs = hr.Spec.ParentRefs
rk := utils.NamespacedNameKind(hr)
for _, gateway := range gateways {
if err := ProcessGatewayProxy(r.Client, r.Log, tctx, gateway.Gateway, rk); err != nil {
acceptStatus.status = false
acceptStatus.msg = err.Error()
}
}
var backendRefErr error
if err := r.processHTTPRoute(tctx, hr); err != nil {
// When encountering a backend reference error, it should not affect the acceptance status
if types.IsSomeReasonError(err, gatewayv1.RouteReasonInvalidKind) {
backendRefErr = err
} else {
acceptStatus.status = false
acceptStatus.msg = err.Error()
}
}
if err := r.processHTTPRoutePolicies(tctx, hr); err != nil {
acceptStatus.status = false
acceptStatus.msg = err.Error()
}
// Store the backend reference error for later use.
// If the backend reference error is because of an invalid kind, use this error first
if err := r.processHTTPRouteBackendRefs(tctx, req.NamespacedName); err != nil && backendRefErr == nil {
backendRefErr = err
}
ProcessBackendTrafficPolicy(r.Client, r.Log, tctx)
filteredHTTPRoute, err := filterHostnames(gateways, hr.DeepCopy())
if err != nil {
acceptStatus.status = false
acceptStatus.msg = err.Error()
}
// TODO: diff the old and new status
hr.Status.Parents = make([]gatewayv1.RouteParentStatus, 0, len(gateways))
for _, gateway := range gateways {
parentStatus := gatewayv1.RouteParentStatus{}
SetRouteParentRef(&parentStatus, gateway.Gateway.Name, gateway.Gateway.Namespace)
for _, condition := range gateway.Conditions {
parentStatus.Conditions = MergeCondition(parentStatus.Conditions, condition)
}
SetRouteConditionAccepted(&parentStatus, hr.GetGeneration(), acceptStatus.status, acceptStatus.msg)
SetRouteConditionResolvedRefs(&parentStatus, hr.GetGeneration(), backendRefErr)
hr.Status.Parents = append(hr.Status.Parents, parentStatus)
}
r.Updater.Update(status.Update{
NamespacedName: utils.NamespacedName(hr),
Resource: &gatewayv1.HTTPRoute{},
Mutator: status.MutatorFunc(func(obj client.Object) client.Object {
h, ok := obj.(*gatewayv1.HTTPRoute)
if !ok {
err := fmt.Errorf("unsupported object type %T", obj)
panic(err)
}
hCopy := h.DeepCopy()
hCopy.Status = hr.Status
return hCopy
}),
})
UpdateStatus(r.Updater, r.Log, tctx)
if isRouteAccepted(gateways) && err == nil {
routeToUpdate := hr
if filteredHTTPRoute != nil {
r.Log.V(1).Info("filtered httproute", "httproute", filteredHTTPRoute)
routeToUpdate = filteredHTTPRoute
}
if err := r.Provider.Update(ctx, tctx, routeToUpdate); err != nil {
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}
func (r *HTTPRouteReconciler) listHTTPRoutesByServiceRef(ctx context.Context, obj client.Object) []reconcile.Request {
endpointSlice, ok := obj.(*discoveryv1.EndpointSlice)
if !ok {
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to EndpointSlice")
return nil
}
namespace := endpointSlice.GetNamespace()
serviceName := endpointSlice.Labels[discoveryv1.LabelServiceName]
hrList := &gatewayv1.HTTPRouteList{}
if err := r.List(ctx, hrList, client.MatchingFields{
indexer.ServiceIndexRef: indexer.GenIndexKey(namespace, serviceName),
}); err != nil {
r.Log.Error(err, "failed to list httproutes by service", "service", serviceName)
return nil
}
requests := make([]reconcile.Request, 0, len(hrList.Items))
for _, hr := range hrList.Items {
requests = append(requests, reconcile.Request{
NamespacedName: client.ObjectKey{
Namespace: hr.Namespace,
Name: hr.Name,
},
})
}
return requests
}
func (r *HTTPRouteReconciler) listHTTPRoutesByExtensionRef(ctx context.Context, obj client.Object) []reconcile.Request {
pluginconfig, ok := obj.(*v1alpha1.PluginConfig)
if !ok {
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to PluginConfig")
return nil
}
namespace := pluginconfig.GetNamespace()
name := pluginconfig.GetName()
hrList := &gatewayv1.HTTPRouteList{}
if err := r.List(ctx, hrList, client.MatchingFields{
indexer.ExtensionRef: indexer.GenIndexKey(namespace, name),
}); err != nil {
r.Log.Error(err, "failed to list httproutes by extension reference", "extension", name)
return nil
}
requests := make([]reconcile.Request, 0, len(hrList.Items))
for _, hr := range hrList.Items {
requests = append(requests, reconcile.Request{
NamespacedName: client.ObjectKey{
Namespace: hr.Namespace,
Name: hr.Name,
},
})
}
return requests
}
func (r *HTTPRouteReconciler) listHTTPRoutesForBackendTrafficPolicy(ctx context.Context, obj client.Object) []reconcile.Request {
policy, ok := obj.(*v1alpha1.BackendTrafficPolicy)
if !ok {
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to BackendTrafficPolicy")
return nil
}
httprouteList := []gatewayv1.HTTPRoute{}
for _, targetRef := range policy.Spec.TargetRefs {
service := &corev1.Service{}
if err := r.Get(ctx, client.ObjectKey{
Namespace: policy.Namespace,
Name: string(targetRef.Name),
}, service); err != nil {
if client.IgnoreNotFound(err) != nil {
r.Log.Error(err, "failed to get service", "namespace", policy.Namespace, "name", targetRef.Name)
}
continue
}
hrList := &gatewayv1.HTTPRouteList{}
if err := r.List(ctx, hrList, client.MatchingFields{
indexer.ServiceIndexRef: indexer.GenIndexKey(policy.Namespace, string(targetRef.Name)),
}); err != nil {
r.Log.Error(err, "failed to list httproutes by service reference", "service", targetRef.Name)
return nil
}
httprouteList = append(httprouteList, hrList.Items...)
}
var namespacedNameMap = make(map[k8stypes.NamespacedName]struct{})
requests := make([]reconcile.Request, 0, len(httprouteList))
for _, hr := range httprouteList {
key := k8stypes.NamespacedName{
Namespace: hr.Namespace,
Name: hr.Name,
}
if _, ok := namespacedNameMap[key]; !ok {
namespacedNameMap[key] = struct{}{}
requests = append(requests, reconcile.Request{
NamespacedName: client.ObjectKey{
Namespace: hr.Namespace,
Name: hr.Name,
},
})
}
}
return requests
}
func (r *HTTPRouteReconciler) listHTTPRoutesForGateway(ctx context.Context, obj client.Object) []reconcile.Request {
gateway, ok := obj.(*gatewayv1.Gateway)
if !ok {
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to Gateway")
}
hrList := &gatewayv1.HTTPRouteList{}
if err := r.List(ctx, hrList, client.MatchingFields{
indexer.ParentRefs: indexer.GenIndexKey(gateway.Namespace, gateway.Name),
}); err != nil {
r.Log.Error(err, "failed to list httproutes by gateway", "gateway", gateway.Name)
return nil
}
requests := make([]reconcile.Request, 0, len(hrList.Items))
for _, hr := range hrList.Items {
requests = append(requests, reconcile.Request{
NamespacedName: client.ObjectKey{
Namespace: hr.Namespace,
Name: hr.Name,
},
})
}
return requests
}
func (r *HTTPRouteReconciler) listHTTPRouteByHTTPRoutePolicy(ctx context.Context, obj client.Object) (requests []reconcile.Request) {
httpRoutePolicy, ok := obj.(*v1alpha1.HTTPRoutePolicy)
if !ok {
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to HTTPRoutePolicy")
return nil
}
var keys = make(map[k8stypes.NamespacedName]struct{})
for _, ref := range httpRoutePolicy.Spec.TargetRefs {
if ref.Kind != types.KindHTTPRoute {
continue
}
key := k8stypes.NamespacedName{
Namespace: obj.GetNamespace(),
Name: string(ref.Name),
}
if _, ok := keys[key]; ok {
continue
}
var httpRoute gatewayv1.HTTPRoute
if err := r.Get(ctx, key, &httpRoute); err != nil {
r.Log.Error(errors.New("httproute not found"),
"failed to get HTTPRoute rule for HTTPRoutePolicy targetRef",
"httproute", key,
)
continue
}
if ref.SectionName != nil {
var matchRuleName bool
for _, rule := range httpRoute.Spec.Rules {
if rule.Name != nil && *rule.Name == *ref.SectionName {
matchRuleName = true
break
}
}
if !matchRuleName {
r.Log.Error(errors.New("httproute section name not found"),
"failed to get HTTPRoute rule by HTTPRoutePolicy targetRef",
"httproute", key,
"sectionName", *ref.SectionName,
)
continue
}
}
keys[key] = struct{}{}
requests = append(requests, reconcile.Request{NamespacedName: key})
}
return requests
}
func (r *HTTPRouteReconciler) listHTTPRouteForGenericEvent(ctx context.Context, obj client.Object) (requests []reconcile.Request) {
switch obj.(type) {
case *v1alpha1.BackendTrafficPolicy:
return r.listHTTPRoutesForBackendTrafficPolicy(ctx, obj)
case *v1alpha1.HTTPRoutePolicy:
return r.listHTTPRouteByHTTPRoutePolicy(ctx, obj)
default:
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to BackendTrafficPolicy or HTTPRoutePolicy")
return nil
}
}
func (r *HTTPRouteReconciler) processHTTPRouteBackendRefs(tctx *provider.TranslateContext, hrNN k8stypes.NamespacedName) error {
var terr error
for _, backend := range tctx.BackendRefs {
targetNN := k8stypes.NamespacedName{
Namespace: hrNN.Namespace,
Name: string(backend.Name),
}
if backend.Namespace != nil {
targetNN.Namespace = string(*backend.Namespace)
}
if backend.Kind != nil && *backend.Kind != types.KindService {
terr = types.NewInvalidKindError(*backend.Kind)
continue
}
if backend.Port == nil {
terr = fmt.Errorf("port is required")
continue
}
var service corev1.Service
if err := r.Get(tctx, targetNN, &service); err != nil {
terr = err
if client.IgnoreNotFound(err) == nil {
terr = types.ReasonError{
Reason: string(gatewayv1.RouteReasonBackendNotFound),
Message: fmt.Sprintf("Service %s not found", targetNN),
}
}
continue
}
// if cross namespaces between HTTPRoute and referenced Service, check ReferenceGrant
if hrNN.Namespace != targetNN.Namespace {
if permitted := checkReferenceGrant(tctx,
r.Client,
v1beta1.ReferenceGrantFrom{
Group: gatewayv1.GroupName,
Kind: KindHTTPRoute,
Namespace: v1beta1.Namespace(hrNN.Namespace),
},
gatewayv1.ObjectReference{
Group: corev1.GroupName,
Kind: KindService,
Name: gatewayv1.ObjectName(targetNN.Name),
Namespace: (*gatewayv1.Namespace)(&targetNN.Namespace),
},
); !permitted {
terr = types.ReasonError{
Reason: string(v1beta1.RouteReasonRefNotPermitted),
Message: fmt.Sprintf("%s is in a different namespace than the HTTPRoute %s and no ReferenceGrant allowing reference is configured", targetNN, hrNN),
}
continue
}
}
if service.Spec.Type == corev1.ServiceTypeExternalName {
tctx.Services[targetNN] = &service
continue
}
portExists := false
for _, port := range service.Spec.Ports {
if port.Port == int32(*backend.Port) {
portExists = true
break
}
}
if !portExists {
terr = fmt.Errorf("port %d not found in service %s", *backend.Port, targetNN.Name)
continue
}
tctx.Services[targetNN] = &service
endpointSliceList := new(discoveryv1.EndpointSliceList)
if err := r.List(tctx, endpointSliceList,
client.InNamespace(targetNN.Namespace),
client.MatchingLabels{
discoveryv1.LabelServiceName: targetNN.Name,
},
); err != nil {
r.Log.Error(err, "failed to list endpoint slices", "Service", targetNN)
terr = err
continue
}
tctx.EndpointSlices[targetNN] = endpointSliceList.Items
}
return terr
}
func (r *HTTPRouteReconciler) processHTTPRoute(tctx *provider.TranslateContext, httpRoute *gatewayv1.HTTPRoute) error {
var terror error
for _, rule := range httpRoute.Spec.Rules {
for _, filter := range rule.Filters {
if filter.Type != gatewayv1.HTTPRouteFilterExtensionRef || filter.ExtensionRef == nil {
continue
}
if filter.ExtensionRef.Kind == types.KindPluginConfig {
pluginconfig := new(v1alpha1.PluginConfig)
if err := r.Get(context.Background(), client.ObjectKey{
Namespace: httpRoute.GetNamespace(),
Name: string(filter.ExtensionRef.Name),
}, pluginconfig); err != nil {
terror = err
continue
}
tctx.PluginConfigs[k8stypes.NamespacedName{
Namespace: httpRoute.GetNamespace(),
Name: string(filter.ExtensionRef.Name),
}] = pluginconfig
}
}
for _, backend := range rule.BackendRefs {
if backend.Kind != nil && *backend.Kind != types.KindService {
terror = types.NewInvalidKindError(*backend.Kind)
continue
}
tctx.BackendRefs = append(tctx.BackendRefs, gatewayv1.BackendRef{
BackendObjectReference: gatewayv1.BackendObjectReference{
Name: backend.Name,
Namespace: cmp.Or(backend.Namespace, (*gatewayv1.Namespace)(&httpRoute.Namespace)),
Port: backend.Port,
},
})
}
}
return terror
}
func httpRoutePolicyPredicateFuncs(channel chan event.GenericEvent) predicate.Predicate {
return predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool {
return true
},
DeleteFunc: func(e event.DeleteEvent) bool {
return true
},
UpdateFunc: func(e event.UpdateEvent) bool {
oldPolicy, ok0 := e.ObjectOld.(*v1alpha1.HTTPRoutePolicy)
newPolicy, ok1 := e.ObjectNew.(*v1alpha1.HTTPRoutePolicy)
if !ok0 || !ok1 {
return false
}
discardsRefs := slices.DeleteFunc(oldPolicy.Spec.TargetRefs, func(oldRef v1alpha2.LocalPolicyTargetReferenceWithSectionName) bool {
return slices.ContainsFunc(newPolicy.Spec.TargetRefs, func(newRef v1alpha2.LocalPolicyTargetReferenceWithSectionName) bool {
return oldRef.LocalPolicyTargetReference == newRef.LocalPolicyTargetReference && ptr.Equal(oldRef.SectionName, newRef.SectionName)
})
})
if len(discardsRefs) > 0 {
dump := oldPolicy.DeepCopy()
dump.Spec.TargetRefs = discardsRefs
channel <- event.GenericEvent{Object: dump}
}
return true
},
GenericFunc: func(e event.GenericEvent) bool {
return false
},
}
}
// listHTTPRoutesForGatewayProxy list all HTTPRoute resources that are affected by a given GatewayProxy
func (r *HTTPRouteReconciler) listHTTPRoutesForGatewayProxy(ctx context.Context, obj client.Object) []reconcile.Request {
gatewayProxy, ok := obj.(*v1alpha1.GatewayProxy)
if !ok {
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to GatewayProxy")
return nil
}
namespace := gatewayProxy.GetNamespace()
name := gatewayProxy.GetName()
// find all gateways that reference this gateway proxy
gatewayList := &gatewayv1.GatewayList{}
if err := r.List(ctx, gatewayList, client.MatchingFields{
indexer.ParametersRef: indexer.GenIndexKey(namespace, name),
}); err != nil {
r.Log.Error(err, "failed to list gateways for gateway proxy", "gatewayproxy", gatewayProxy.GetName())
return nil
}
var requests []reconcile.Request
// for each gateway, find all HTTPRoute resources that reference it
for _, gateway := range gatewayList.Items {
httpRouteList := &gatewayv1.HTTPRouteList{}
if err := r.List(ctx, httpRouteList, client.MatchingFields{
indexer.ParentRefs: indexer.GenIndexKey(gateway.Namespace, gateway.Name),
}); err != nil {
r.Log.Error(err, "failed to list httproutes for gateway", "gateway", gateway.Name)
continue
}
for _, httpRoute := range httpRouteList.Items {
requests = append(requests, reconcile.Request{
NamespacedName: client.ObjectKey{
Namespace: httpRoute.Namespace,
Name: httpRoute.Name,
},
})
}
}
return requests
}
func (r *HTTPRouteReconciler) listHTTPRoutesForReferenceGrant(ctx context.Context, obj client.Object) (requests []reconcile.Request) {
grant, ok := obj.(*v1beta1.ReferenceGrant)
if !ok {
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to ReferenceGrant")
return nil
}
var httpRouteList gatewayv1.HTTPRouteList
if err := r.List(ctx, &httpRouteList); err != nil {
r.Log.Error(err, "failed to list httproutes for reference ReferenceGrant", "ReferenceGrant", k8stypes.NamespacedName{Namespace: obj.GetNamespace(), Name: obj.GetName()})
return nil
}
for _, httpRoute := range httpRouteList.Items {
hr := v1beta1.ReferenceGrantFrom{
Group: gatewayv1.GroupName,
Kind: KindHTTPRoute,
Namespace: v1beta1.Namespace(httpRoute.GetNamespace()),
}
for _, from := range grant.Spec.From {
if from == hr {
requests = append(requests, reconcile.Request{
NamespacedName: client.ObjectKey{
Namespace: httpRoute.GetNamespace(),
Name: httpRoute.GetName(),
},
})
}
}
}
return requests
}