diff --git a/integration/k8s_conformance_test.go b/integration/k8s_conformance_test.go index f5c869f6c..88be63c28 100644 --- a/integration/k8s_conformance_test.go +++ b/integration/k8s_conformance_test.go @@ -153,17 +153,16 @@ func (s *K8sConformanceSuite) TestK8sGatewayAPIConformance() { RequiredConsecutiveSuccesses: 0, }, SupportedFeatures: sets.New[ksuite.SupportedFeature](). - Insert(ksuite.GatewayCoreFeatures.UnsortedList()...), + Insert(ksuite.GatewayCoreFeatures.UnsortedList()...). + Insert(ksuite.ReferenceGrantCoreFeatures.UnsortedList()...), EnableAllSupportedFeatures: false, RunTest: *k8sConformanceRunTest, // Until the feature are all supported, following tests are skipped. SkipTests: []string{ "HTTPExactPathMatching", "HTTPRouteHostnameIntersection", - "GatewaySecretReferenceGrantAllInNamespace", "HTTPRouteListenerHostnameMatching", "HTTPRouteRequestHeaderModifier", - "GatewaySecretInvalidReferenceGrant", "GatewayClassObservedGenerationBump", "HTTPRouteInvalidNonExistentBackendRef", "GatewayWithAttachedRoutes", @@ -171,14 +170,11 @@ func (s *K8sConformanceSuite) TestK8sGatewayAPIConformance() { "HTTPRouteDisallowedKind", "HTTPRouteInvalidReferenceGrant", "HTTPRouteObservedGenerationBump", - "GatewayInvalidRouteKind", "TLSRouteSimpleSameNamespace", "TLSRouteInvalidReferenceGrant", "HTTPRouteInvalidCrossNamespaceParentRef", "HTTPRouteInvalidParentRefNotMatchingSectionName", - "GatewaySecretReferenceGrantSpecific", "GatewayModifyListeners", - "GatewaySecretMissingReferenceGrant", "GatewayInvalidTLSConfiguration", "HTTPRouteInvalidCrossNamespaceBackendRef", "HTTPRouteMatchingAcrossRoutes", diff --git a/pkg/provider/kubernetes/gateway/client.go b/pkg/provider/kubernetes/gateway/client.go index 709be079d..dc957e966 100644 --- a/pkg/provider/kubernetes/gateway/client.go +++ b/pkg/provider/kubernetes/gateway/client.go @@ -19,6 +19,7 @@ import ( "k8s.io/client-go/tools/clientcmd" gatev1 "sigs.k8s.io/gateway-api/apis/v1" gatev1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gatev1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" gateclientset "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned" gateinformers "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions" ) @@ -34,13 +35,7 @@ func (reh *resourceEventHandler) OnAdd(obj interface{}, isInInitialList bool) { } func (reh *resourceEventHandler) OnUpdate(oldObj, newObj interface{}) { - switch oldObj.(type) { - case *gatev1.GatewayClass: - // Skip update for gateway classes. We only manage addition or deletion for this cluster-wide resource. - return - default: - eventHandlerFunc(reh.ev, newObj) - } + eventHandlerFunc(reh.ev, newObj) } func (reh *resourceEventHandler) OnDelete(obj interface{}) { @@ -59,6 +54,7 @@ type Client interface { GetHTTPRoutes(namespaces []string) ([]*gatev1.HTTPRoute, error) GetTCPRoutes(namespaces []string) ([]*gatev1alpha2.TCPRoute, error) GetTLSRoutes(namespaces []string) ([]*gatev1alpha2.TLSRoute, error) + GetReferenceGrants(namespace string) ([]*gatev1beta1.ReferenceGrant, error) GetService(namespace, name string) (*corev1.Service, bool, error) GetSecret(namespace, name string) (*corev1.Secret, bool, error) GetEndpoints(namespace, name string) (*corev1.Endpoints, bool, error) @@ -189,9 +185,6 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< return nil, err } - // TODO manage Reference Policy - // https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.ReferencePolicy - for _, ns := range namespaces { factoryGateway := gateinformers.NewSharedInformerFactoryWithOptions(c.csGateway, resyncPeriod, gateinformers.WithNamespace(ns)) _, err = factoryGateway.Gateway().V1().Gateways().Informer().AddEventHandler(eventHandler) @@ -210,6 +203,10 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< if err != nil { return nil, err } + _, err = factoryGateway.Gateway().V1beta1().ReferenceGrants().Informer().AddEventHandler(eventHandler) + if err != nil { + return nil, err + } factoryKube := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns)) _, err = factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler) @@ -363,6 +360,21 @@ func (c *clientWrapper) GetTLSRoutes(namespaces []string) ([]*gatev1alpha2.TLSRo return tlsRoutes, nil } +func (c *clientWrapper) GetReferenceGrants(namespace string) ([]*gatev1beta1.ReferenceGrant, error) { + if !c.isWatchedNamespace(namespace) { + log.Warn().Msgf("Failed to get ReferenceGrants: %q is not within watched namespaces", namespace) + + return nil, fmt.Errorf("failed to get ReferenceGrants: namespace %s is not within watched namespaces", namespace) + } + + referenceGrants, err := c.factoriesGateway[c.lookupNamespace(namespace)].Gateway().V1beta1().ReferenceGrants().Lister().ReferenceGrants(namespace).List(labels.Everything()) + if err != nil { + return nil, err + } + + return referenceGrants, nil +} + func (c *clientWrapper) GetGateways() []*gatev1.Gateway { var result []*gatev1.Gateway @@ -388,7 +400,7 @@ func (c *clientWrapper) UpdateGatewayClassStatus(gatewayClass *gatev1.GatewayCla var newConditions []metav1.Condition for _, cond := range gc.Status.Conditions { // No update for identical condition. - if cond.Type == condition.Type && cond.Status == condition.Status { + if cond.Type == condition.Type && cond.Status == condition.Status && cond.ObservedGeneration == condition.ObservedGeneration { return nil } @@ -470,7 +482,7 @@ func conditionsEquals(conditionsA, conditionsB []metav1.Condition) bool { for _, conditionA := range conditionsA { for _, conditionB := range conditionsB { if conditionA.Type == conditionB.Type { - if conditionA.Reason != conditionB.Reason || conditionA.Status != conditionB.Status || conditionA.Message != conditionB.Message { + if conditionA.Reason != conditionB.Reason || conditionA.Status != conditionB.Status || conditionA.Message != conditionB.Message || conditionA.ObservedGeneration != conditionB.ObservedGeneration { return false } conditionMatches++ diff --git a/pkg/provider/kubernetes/gateway/client_mock_test.go b/pkg/provider/kubernetes/gateway/client_mock_test.go index 01611de40..b19dd9609 100644 --- a/pkg/provider/kubernetes/gateway/client_mock_test.go +++ b/pkg/provider/kubernetes/gateway/client_mock_test.go @@ -12,6 +12,7 @@ import ( kscheme "k8s.io/client-go/kubernetes/scheme" gatev1 "sigs.k8s.io/gateway-api/apis/v1" gatev1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gatev1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) var _ Client = (*clientMock)(nil) @@ -23,6 +24,11 @@ func init() { panic(err) } + err = gatev1beta1.AddToScheme(kscheme.Scheme) + if err != nil { + panic(err) + } + err = gatev1.AddToScheme(kscheme.Scheme) if err != nil { panic(err) @@ -39,11 +45,12 @@ type clientMock struct { apiSecretError error apiEndpointsError error - gatewayClasses []*gatev1.GatewayClass - gateways []*gatev1.Gateway - httpRoutes []*gatev1.HTTPRoute - tcpRoutes []*gatev1alpha2.TCPRoute - tlsRoutes []*gatev1alpha2.TLSRoute + gatewayClasses []*gatev1.GatewayClass + gateways []*gatev1.Gateway + httpRoutes []*gatev1.HTTPRoute + tcpRoutes []*gatev1alpha2.TCPRoute + tlsRoutes []*gatev1alpha2.TLSRoute + referenceGrants []*gatev1beta1.ReferenceGrant watchChan chan interface{} } @@ -78,6 +85,8 @@ func newClientMock(paths ...string) clientMock { c.tcpRoutes = append(c.tcpRoutes, o) case *gatev1alpha2.TLSRoute: c.tlsRoutes = append(c.tlsRoutes, o) + case *gatev1beta1.ReferenceGrant: + c.referenceGrants = append(c.referenceGrants, o) default: panic(fmt.Sprintf("Unknown runtime object %+v %T", o, o)) } @@ -190,6 +199,16 @@ func (c clientMock) GetTLSRoutes(namespaces []string) ([]*gatev1alpha2.TLSRoute, return tlsRoutes, nil } +func (c clientMock) GetReferenceGrants(namespace string) ([]*gatev1beta1.ReferenceGrant, error) { + var referenceGrants []*gatev1beta1.ReferenceGrant + for _, referenceGrant := range c.referenceGrants { + if inNamespace(referenceGrant.ObjectMeta, namespace) { + referenceGrants = append(referenceGrants, referenceGrant) + } + } + return referenceGrants, nil +} + func (c clientMock) GetService(namespace, name string) (*corev1.Service, bool, error) { if c.apiServiceError != nil { return nil, false, c.apiServiceError diff --git a/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret.yml b/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret.yml new file mode 100644 index 000000000..ba5f1a9fb --- /dev/null +++ b/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret.yml @@ -0,0 +1,78 @@ +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: ReferenceGrant +metadata: + name: secret-from-default + namespace: secret-namespace +spec: + from: + - group: gateway.networking.k8s.io + kind: Gateway + namespace: default + to: + - group: "" + kind: Secret +--- +apiVersion: v1 +kind: Secret +metadata: + name: supersecret + namespace: secret-namespace + +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + +--- +kind: GatewayClass +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: my-gateway-class +spec: + controllerName: traefik.io/gateway-controller + +--- +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: my-gateway + namespace: default +spec: + gatewayClassName: my-gateway-class + listeners: # Use GatewayClass defaults for listener definition. + - name: tls + protocol: TLS + port: 9000 + hostname: foo.example.com + tls: + mode: Terminate # Default mode + certificateRefs: + - kind: Secret + name: supersecret + namespace: secret-namespace + group: "" + allowedRoutes: + kinds: + - kind: TCPRoute + group: gateway.networking.k8s.io + namespaces: + from: Same + +--- +kind: TCPRoute +apiVersion: gateway.networking.k8s.io/v1alpha2 +metadata: + name: tcp-app-1 + namespace: default +spec: + parentRefs: + - name: my-gateway + kind: Gateway + group: gateway.networking.k8s.io + rules: + - backendRefs: + - name: whoamitcp + port: 9000 + weight: 1 + kind: Service + group: "" diff --git a/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_missing.yml b/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_missing.yml new file mode 100644 index 000000000..660ff18b6 --- /dev/null +++ b/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_missing.yml @@ -0,0 +1,64 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + name: supersecret + namespace: secret-namespace + +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + +--- +kind: GatewayClass +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: my-gateway-class +spec: + controllerName: traefik.io/gateway-controller + +--- +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: my-gateway + namespace: default +spec: + gatewayClassName: my-gateway-class + listeners: # Use GatewayClass defaults for listener definition. + - name: tls + protocol: TLS + port: 9000 + hostname: foo.example.com + tls: + mode: Terminate # Default mode + certificateRefs: + - kind: Secret + name: supersecret + namespace: secret-namespace + group: "" + allowedRoutes: + kinds: + - kind: TCPRoute + group: gateway.networking.k8s.io + namespaces: + from: Same + +--- +kind: TCPRoute +apiVersion: gateway.networking.k8s.io/v1alpha2 +metadata: + name: tcp-app-1 + namespace: default +spec: + parentRefs: + - name: my-gateway + kind: Gateway + group: gateway.networking.k8s.io + rules: + - backendRefs: + - name: whoamitcp + port: 9000 + weight: 1 + kind: Service + group: "" diff --git a/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_not_matching_from.yml b/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_not_matching_from.yml new file mode 100644 index 000000000..47aba19d8 --- /dev/null +++ b/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_not_matching_from.yml @@ -0,0 +1,78 @@ +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: ReferenceGrant +metadata: + name: secret-from-default + namespace: secret-namespace +spec: + from: + - group: gateway.networking.k8s.io + kind: Gateway + namespace: differentnamespace + to: + - group: "" + kind: Secret +--- +apiVersion: v1 +kind: Secret +metadata: + name: supersecret + namespace: secret-namespace + +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + +--- +kind: GatewayClass +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: my-gateway-class +spec: + controllerName: traefik.io/gateway-controller + +--- +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: my-gateway + namespace: default +spec: + gatewayClassName: my-gateway-class + listeners: # Use GatewayClass defaults for listener definition. + - name: tls + protocol: TLS + port: 9000 + hostname: foo.example.com + tls: + mode: Terminate # Default mode + certificateRefs: + - kind: Secret + name: supersecret + namespace: secret-namespace + group: "" + allowedRoutes: + kinds: + - kind: TCPRoute + group: gateway.networking.k8s.io + namespaces: + from: Same + +--- +kind: TCPRoute +apiVersion: gateway.networking.k8s.io/v1alpha2 +metadata: + name: tcp-app-1 + namespace: default +spec: + parentRefs: + - name: my-gateway + kind: Gateway + group: gateway.networking.k8s.io + rules: + - backendRefs: + - name: whoamitcp + port: 9000 + weight: 1 + kind: Service + group: "" diff --git a/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_not_matching_to.yml b/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_not_matching_to.yml new file mode 100644 index 000000000..e08fa7a42 --- /dev/null +++ b/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_not_matching_to.yml @@ -0,0 +1,79 @@ +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: ReferenceGrant +metadata: + name: secret-from-default + namespace: secret-namespace +spec: + from: + - group: gateway.networking.k8s.io + kind: Gateway + namespace: default + to: + - group: "" + kind: Secret + name: differentsecret +--- +apiVersion: v1 +kind: Secret +metadata: + name: supersecret + namespace: secret-namespace + +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + +--- +kind: GatewayClass +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: my-gateway-class +spec: + controllerName: traefik.io/gateway-controller + +--- +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: my-gateway + namespace: default +spec: + gatewayClassName: my-gateway-class + listeners: # Use GatewayClass defaults for listener definition. + - name: tls + protocol: TLS + port: 9000 + hostname: foo.example.com + tls: + mode: Terminate # Default mode + certificateRefs: + - kind: Secret + name: supersecret + namespace: secret-namespace + group: "" + allowedRoutes: + kinds: + - kind: TCPRoute + group: gateway.networking.k8s.io + namespaces: + from: Same + +--- +kind: TCPRoute +apiVersion: gateway.networking.k8s.io/v1alpha2 +metadata: + name: tcp-app-1 + namespace: default +spec: + parentRefs: + - name: my-gateway + kind: Gateway + group: gateway.networking.k8s.io + rules: + - backendRefs: + - name: whoamitcp + port: 9000 + weight: 1 + kind: Service + group: "" diff --git a/pkg/provider/kubernetes/gateway/kubernetes.go b/pkg/provider/kubernetes/gateway/kubernetes.go index c632cde73..724d5b841 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes.go +++ b/pkg/provider/kubernetes/gateway/kubernetes.go @@ -34,11 +34,14 @@ import ( "k8s.io/utils/ptr" "k8s.io/utils/strings/slices" gatev1 "sigs.k8s.io/gateway-api/apis/v1" + gatev1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) const ( providerName = "kubernetesgateway" + groupCore = "core" + kindGateway = "Gateway" kindTraefikService = "TraefikService" kindHTTPRoute = "HTTPRoute" @@ -348,6 +351,7 @@ func (p *Provider) fillGatewayConf(ctx context.Context, client Client, gateway * Name: listener.Name, SupportedKinds: []gatev1.RouteGroupKind{}, Conditions: []metav1.Condition{}, + // AttachedRoutes: 0 TODO Set to number of Routes associated with a Listener regardless of Gateway or Route status } supportedKinds, conditions := supportedRouteKinds(listener.Protocol) @@ -356,9 +360,8 @@ func (p *Provider) fillGatewayConf(ctx context.Context, client Client, gateway * continue } - listenerStatuses[i].SupportedKinds = supportedKinds - routeKinds, conditions := getAllowedRouteKinds(gateway, listener, supportedKinds) + listenerStatuses[i].SupportedKinds = routeKinds if len(conditions) > 0 { listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions, conditions...) continue @@ -474,7 +477,7 @@ func (p *Provider) fillGatewayConf(ctx context.Context, client Client, gateway * certificateRef := listener.TLS.CertificateRefs[0] if certificateRef.Kind == nil || *certificateRef.Kind != "Secret" || - certificateRef.Group == nil || (*certificateRef.Group != "" && *certificateRef.Group != "core") { + certificateRef.Group == nil || (*certificateRef.Group != "" && *certificateRef.Group != groupCore) { // update "ResolvedRefs" status true with "InvalidCertificateRef" reason listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions, metav1.Condition{ Type: string(gatev1.ListenerConditionResolvedRefs), @@ -482,43 +485,74 @@ func (p *Provider) fillGatewayConf(ctx context.Context, client Client, gateway * ObservedGeneration: gateway.Generation, LastTransitionTime: metav1.Now(), Reason: string(gatev1.ListenerReasonInvalidCertificateRef), - Message: fmt.Sprintf("Unsupported TLS CertificateRef group/kind: %v/%v", certificateRef.Group, certificateRef.Kind), + Message: fmt.Sprintf("Unsupported TLS CertificateRef group/kind: %s/%s", groupToString(certificateRef.Group), kindToString(certificateRef.Kind)), }) continue } - // TODO Support ReferencePolicy to support cross namespace references. + certificateNamespace := gateway.Namespace if certificateRef.Namespace != nil && string(*certificateRef.Namespace) != gateway.Namespace { - listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions, metav1.Condition{ - Type: string(gatev1.ListenerConditionResolvedRefs), - Status: metav1.ConditionFalse, - ObservedGeneration: gateway.Generation, - LastTransitionTime: metav1.Now(), - Reason: string(gatev1.ListenerReasonInvalidCertificateRef), - Message: "Cross namespace secrets are not supported", - }) - - continue + certificateNamespace = string(*certificateRef.Namespace) } - configKey := gateway.Namespace + "/" + string(certificateRef.Name) - if _, tlsExists := tlsConfigs[configKey]; !tlsExists { - tlsConf, err := getTLS(client, certificateRef.Name, gateway.Namespace) + if certificateNamespace != gateway.Namespace { + referenceGrants, err := client.GetReferenceGrants(certificateNamespace) if err != nil { - // update "ResolvedRefs" status true with "InvalidCertificateRef" reason listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions, metav1.Condition{ Type: string(gatev1.ListenerConditionResolvedRefs), Status: metav1.ConditionFalse, ObservedGeneration: gateway.Generation, LastTransitionTime: metav1.Now(), - Reason: string(gatev1.ListenerReasonInvalidCertificateRef), - Message: fmt.Sprintf("Error while retrieving certificate: %v", err), + Reason: string(gatev1.ListenerReasonRefNotPermitted), + Message: fmt.Sprintf("Cannot find any ReferenceGrant: %v", err), + }) + continue + } + + referenceGrants = filterReferenceGrantsFrom(referenceGrants, "gateway.networking.k8s.io", "Gateway", gateway.Namespace) + referenceGrants = filterReferenceGrantsTo(referenceGrants, groupCore, "Secret", string(certificateRef.Name)) + if len(referenceGrants) == 0 { + listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions, metav1.Condition{ + Type: string(gatev1.ListenerConditionResolvedRefs), + Status: metav1.ConditionFalse, + ObservedGeneration: gateway.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.ListenerReasonRefNotPermitted), + Message: "Required ReferenceGrant for cross namespace secret reference is missing", }) continue } + } + configKey := certificateNamespace + "/" + string(certificateRef.Name) + if _, tlsExists := tlsConfigs[configKey]; !tlsExists { + tlsConf, err := getTLS(client, certificateRef.Name, certificateNamespace) + if err != nil { + // update "ResolvedRefs" status false with "InvalidCertificateRef" reason + // update "Programmed" status false with "Invalid" reason + listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions, + metav1.Condition{ + Type: string(gatev1.ListenerConditionResolvedRefs), + Status: metav1.ConditionFalse, + ObservedGeneration: gateway.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.ListenerReasonInvalidCertificateRef), + Message: fmt.Sprintf("Error while retrieving certificate: %v", err), + }, + metav1.Condition{ + Type: string(gatev1.ListenerConditionProgrammed), + Status: metav1.ConditionFalse, + ObservedGeneration: gateway.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.ListenerReasonInvalid), + Message: fmt.Sprintf("Error while retrieving certificate: %v", err), + }, + ) + + continue + } tlsConfigs[configKey] = tlsConf } } @@ -548,15 +582,32 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listenerStatuses [ var result error for i, listener := range listenerStatuses { if len(listener.Conditions) == 0 { - // GatewayConditionReady "Ready", GatewayConditionReason "ListenerReady" - listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions, metav1.Condition{ - Type: string(gatev1.ListenerReasonAccepted), - Status: metav1.ConditionTrue, - ObservedGeneration: gateway.Generation, - LastTransitionTime: metav1.Now(), - Reason: "ListenerReady", - Message: "No error found", - }) + listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions, + metav1.Condition{ + Type: string(gatev1.ListenerConditionAccepted), + Status: metav1.ConditionTrue, + ObservedGeneration: gateway.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.ListenerReasonAccepted), + Message: "No error found", + }, + metav1.Condition{ + Type: string(gatev1.ListenerConditionResolvedRefs), + Status: metav1.ConditionTrue, + ObservedGeneration: gateway.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.ListenerReasonResolvedRefs), + Message: "No error found", + }, + metav1.Condition{ + Type: string(gatev1.ListenerConditionProgrammed), + Status: metav1.ConditionTrue, + ObservedGeneration: gateway.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.ListenerReasonProgrammed), + Message: "No error found", + }, + ) continue } @@ -565,6 +616,7 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listenerStatuses [ result = multierror.Append(result, errors.New(condition.Message)) } } + gatewayStatus.Listeners = listenerStatuses if result != nil { // GatewayConditionReady "Ready", GatewayConditionReason "ListenersNotValid" @@ -580,8 +632,6 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listenerStatuses [ return gatewayStatus, result } - gatewayStatus.Listeners = listenerStatuses - gatewayStatus.Conditions = append(gatewayStatus.Conditions, // update "Accepted" status with "Accepted" reason metav1.Condition{ @@ -656,7 +706,7 @@ func getAllowedRouteKinds(gateway *gatev1.Gateway, listener gatev1.Listener, sup } var ( - routeKinds []gatev1.RouteGroupKind + routeKinds = []gatev1.RouteGroupKind{} conditions []metav1.Condition ) @@ -672,12 +722,12 @@ func getAllowedRouteKinds(gateway *gatev1.Gateway, listener gatev1.Listener, sup if !isSupported { conditions = append(conditions, metav1.Condition{ - Type: string(gatev1.ListenerConditionAccepted), - Status: metav1.ConditionTrue, + Type: string(gatev1.ListenerConditionResolvedRefs), + Status: metav1.ConditionFalse, ObservedGeneration: gateway.Generation, LastTransitionTime: metav1.Now(), Reason: string(gatev1.ListenerReasonInvalidRouteKinds), - Message: fmt.Sprintf("Listener protocol %q does not support RouteGroupKind %v/%s", listener.Protocol, routeKind.Group, routeKind.Kind), + Message: fmt.Sprintf("Listener protocol %q does not support RouteGroupKind %s/%s", listener.Protocol, groupToString(routeKind.Group), routeKind.Kind), }) continue } @@ -712,7 +762,7 @@ func (p *Provider) gatewayHTTPRouteToHTTPConf(ctx context.Context, ep string, li routes, err := client.GetHTTPRoutes(namespaces) if err != nil { - // update "ResolvedRefs" status true with "InvalidRoutesRef" reason + // update "ResolvedRefs" status true with "RefNotPermitted" reason return []metav1.Condition{{ Type: string(gatev1.ListenerConditionResolvedRefs), Status: metav1.ConditionFalse, @@ -757,7 +807,7 @@ func (p *Provider) gatewayHTTPRouteToHTTPConf(ctx context.Context, ep string, li for _, routeRule := range route.Spec.Rules { rule, err := extractRule(routeRule, hostRule) if err != nil { - // update "ResolvedRefs" status true with "DroppedRoutes" reason + // update "ResolvedRefs" status true with "UnsupportedPathOrHeaderType" reason conditions = append(conditions, metav1.Condition{ Type: string(gatev1.ListenerConditionResolvedRefs), Status: metav1.ConditionFalse, @@ -1048,7 +1098,7 @@ func gatewayTLSRouteToTCPConf(ctx context.Context, ep string, listener gatev1.Li Type: string(gatev1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionFalse, ObservedGeneration: gateway.Generation, - Reason: string(gatev1.ListenerConditionConflicted), + Reason: string(gatev1.ListenerReasonHostnameConflict), Message: fmt.Sprintf("No hostname match between listener: %v and route: %v", listener.Hostname, route.Spec.Hostnames), LastTransitionTime: metav1.Now(), }) @@ -1059,7 +1109,7 @@ func gatewayTLSRouteToTCPConf(ctx context.Context, ep string, listener gatev1.Li rule, err := hostSNIRule(hostnames) if err != nil { - // update "ResolvedRefs" status true with "DroppedRoutes" reason + // update "ResolvedRefs" status true with "InvalidHostnames" reason conditions = append(conditions, metav1.Condition{ Type: string(gatev1.ListenerConditionResolvedRefs), Status: metav1.ConditionFalse, @@ -1111,7 +1161,7 @@ func gatewayTLSRouteToTCPConf(ctx context.Context, ep string, listener gatev1.Li wrrService, subServices, err := loadTCPServices(client, route.Namespace, routeRule.BackendRefs) if err != nil { - // update "ResolvedRefs" status true with "DroppedRoutes" reason + // update "ResolvedRefs" status true with "InvalidBackendRefs" reason conditions = append(conditions, metav1.Condition{ Type: string(gatev1.ListenerConditionResolvedRefs), Status: metav1.ConditionFalse, @@ -1526,7 +1576,7 @@ func loadServices(client Client, namespace string, backendRefs []gatev1.HTTPBack continue } - if *backendRef.Group != "" && *backendRef.Group != "core" && *backendRef.Kind != "Service" { + if *backendRef.Group != "" && *backendRef.Group != groupCore && *backendRef.Kind != "Service" { return nil, nil, fmt.Errorf("unsupported HTTPBackendRef %s/%s/%s", *backendRef.Group, *backendRef.Kind, backendRef.Name) } @@ -1649,7 +1699,7 @@ func loadTCPServices(client Client, namespace string, backendRefs []gatev1.Backe continue } - if *backendRef.Group != "" && *backendRef.Group != "core" && *backendRef.Kind != "Service" { + if *backendRef.Group != "" && *backendRef.Group != groupCore && *backendRef.Kind != "Service" { return nil, nil, fmt.Errorf("unsupported BackendRef %s/%s/%s", *backendRef.Group, *backendRef.Kind, backendRef.Name) } @@ -1880,3 +1930,65 @@ func makeListenerKey(l gatev1.Listener) string { return fmt.Sprintf("%s|%s|%d", l.Protocol, hostname, l.Port) } + +func filterReferenceGrantsFrom(referenceGrants []*gatev1beta1.ReferenceGrant, group, kind, namespace string) []*gatev1beta1.ReferenceGrant { + var matchingReferenceGrants []*gatev1beta1.ReferenceGrant + for _, referenceGrant := range referenceGrants { + if referenceGrantMatchesFrom(referenceGrant, group, kind, namespace) { + matchingReferenceGrants = append(matchingReferenceGrants, referenceGrant) + } + } + return matchingReferenceGrants +} + +func referenceGrantMatchesFrom(referenceGrant *gatev1beta1.ReferenceGrant, group, kind, namespace string) bool { + for _, from := range referenceGrant.Spec.From { + sanitizedGroup := string(from.Group) + if sanitizedGroup == "" { + sanitizedGroup = groupCore + } + if string(from.Namespace) != namespace || string(from.Kind) != kind || sanitizedGroup != group { + continue + } + return true + } + return false +} + +func filterReferenceGrantsTo(referenceGrants []*gatev1beta1.ReferenceGrant, group, kind, name string) []*gatev1beta1.ReferenceGrant { + var matchingReferenceGrants []*gatev1beta1.ReferenceGrant + for _, referenceGrant := range referenceGrants { + if referenceGrantMatchesTo(referenceGrant, group, kind, name) { + matchingReferenceGrants = append(matchingReferenceGrants, referenceGrant) + } + } + return matchingReferenceGrants +} + +func referenceGrantMatchesTo(referenceGrant *gatev1beta1.ReferenceGrant, group, kind, name string) bool { + for _, to := range referenceGrant.Spec.To { + sanitizedGroup := string(to.Group) + if sanitizedGroup == "" { + sanitizedGroup = groupCore + } + if string(to.Kind) != kind || sanitizedGroup != group || (to.Name != nil && string(*to.Name) != name) { + continue + } + return true + } + return false +} + +func groupToString(p *gatev1.Group) string { + if p == nil { + return "" + } + return string(*p) +} + +func kindToString(p *gatev1.Kind) string { + if p == nil { + return "" + } + return string(*p) +} diff --git a/pkg/provider/kubernetes/gateway/kubernetes_test.go b/pkg/provider/kubernetes/gateway/kubernetes_test.go index df3b95a0b..d5aeca18b 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes_test.go +++ b/pkg/provider/kubernetes/gateway/kubernetes_test.go @@ -15,6 +15,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" gatev1 "sigs.k8s.io/gateway-api/apis/v1" + gatev1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) var _ provider.Provider = (*Provider)(nil) @@ -4628,6 +4629,196 @@ func TestLoadMixedRoutes(t *testing.T) { } } +func TestLoadRoutesWithReferenceGrants(t *testing.T) { + testCases := []struct { + desc string + ingressClass string + paths []string + expected *dynamic.Configuration + entryPoints map[string]Entrypoint + }{ + { + desc: "Empty", + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "Empty because ReferenceGrant for Secret is missing", + paths: []string{"services.yml", "referencegrant/for_secret_missing.yml"}, + entryPoints: map[string]Entrypoint{ + "tls": {Address: ":9000"}, + }, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "Empty because ReferenceGrant spec.from does not match", + paths: []string{"services.yml", "referencegrant/for_secret_not_matching_from.yml"}, + entryPoints: map[string]Entrypoint{ + "tls": {Address: ":9000"}, + }, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "Empty because ReferenceGrant spec.to does not match", + paths: []string{"services.yml", "referencegrant/for_secret_not_matching_to.yml"}, + entryPoints: map[string]Entrypoint{ + "tls": {Address: ":9000"}, + }, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "For Secret", + paths: []string{"services.yml", "referencegrant/for_secret.yml"}, + entryPoints: map[string]Entrypoint{ + "tls": {Address: ":9000"}, + }, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{ + "default-tcp-app-1-my-gateway-tls-e3b0c44298fc1c149afb": { + EntryPoints: []string{"tls"}, + Service: "default-tcp-app-1-my-gateway-tls-e3b0c44298fc1c149afb-wrr-0", + Rule: "HostSNI(`*`)", + RuleSyntax: "v3", + TLS: &dynamic.RouterTCPTLSConfig{}, + }, + }, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{ + "default-tcp-app-1-my-gateway-tls-e3b0c44298fc1c149afb-wrr-0": { + Weighted: &dynamic.TCPWeightedRoundRobin{ + Services: []dynamic.TCPWRRService{{ + Name: "default-whoamitcp-9000", + Weight: func(i int) *int { return &i }(1), + }}, + }, + }, + "default-whoamitcp-9000": { + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "10.10.0.9:9000", + }, + { + Address: "10.10.0.10:9000", + }, + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{ + Certificates: []*tls.CertAndStores{ + { + Certificate: tls.Certificate{ + CertFile: types.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), + KeyFile: types.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), + }, + }, + }, + }, + }, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + if test.expected == nil { + return + } + + p := Provider{EntryPoints: test.entryPoints} + conf := p.loadConfigurationFromGateway(context.Background(), newClientMock(test.paths...)) + assert.Equal(t, test.expected, conf) + }) + } +} + func Test_hostRule(t *testing.T) { testCases := []struct { desc string @@ -5644,3 +5835,247 @@ func kindPtr(kind gatev1.Kind) *gatev1.Kind { func pathMatchTypePtr(p gatev1.PathMatchType) *gatev1.PathMatchType { return &p } func headerMatchTypePtr(h gatev1.HeaderMatchType) *gatev1.HeaderMatchType { return &h } + +func Test_referenceGrantMatchesFrom(t *testing.T) { + testCases := []struct { + desc string + referenceGrant gatev1beta1.ReferenceGrant + group string + kind string + namespace string + expectedResult bool + }{ + { + desc: "matches", + referenceGrant: gatev1beta1.ReferenceGrant{ + Spec: gatev1beta1.ReferenceGrantSpec{ + From: []gatev1beta1.ReferenceGrantFrom{ + { + Group: "correct-group", + Kind: "correct-kind", + Namespace: "correct-namespace", + }, + }, + }, + }, + group: "correct-group", + kind: "correct-kind", + namespace: "correct-namespace", + expectedResult: true, + }, + { + desc: "empty group matches core", + referenceGrant: gatev1beta1.ReferenceGrant{ + Spec: gatev1beta1.ReferenceGrantSpec{ + From: []gatev1beta1.ReferenceGrantFrom{ + { + Group: "", + Kind: "correct-kind", + Namespace: "correct-namespace", + }, + }, + }, + }, + group: "core", + kind: "correct-kind", + namespace: "correct-namespace", + expectedResult: true, + }, + { + desc: "wrong group", + referenceGrant: gatev1beta1.ReferenceGrant{ + Spec: gatev1beta1.ReferenceGrantSpec{ + From: []gatev1beta1.ReferenceGrantFrom{ + { + Group: "wrong-group", + Kind: "correct-kind", + Namespace: "correct-namespace", + }, + }, + }, + }, + group: "correct-group", + kind: "correct-kind", + namespace: "correct-namespace", + expectedResult: false, + }, + { + desc: "wrong kind", + referenceGrant: gatev1beta1.ReferenceGrant{ + Spec: gatev1beta1.ReferenceGrantSpec{ + From: []gatev1beta1.ReferenceGrantFrom{ + { + Group: "correct-group", + Kind: "wrong-kind", + Namespace: "correct-namespace", + }, + }, + }, + }, + group: "correct-group", + kind: "correct-kind", + namespace: "correct-namespace", + expectedResult: false, + }, + { + desc: "wrong namespace", + referenceGrant: gatev1beta1.ReferenceGrant{ + Spec: gatev1beta1.ReferenceGrantSpec{ + From: []gatev1beta1.ReferenceGrantFrom{ + { + Group: "correct-group", + Kind: "correct-kind", + Namespace: "wrong-namespace", + }, + }, + }, + }, + group: "correct-group", + kind: "correct-kind", + namespace: "correct-namespace", + expectedResult: false, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + assert.Equal(t, test.expectedResult, referenceGrantMatchesFrom(&test.referenceGrant, test.group, test.kind, test.namespace)) + }) + } +} + +func Test_referenceGrantMatchesTo(t *testing.T) { + testCases := []struct { + desc string + referenceGrant gatev1beta1.ReferenceGrant + group string + kind string + name string + expectedResult bool + }{ + { + desc: "matches", + referenceGrant: gatev1beta1.ReferenceGrant{ + Spec: gatev1beta1.ReferenceGrantSpec{ + To: []gatev1beta1.ReferenceGrantTo{ + { + Group: "correct-group", + Kind: "correct-kind", + Name: objectNamePtr("correct-name"), + }, + }, + }, + }, + group: "correct-group", + kind: "correct-kind", + name: "correct-name", + expectedResult: true, + }, + { + desc: "matches without name", + referenceGrant: gatev1beta1.ReferenceGrant{ + Spec: gatev1beta1.ReferenceGrantSpec{ + To: []gatev1beta1.ReferenceGrantTo{ + { + Group: "correct-group", + Kind: "correct-kind", + Name: nil, + }, + }, + }, + }, + group: "correct-group", + kind: "correct-kind", + name: "some-name", + expectedResult: true, + }, + { + desc: "empty group matches core", + referenceGrant: gatev1beta1.ReferenceGrant{ + Spec: gatev1beta1.ReferenceGrantSpec{ + To: []gatev1beta1.ReferenceGrantTo{ + { + Group: "", + Kind: "correct-kind", + Name: objectNamePtr("correct-name"), + }, + }, + }, + }, + group: "core", + kind: "correct-kind", + name: "correct-name", + expectedResult: true, + }, + { + desc: "wrong group", + referenceGrant: gatev1beta1.ReferenceGrant{ + Spec: gatev1beta1.ReferenceGrantSpec{ + To: []gatev1beta1.ReferenceGrantTo{ + { + Group: "wrong-group", + Kind: "correct-kind", + Name: objectNamePtr("correct-name"), + }, + }, + }, + }, + group: "correct-group", + kind: "correct-kind", + name: "correct-namespace", + expectedResult: false, + }, + { + desc: "wrong kind", + referenceGrant: gatev1beta1.ReferenceGrant{ + Spec: gatev1beta1.ReferenceGrantSpec{ + To: []gatev1beta1.ReferenceGrantTo{ + { + Group: "correct-group", + Kind: "wrong-kind", + Name: objectNamePtr("correct-name"), + }, + }, + }, + }, + group: "correct-group", + kind: "correct-kind", + name: "correct-name", + expectedResult: false, + }, + { + desc: "wrong name", + referenceGrant: gatev1beta1.ReferenceGrant{ + Spec: gatev1beta1.ReferenceGrantSpec{ + To: []gatev1beta1.ReferenceGrantTo{ + { + Group: "correct-group", + Kind: "correct-kind", + Name: objectNamePtr("wrong-name"), + }, + }, + }, + }, + group: "correct-group", + kind: "correct-kind", + name: "correct-name", + expectedResult: false, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + assert.Equal(t, test.expectedResult, referenceGrantMatchesTo(&test.referenceGrant, test.group, test.kind, test.name)) + }) + } +} + +func objectNamePtr(objectName gatev1.ObjectName) *gatev1.ObjectName { + return &objectName +} diff --git a/pkg/provider/kubernetes/k8s/parser.go b/pkg/provider/kubernetes/k8s/parser.go index 769d5edf5..ddcd69b06 100644 --- a/pkg/provider/kubernetes/k8s/parser.go +++ b/pkg/provider/kubernetes/k8s/parser.go @@ -12,7 +12,7 @@ import ( // MustParseYaml parses a YAML to objects. func MustParseYaml(content []byte) []runtime.Object { - acceptedK8sTypes := regexp.MustCompile(`^(Namespace|Deployment|Endpoints|Service|Ingress|IngressRoute|IngressRouteTCP|IngressRouteUDP|Middleware|MiddlewareTCP|Secret|TLSOption|TLSStore|TraefikService|IngressClass|ServersTransport|ServersTransportTCP|GatewayClass|Gateway|HTTPRoute|TCPRoute|TLSRoute)$`) + acceptedK8sTypes := regexp.MustCompile(`^(Namespace|Deployment|Endpoints|Service|Ingress|IngressRoute|IngressRouteTCP|IngressRouteUDP|Middleware|MiddlewareTCP|Secret|TLSOption|TLSStore|TraefikService|IngressClass|ServersTransport|ServersTransportTCP|GatewayClass|Gateway|HTTPRoute|TCPRoute|TLSRoute|ReferenceGrant)$`) files := strings.Split(string(content), "---\n") retVal := make([]runtime.Object, 0, len(files))