Set Gateway HTTPRoute status
Co-authored-by: Romain <rtribotte@users.noreply.github.com>
This commit is contained in:
parent
9d8fd24730
commit
05d2c86074
6 changed files with 995 additions and 883 deletions
|
@ -195,40 +195,28 @@ func (s *K8sConformanceSuite) TestK8sGatewayAPIConformance() {
|
|||
LatestObservedGenerationSet: 5 * time.Second,
|
||||
RequiredConsecutiveSuccesses: 0,
|
||||
},
|
||||
SupportedFeatures: sets.New[ksuite.SupportedFeature]().
|
||||
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",
|
||||
"HTTPRouteListenerHostnameMatching",
|
||||
"HTTPRouteRequestHeaderModifier",
|
||||
"GatewayClassObservedGenerationBump",
|
||||
"HTTPRouteInvalidNonExistentBackendRef",
|
||||
"GatewayWithAttachedRoutes",
|
||||
"HTTPRouteCrossNamespace",
|
||||
"HTTPRouteDisallowedKind",
|
||||
"HTTPRouteInvalidReferenceGrant",
|
||||
"HTTPRouteObservedGenerationBump",
|
||||
"TLSRouteSimpleSameNamespace",
|
||||
"TLSRouteInvalidReferenceGrant",
|
||||
"HTTPRouteInvalidCrossNamespaceParentRef",
|
||||
"HTTPRouteInvalidParentRefNotMatchingSectionName",
|
||||
"GatewayModifyListeners",
|
||||
"GatewayInvalidTLSConfiguration",
|
||||
"HTTPRouteInvalidCrossNamespaceBackendRef",
|
||||
"HTTPRouteMatchingAcrossRoutes",
|
||||
"HTTPRoutePartiallyInvalidViaInvalidReferenceGrant",
|
||||
"HTTPRouteRedirectHostAndStatus",
|
||||
"HTTPRouteInvalidBackendRefUnknownKind",
|
||||
"HTTPRoutePathMatchOrder",
|
||||
"HTTPRouteSimpleSameNamespace",
|
||||
"HTTPRouteMatching",
|
||||
"HTTPRouteHeaderMatching",
|
||||
"HTTPRouteReferenceGrant",
|
||||
tests.GatewayClassObservedGenerationBump.ShortName,
|
||||
tests.GatewayWithAttachedRoutes.ShortName,
|
||||
tests.GatewayModifyListeners.ShortName,
|
||||
tests.GatewayInvalidTLSConfiguration.ShortName,
|
||||
tests.HTTPRouteHostnameIntersection.ShortName,
|
||||
tests.HTTPRouteListenerHostnameMatching.ShortName,
|
||||
tests.HTTPRouteInvalidNonExistentBackendRef.ShortName,
|
||||
tests.HTTPRouteInvalidReferenceGrant.ShortName,
|
||||
tests.HTTPRouteInvalidCrossNamespaceParentRef.ShortName,
|
||||
tests.HTTPRouteInvalidParentRefNotMatchingSectionName.ShortName,
|
||||
tests.HTTPRouteInvalidCrossNamespaceBackendRef.ShortName,
|
||||
tests.HTTPRouteMatchingAcrossRoutes.ShortName,
|
||||
tests.HTTPRoutePartiallyInvalidViaInvalidReferenceGrant.ShortName,
|
||||
tests.HTTPRouteRedirectHostAndStatus.ShortName,
|
||||
tests.HTTPRouteInvalidBackendRefUnknownKind.ShortName,
|
||||
tests.HTTPRoutePathMatchOrder.ShortName,
|
||||
tests.HTTPRouteHeaderMatching.ShortName,
|
||||
tests.HTTPRouteReferenceGrant.ShortName,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -241,10 +229,7 @@ func (s *K8sConformanceSuite) TestK8sGatewayAPIConformance() {
|
|||
Version: version.Version,
|
||||
Contact: []string{"@traefik/maintainers"},
|
||||
},
|
||||
ConformanceProfiles: sets.New[ksuite.ConformanceProfileName](
|
||||
ksuite.HTTPConformanceProfileName,
|
||||
ksuite.TLSConformanceProfileName,
|
||||
),
|
||||
ConformanceProfiles: sets.New(ksuite.HTTPConformanceProfileName),
|
||||
})
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
kerror "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
ktypes "k8s.io/apimachinery/pkg/types"
|
||||
kinformers "k8s.io/client-go/informers"
|
||||
kclientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
|
@ -51,6 +52,7 @@ type Client interface {
|
|||
GetGatewayClasses() ([]*gatev1.GatewayClass, error)
|
||||
UpdateGatewayStatus(gateway *gatev1.Gateway, gatewayStatus gatev1.GatewayStatus) error
|
||||
UpdateGatewayClassStatus(gatewayClass *gatev1.GatewayClass, condition metav1.Condition) error
|
||||
UpdateHTTPRouteStatus(ctx context.Context, gateway *gatev1.Gateway, nsName ktypes.NamespacedName, status gatev1.HTTPRouteStatus) error
|
||||
GetGateways() []*gatev1.Gateway
|
||||
GetHTTPRoutes(namespaces []string) ([]*gatev1.HTTPRoute, error)
|
||||
GetTCPRoutes(namespaces []string) ([]*gatev1alpha2.TCPRoute, error)
|
||||
|
@ -384,7 +386,7 @@ func (c *clientWrapper) GetGateways() []*gatev1.Gateway {
|
|||
var result []*gatev1.Gateway
|
||||
|
||||
for ns, factory := range c.factoriesGateway {
|
||||
gateways, err := factory.Gateway().V1().Gateways().Lister().List(labels.Everything())
|
||||
gateways, err := factory.Gateway().V1().Gateways().Lister().Gateways(ns).List(labels.Everything())
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("Failed to list Gateways in namespace %s", ns)
|
||||
continue
|
||||
|
@ -453,6 +455,46 @@ func (c *clientWrapper) UpdateGatewayStatus(gateway *gatev1.Gateway, gatewayStat
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *clientWrapper) UpdateHTTPRouteStatus(ctx context.Context, gateway *gatev1.Gateway, nsName ktypes.NamespacedName, status gatev1.HTTPRouteStatus) error {
|
||||
if !c.isWatchedNamespace(nsName.Namespace) {
|
||||
return fmt.Errorf("updating HTTPRoute status %s/%s: namespace is not within watched namespaces", nsName.Namespace, nsName.Name)
|
||||
}
|
||||
|
||||
route, err := c.factoriesGateway[c.lookupNamespace(nsName.Namespace)].Gateway().V1().HTTPRoutes().Lister().HTTPRoutes(nsName.Namespace).Get(nsName.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting HTTPRoute %s/%s: %w", nsName.Namespace, nsName.Name, err)
|
||||
}
|
||||
|
||||
var statuses []gatev1.RouteParentStatus
|
||||
for _, status := range route.Status.Parents {
|
||||
if status.ControllerName != controllerName {
|
||||
statuses = append(statuses, status)
|
||||
continue
|
||||
}
|
||||
if status.ParentRef.Namespace != nil && string(*status.ParentRef.Namespace) != gateway.Namespace {
|
||||
statuses = append(statuses, status)
|
||||
continue
|
||||
}
|
||||
if string(status.ParentRef.Name) != gateway.Name {
|
||||
statuses = append(statuses, status)
|
||||
continue
|
||||
}
|
||||
}
|
||||
statuses = append(statuses, status.Parents...)
|
||||
|
||||
route = route.DeepCopy()
|
||||
route.Status = gatev1.HTTPRouteStatus{
|
||||
RouteStatus: gatev1.RouteStatus{
|
||||
Parents: statuses,
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := c.csGateway.GatewayV1().HTTPRoutes(nsName.Namespace).UpdateStatus(ctx, route, metav1.UpdateOptions{}); err != nil {
|
||||
return fmt.Errorf("updating HTTPRoute %s/%s status: %w", nsName.Namespace, nsName.Name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func statusEquals(oldStatus, newStatus gatev1.GatewayStatus) bool {
|
||||
if len(oldStatus.Listeners) != len(newStatus.Listeners) {
|
||||
return false
|
||||
|
|
|
@ -1,254 +0,0 @@
|
|||
package gateway
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/traefik/traefik/v3/pkg/provider/kubernetes/k8s"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
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)
|
||||
|
||||
func init() {
|
||||
// required by k8s.MustParseYaml
|
||||
err := gatev1alpha2.AddToScheme(kscheme.Scheme)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = gatev1beta1.AddToScheme(kscheme.Scheme)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = gatev1.AddToScheme(kscheme.Scheme)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type clientMock struct {
|
||||
services []*corev1.Service
|
||||
secrets []*corev1.Secret
|
||||
endpoints []*corev1.Endpoints
|
||||
namespaces []*corev1.Namespace
|
||||
|
||||
apiServiceError error
|
||||
apiSecretError error
|
||||
apiEndpointsError error
|
||||
|
||||
gatewayClasses []*gatev1.GatewayClass
|
||||
gateways []*gatev1.Gateway
|
||||
httpRoutes []*gatev1.HTTPRoute
|
||||
tcpRoutes []*gatev1alpha2.TCPRoute
|
||||
tlsRoutes []*gatev1alpha2.TLSRoute
|
||||
referenceGrants []*gatev1beta1.ReferenceGrant
|
||||
|
||||
watchChan chan interface{}
|
||||
}
|
||||
|
||||
func newClientMock(paths ...string) clientMock {
|
||||
var c clientMock
|
||||
|
||||
for _, path := range paths {
|
||||
yamlContent, err := os.ReadFile(filepath.FromSlash("./fixtures/" + path))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
k8sObjects := k8s.MustParseYaml(yamlContent)
|
||||
for _, obj := range k8sObjects {
|
||||
switch o := obj.(type) {
|
||||
case *corev1.Service:
|
||||
c.services = append(c.services, o)
|
||||
case *corev1.Secret:
|
||||
c.secrets = append(c.secrets, o)
|
||||
case *corev1.Namespace:
|
||||
c.namespaces = append(c.namespaces, o)
|
||||
case *corev1.Endpoints:
|
||||
c.endpoints = append(c.endpoints, o)
|
||||
case *gatev1.GatewayClass:
|
||||
c.gatewayClasses = append(c.gatewayClasses, o)
|
||||
case *gatev1.Gateway:
|
||||
c.gateways = append(c.gateways, o)
|
||||
case *gatev1.HTTPRoute:
|
||||
c.httpRoutes = append(c.httpRoutes, o)
|
||||
case *gatev1alpha2.TCPRoute:
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c clientMock) UpdateGatewayStatus(gateway *gatev1.Gateway, gatewayStatus gatev1.GatewayStatus) error {
|
||||
for _, g := range c.gateways {
|
||||
if g.Name == gateway.Name {
|
||||
if !statusEquals(g.Status, gatewayStatus) {
|
||||
g.Status = gatewayStatus
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("cannot update gateway %v", gateway.Name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c clientMock) UpdateGatewayClassStatus(gatewayClass *gatev1.GatewayClass, condition metav1.Condition) error {
|
||||
for _, gc := range c.gatewayClasses {
|
||||
if gc.Name == gatewayClass.Name {
|
||||
for _, c := range gc.Status.Conditions {
|
||||
if c.Type == condition.Type && c.Status != condition.Status {
|
||||
c.Status = condition.Status
|
||||
c.LastTransitionTime = condition.LastTransitionTime
|
||||
c.Message = condition.Message
|
||||
c.Reason = condition.Reason
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c clientMock) UpdateGatewayStatusConditions(gateway *gatev1.Gateway, condition metav1.Condition) error {
|
||||
for _, g := range c.gatewayClasses {
|
||||
if g.Name == gateway.Name {
|
||||
for _, c := range g.Status.Conditions {
|
||||
if c.Type == condition.Type && (c.Status != condition.Status || c.Reason != condition.Reason) {
|
||||
c.Status = condition.Status
|
||||
c.LastTransitionTime = condition.LastTransitionTime
|
||||
c.Message = condition.Message
|
||||
c.Reason = condition.Reason
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c clientMock) GetGatewayClasses() ([]*gatev1.GatewayClass, error) {
|
||||
return c.gatewayClasses, nil
|
||||
}
|
||||
|
||||
func (c clientMock) GetGateways() []*gatev1.Gateway {
|
||||
return c.gateways
|
||||
}
|
||||
|
||||
func inNamespace(m metav1.ObjectMeta, s string) bool {
|
||||
return s == metav1.NamespaceAll || m.Namespace == s
|
||||
}
|
||||
|
||||
func (c clientMock) GetNamespaces(selector labels.Selector) ([]string, error) {
|
||||
var ns []string
|
||||
for _, namespace := range c.namespaces {
|
||||
if selector.Matches(labels.Set(namespace.Labels)) {
|
||||
ns = append(ns, namespace.Name)
|
||||
}
|
||||
}
|
||||
return ns, nil
|
||||
}
|
||||
|
||||
func (c clientMock) GetHTTPRoutes(namespaces []string) ([]*gatev1.HTTPRoute, error) {
|
||||
var httpRoutes []*gatev1.HTTPRoute
|
||||
for _, namespace := range namespaces {
|
||||
for _, httpRoute := range c.httpRoutes {
|
||||
if inNamespace(httpRoute.ObjectMeta, namespace) {
|
||||
httpRoutes = append(httpRoutes, httpRoute)
|
||||
}
|
||||
}
|
||||
}
|
||||
return httpRoutes, nil
|
||||
}
|
||||
|
||||
func (c clientMock) GetTCPRoutes(namespaces []string) ([]*gatev1alpha2.TCPRoute, error) {
|
||||
var tcpRoutes []*gatev1alpha2.TCPRoute
|
||||
for _, namespace := range namespaces {
|
||||
for _, tcpRoute := range c.tcpRoutes {
|
||||
if inNamespace(tcpRoute.ObjectMeta, namespace) {
|
||||
tcpRoutes = append(tcpRoutes, tcpRoute)
|
||||
}
|
||||
}
|
||||
}
|
||||
return tcpRoutes, nil
|
||||
}
|
||||
|
||||
func (c clientMock) GetTLSRoutes(namespaces []string) ([]*gatev1alpha2.TLSRoute, error) {
|
||||
var tlsRoutes []*gatev1alpha2.TLSRoute
|
||||
for _, namespace := range namespaces {
|
||||
for _, tlsRoute := range c.tlsRoutes {
|
||||
if inNamespace(tlsRoute.ObjectMeta, namespace) {
|
||||
tlsRoutes = append(tlsRoutes, 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
|
||||
}
|
||||
|
||||
for _, service := range c.services {
|
||||
if inNamespace(service.ObjectMeta, namespace) && service.Name == name {
|
||||
return service, true, nil
|
||||
}
|
||||
}
|
||||
return nil, false, c.apiServiceError
|
||||
}
|
||||
|
||||
func (c clientMock) GetEndpoints(namespace, name string) (*corev1.Endpoints, bool, error) {
|
||||
if c.apiEndpointsError != nil {
|
||||
return nil, false, c.apiEndpointsError
|
||||
}
|
||||
|
||||
for _, endpoints := range c.endpoints {
|
||||
if inNamespace(endpoints.ObjectMeta, namespace) && endpoints.Name == name {
|
||||
return endpoints, true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return &corev1.Endpoints{}, false, nil
|
||||
}
|
||||
|
||||
func (c clientMock) GetSecret(namespace, name string) (*corev1.Secret, bool, error) {
|
||||
if c.apiSecretError != nil {
|
||||
return nil, false, c.apiSecretError
|
||||
}
|
||||
|
||||
for _, secret := range c.secrets {
|
||||
if inNamespace(secret.ObjectMeta, namespace) && secret.Name == name {
|
||||
return secret, true, nil
|
||||
}
|
||||
}
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
func (c clientMock) WatchAll(namespaces []string, stopCh <-chan struct{}) (<-chan interface{}, error) {
|
||||
return c.watchChan, nil
|
||||
}
|
|
@ -17,17 +17,6 @@ data:
|
|||
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
|
||||
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: supersecret
|
||||
namespace: default
|
||||
|
||||
data:
|
||||
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
|
||||
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=
|
||||
|
||||
---
|
||||
kind: GatewayClass
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,6 +3,8 @@ package gateway
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -12,16 +14,35 @@ import (
|
|||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v3/pkg/provider"
|
||||
traefikv1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1"
|
||||
"github.com/traefik/traefik/v3/pkg/provider/kubernetes/k8s"
|
||||
"github.com/traefik/traefik/v3/pkg/tls"
|
||||
"github.com/traefik/traefik/v3/pkg/types"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||
kscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/utils/ptr"
|
||||
gatev1 "sigs.k8s.io/gateway-api/apis/v1"
|
||||
gatev1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
|
||||
gatev1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
|
||||
gatefake "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned/fake"
|
||||
)
|
||||
|
||||
var _ provider.Provider = (*Provider)(nil)
|
||||
|
||||
func init() {
|
||||
// required by k8s.MustParseYaml
|
||||
if err := gatev1.AddToScheme(kscheme.Scheme); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := gatev1beta1.AddToScheme(kscheme.Scheme); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := gatev1alpha2.AddToScheme(kscheme.Scheme); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadHTTPRoutes(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
@ -1248,10 +1269,10 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
|||
},
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Routers: map[string]*dynamic.Router{
|
||||
"default-http-app-1-my-gateway-web-4a1b73e6f83804949a37": {
|
||||
"default-http-app-1-my-gateway-web-6cf37fa71907768d925c": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "default-http-app-1-my-gateway-web-4a1b73e6f83804949a37-wrr",
|
||||
Rule: "Host(`foo.com`) && PathPrefix(`/bar`) && Header(`my-header`,`foo`) && Header(`my-header2`,`bar`)",
|
||||
Service: "default-http-app-1-my-gateway-web-6cf37fa71907768d925c-wrr",
|
||||
Rule: "Host(`foo.com`) && (Path(`/bar`) || PathPrefix(`/bar/`)) && Header(`my-header`,`foo`) && Header(`my-header2`,`bar`)",
|
||||
RuleSyntax: "v3",
|
||||
},
|
||||
"default-http-app-1-my-gateway-web-aaba0f24fd26e1ca2276": {
|
||||
|
@ -1263,7 +1284,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
|||
},
|
||||
Middlewares: map[string]*dynamic.Middleware{},
|
||||
Services: map[string]*dynamic.Service{
|
||||
"default-http-app-1-my-gateway-web-4a1b73e6f83804949a37-wrr": {
|
||||
"default-http-app-1-my-gateway-web-6cf37fa71907768d925c-wrr": {
|
||||
Weighted: &dynamic.WeightedRoundRobin{
|
||||
Services: []dynamic.WRRService{
|
||||
{
|
||||
|
@ -1733,9 +1754,27 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
p := Provider{EntryPoints: test.entryPoints, ExperimentalChannel: test.experimentalChannel}
|
||||
p := Provider{
|
||||
EntryPoints: test.entryPoints,
|
||||
ExperimentalChannel: test.experimentalChannel,
|
||||
}
|
||||
|
||||
conf := p.loadConfigurationFromGateway(context.Background(), newClientMock(test.paths...))
|
||||
k8sObjects, gwObjects := readResources(t, test.paths)
|
||||
|
||||
kubeClient := kubefake.NewSimpleClientset(k8sObjects...)
|
||||
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
|
||||
|
||||
client := newClientImpl(kubeClient, gwClient)
|
||||
|
||||
eventCh, err := client.WatchAll(nil, make(chan struct{}))
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
|
||||
// just wait for the first event
|
||||
<-eventCh
|
||||
}
|
||||
|
||||
conf := p.loadConfigurationFromGateway(context.Background(), client)
|
||||
assert.Equal(t, test.expected, conf)
|
||||
})
|
||||
}
|
||||
|
@ -1992,12 +2031,28 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) {
|
|||
}
|
||||
|
||||
p := Provider{EntryPoints: test.entryPoints}
|
||||
|
||||
k8sObjects, gwObjects := readResources(t, test.paths)
|
||||
|
||||
kubeClient := kubefake.NewSimpleClientset(k8sObjects...)
|
||||
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
|
||||
|
||||
client := newClientImpl(kubeClient, gwClient)
|
||||
|
||||
eventCh, err := client.WatchAll(nil, make(chan struct{}))
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
|
||||
// just wait for the first event
|
||||
<-eventCh
|
||||
}
|
||||
|
||||
for group, kindFuncs := range test.groupKindBackendFuncs {
|
||||
for kind, backendFunc := range kindFuncs {
|
||||
p.RegisterBackendFuncs(group, kind, backendFunc)
|
||||
}
|
||||
}
|
||||
conf := p.loadConfigurationFromGateway(context.Background(), newClientMock(test.paths...))
|
||||
conf := p.loadConfigurationFromGateway(context.Background(), client)
|
||||
assert.Equal(t, test.expected, conf)
|
||||
})
|
||||
}
|
||||
|
@ -2208,12 +2263,28 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) {
|
|||
}
|
||||
|
||||
p := Provider{EntryPoints: test.entryPoints}
|
||||
|
||||
k8sObjects, gwObjects := readResources(t, []string{"services.yml", "httproute/filter_extension_ref.yml"})
|
||||
|
||||
kubeClient := kubefake.NewSimpleClientset(k8sObjects...)
|
||||
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
|
||||
|
||||
client := newClientImpl(kubeClient, gwClient)
|
||||
|
||||
eventCh, err := client.WatchAll(nil, make(chan struct{}))
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
|
||||
// just wait for the first event
|
||||
<-eventCh
|
||||
}
|
||||
|
||||
for group, kindFuncs := range test.groupKindFilterFuncs {
|
||||
for kind, filterFunc := range kindFuncs {
|
||||
p.RegisterFilterFuncs(group, kind, filterFunc)
|
||||
}
|
||||
}
|
||||
conf := p.loadConfigurationFromGateway(context.Background(), newClientMock([]string{"services.yml", "httproute/filter_extension_ref.yml"}...))
|
||||
conf := p.loadConfigurationFromGateway(context.Background(), client)
|
||||
assert.Equal(t, test.expected, conf)
|
||||
})
|
||||
}
|
||||
|
@ -2971,8 +3042,28 @@ func TestLoadTCPRoutes(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
p := Provider{EntryPoints: test.entryPoints, ExperimentalChannel: true}
|
||||
conf := p.loadConfigurationFromGateway(context.Background(), newClientMock(test.paths...))
|
||||
p := Provider{
|
||||
EntryPoints: test.entryPoints,
|
||||
ExperimentalChannel: true,
|
||||
}
|
||||
|
||||
k8sObjects, gwObjects := readResources(t, test.paths)
|
||||
|
||||
kubeClient := kubefake.NewSimpleClientset(k8sObjects...)
|
||||
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
|
||||
|
||||
client := newClientImpl(kubeClient, gwClient)
|
||||
client.experimentalChannel = true
|
||||
|
||||
eventCh, err := client.WatchAll(nil, make(chan struct{}))
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
|
||||
// just wait for the first event
|
||||
<-eventCh
|
||||
}
|
||||
|
||||
conf := p.loadConfigurationFromGateway(context.Background(), client)
|
||||
assert.Equal(t, test.expected, conf)
|
||||
})
|
||||
}
|
||||
|
@ -4099,8 +4190,28 @@ func TestLoadTLSRoutes(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
p := Provider{EntryPoints: test.entryPoints, ExperimentalChannel: true}
|
||||
conf := p.loadConfigurationFromGateway(context.Background(), newClientMock(test.paths...))
|
||||
p := Provider{
|
||||
EntryPoints: test.entryPoints,
|
||||
ExperimentalChannel: true,
|
||||
}
|
||||
|
||||
k8sObjects, gwObjects := readResources(t, test.paths)
|
||||
|
||||
kubeClient := kubefake.NewSimpleClientset(k8sObjects...)
|
||||
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
|
||||
|
||||
client := newClientImpl(kubeClient, gwClient)
|
||||
client.experimentalChannel = true
|
||||
|
||||
eventCh, err := client.WatchAll(nil, make(chan struct{}))
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
|
||||
// just wait for the first event
|
||||
<-eventCh
|
||||
}
|
||||
|
||||
conf := p.loadConfigurationFromGateway(context.Background(), client)
|
||||
assert.Equal(t, test.expected, conf)
|
||||
})
|
||||
}
|
||||
|
@ -5112,8 +5223,28 @@ func TestLoadMixedRoutes(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
p := Provider{EntryPoints: test.entryPoints, ExperimentalChannel: test.experimentalChannel}
|
||||
conf := p.loadConfigurationFromGateway(context.Background(), newClientMock(test.paths...))
|
||||
p := Provider{
|
||||
EntryPoints: test.entryPoints,
|
||||
ExperimentalChannel: test.experimentalChannel,
|
||||
}
|
||||
|
||||
k8sObjects, gwObjects := readResources(t, test.paths)
|
||||
|
||||
kubeClient := kubefake.NewSimpleClientset(k8sObjects...)
|
||||
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
|
||||
|
||||
client := newClientImpl(kubeClient, gwClient)
|
||||
client.experimentalChannel = test.experimentalChannel
|
||||
|
||||
eventCh, err := client.WatchAll(nil, make(chan struct{}))
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
|
||||
// just wait for the first event
|
||||
<-eventCh
|
||||
}
|
||||
|
||||
conf := p.loadConfigurationFromGateway(context.Background(), client)
|
||||
assert.Equal(t, test.expected, conf)
|
||||
})
|
||||
}
|
||||
|
@ -5303,8 +5434,28 @@ func TestLoadRoutesWithReferenceGrants(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
p := Provider{EntryPoints: test.entryPoints, ExperimentalChannel: test.experimentalChannel}
|
||||
conf := p.loadConfigurationFromGateway(context.Background(), newClientMock(test.paths...))
|
||||
p := Provider{
|
||||
EntryPoints: test.entryPoints,
|
||||
ExperimentalChannel: test.experimentalChannel,
|
||||
}
|
||||
|
||||
k8sObjects, gwObjects := readResources(t, test.paths)
|
||||
|
||||
kubeClient := kubefake.NewSimpleClientset(k8sObjects...)
|
||||
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
|
||||
|
||||
client := newClientImpl(kubeClient, gwClient)
|
||||
client.experimentalChannel = test.experimentalChannel
|
||||
|
||||
eventCh, err := client.WatchAll(nil, make(chan struct{}))
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
|
||||
// just wait for the first event
|
||||
<-eventCh
|
||||
}
|
||||
|
||||
conf := p.loadConfigurationFromGateway(context.Background(), client)
|
||||
assert.Equal(t, test.expected, conf)
|
||||
})
|
||||
}
|
||||
|
@ -5731,7 +5882,8 @@ func Test_shouldAttach(t *testing.T) {
|
|||
listener gatev1.Listener
|
||||
routeNamespace string
|
||||
routeSpec gatev1.CommonRouteSpec
|
||||
expectedAttach bool
|
||||
wantAttach bool
|
||||
wantParentRef gatev1.ParentReference
|
||||
}{
|
||||
{
|
||||
desc: "No ParentRefs",
|
||||
|
@ -5748,7 +5900,7 @@ func Test_shouldAttach(t *testing.T) {
|
|||
routeSpec: gatev1.CommonRouteSpec{
|
||||
ParentRefs: nil,
|
||||
},
|
||||
expectedAttach: false,
|
||||
wantAttach: false,
|
||||
},
|
||||
{
|
||||
desc: "Unsupported Kind",
|
||||
|
@ -5773,7 +5925,7 @@ func Test_shouldAttach(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
expectedAttach: false,
|
||||
wantAttach: false,
|
||||
},
|
||||
{
|
||||
desc: "Unsupported Group",
|
||||
|
@ -5798,7 +5950,7 @@ func Test_shouldAttach(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
expectedAttach: false,
|
||||
wantAttach: false,
|
||||
},
|
||||
{
|
||||
desc: "Kind is nil",
|
||||
|
@ -5822,7 +5974,7 @@ func Test_shouldAttach(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
expectedAttach: false,
|
||||
wantAttach: false,
|
||||
},
|
||||
{
|
||||
desc: "Group is nil",
|
||||
|
@ -5846,7 +5998,7 @@ func Test_shouldAttach(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
expectedAttach: false,
|
||||
wantAttach: false,
|
||||
},
|
||||
{
|
||||
desc: "SectionName does not match a listener desc",
|
||||
|
@ -5871,7 +6023,7 @@ func Test_shouldAttach(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
expectedAttach: false,
|
||||
wantAttach: false,
|
||||
},
|
||||
{
|
||||
desc: "Namespace does not match the Gateway namespace",
|
||||
|
@ -5896,7 +6048,7 @@ func Test_shouldAttach(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
expectedAttach: false,
|
||||
wantAttach: false,
|
||||
},
|
||||
{
|
||||
desc: "Route namespace does not match the Gateway namespace",
|
||||
|
@ -5920,7 +6072,7 @@ func Test_shouldAttach(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
expectedAttach: false,
|
||||
wantAttach: false,
|
||||
},
|
||||
{
|
||||
desc: "Unsupported Kind",
|
||||
|
@ -5945,7 +6097,7 @@ func Test_shouldAttach(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
expectedAttach: false,
|
||||
wantAttach: false,
|
||||
},
|
||||
{
|
||||
desc: "Route namespace matches the Gateway namespace",
|
||||
|
@ -5969,7 +6121,13 @@ func Test_shouldAttach(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
expectedAttach: true,
|
||||
wantAttach: true,
|
||||
wantParentRef: gatev1.ParentReference{
|
||||
SectionName: sectionNamePtr("foo"),
|
||||
Name: "gateway",
|
||||
Kind: kindPtr("Gateway"),
|
||||
Group: groupPtr(gatev1.GroupName),
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Namespace matches the Gateway namespace",
|
||||
|
@ -5994,7 +6152,14 @@ func Test_shouldAttach(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
expectedAttach: true,
|
||||
wantAttach: true,
|
||||
wantParentRef: gatev1.ParentReference{
|
||||
SectionName: sectionNamePtr("foo"),
|
||||
Name: "gateway",
|
||||
Namespace: namespacePtr("default"),
|
||||
Kind: kindPtr("Gateway"),
|
||||
Group: groupPtr(gatev1.GroupName),
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Only one ParentRef matches the Gateway",
|
||||
|
@ -6024,7 +6189,13 @@ func Test_shouldAttach(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
expectedAttach: true,
|
||||
wantAttach: true,
|
||||
wantParentRef: gatev1.ParentReference{
|
||||
Name: "gateway",
|
||||
Namespace: namespacePtr("default"),
|
||||
Kind: kindPtr("Gateway"),
|
||||
Group: groupPtr(gatev1.GroupName),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -6032,8 +6203,9 @@ func Test_shouldAttach(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := shouldAttach(test.gateway, test.listener, test.routeNamespace, test.routeSpec)
|
||||
assert.Equal(t, test.expectedAttach, got)
|
||||
gotParentRef, gotAttach := shouldAttach(test.gateway, test.listener, test.routeNamespace, test.routeSpec)
|
||||
assert.Equal(t, test.wantAttach, gotAttach)
|
||||
assert.Equal(t, test.wantParentRef, gotParentRef)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -6627,7 +6799,22 @@ func Test_gatewayAddresses(t *testing.T) {
|
|||
|
||||
p := Provider{StatusAddress: test.statusAddress}
|
||||
|
||||
got, err := p.gatewayAddresses(newClientMock(test.paths...))
|
||||
k8sObjects, gwObjects := readResources(t, test.paths)
|
||||
|
||||
kubeClient := kubefake.NewSimpleClientset(k8sObjects...)
|
||||
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
|
||||
|
||||
client := newClientImpl(kubeClient, gwClient)
|
||||
|
||||
eventCh, err := client.WatchAll(nil, make(chan struct{}))
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
|
||||
// just wait for the first event
|
||||
<-eventCh
|
||||
}
|
||||
|
||||
got, err := p.gatewayAddresses(client)
|
||||
test.wantErr(t, err)
|
||||
|
||||
assert.Equal(t, test.want, got)
|
||||
|
@ -6662,3 +6849,46 @@ func headerMatchTypePtr(h gatev1.HeaderMatchType) *gatev1.HeaderMatchType { retu
|
|||
func objectNamePtr(objectName gatev1.ObjectName) *gatev1.ObjectName {
|
||||
return &objectName
|
||||
}
|
||||
|
||||
// We cannot use the gateway-api fake.NewSimpleClientset due to Gateway being pluralized as "gatewaies" instead of "gateways".
|
||||
func newGatewaySimpleClientSet(t *testing.T, objects ...runtime.Object) *gatefake.Clientset {
|
||||
t.Helper()
|
||||
|
||||
client := gatefake.NewSimpleClientset(objects...)
|
||||
for _, object := range objects {
|
||||
gateway, ok := object.(*gatev1.Gateway)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err := client.GatewayV1().Gateways(gateway.Namespace).Create(context.Background(), gateway, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
func readResources(t *testing.T, paths []string) ([]runtime.Object, []runtime.Object) {
|
||||
t.Helper()
|
||||
|
||||
var k8sObjects []runtime.Object
|
||||
var gwObjects []runtime.Object
|
||||
for _, path := range paths {
|
||||
yamlContent, err := os.ReadFile(filepath.FromSlash("./fixtures/" + path))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
objects := k8s.MustParseYaml(yamlContent)
|
||||
for _, obj := range objects {
|
||||
switch obj.GetObjectKind().GroupVersionKind().Group {
|
||||
case "gateway.networking.k8s.io":
|
||||
gwObjects = append(gwObjects, obj)
|
||||
default:
|
||||
k8sObjects = append(k8sObjects, obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return k8sObjects, gwObjects
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue