Support for cross-namespace references / GatewayAPI ReferenceGrants
This commit is contained in:
parent
8b77f0c2dd
commit
9be523d772
10 changed files with 940 additions and 67 deletions
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
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++
|
||||
|
|
|
@ -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)
|
||||
|
@ -44,6 +50,7 @@ type clientMock struct {
|
|||
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
|
||||
|
|
|
@ -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: ""
|
|
@ -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: ""
|
|
@ -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: ""
|
|
@ -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: ""
|
|
@ -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 {
|
||||
certificateNamespace = string(*certificateRef.Namespace)
|
||||
}
|
||||
|
||||
if certificateNamespace != gateway.Namespace {
|
||||
referenceGrants, err := client.GetReferenceGrants(certificateNamespace)
|
||||
if err != nil {
|
||||
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",
|
||||
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 := gateway.Namespace + "/" + string(certificateRef.Name)
|
||||
configKey := certificateNamespace + "/" + string(certificateRef.Name)
|
||||
if _, tlsExists := tlsConfigs[configKey]; !tlsExists {
|
||||
tlsConf, err := getTLS(client, certificateRef.Name, gateway.Namespace)
|
||||
tlsConf, err := getTLS(client, certificateRef.Name, certificateNamespace)
|
||||
if err != nil {
|
||||
// update "ResolvedRefs" status true with "InvalidCertificateRef" reason
|
||||
listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions, metav1.Condition{
|
||||
// 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),
|
||||
listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions,
|
||||
metav1.Condition{
|
||||
Type: string(gatev1.ListenerConditionAccepted),
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: gateway.Generation,
|
||||
LastTransitionTime: metav1.Now(),
|
||||
Reason: "ListenerReady",
|
||||
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 "<nil>"
|
||||
}
|
||||
return string(*p)
|
||||
}
|
||||
|
||||
func kindToString(p *gatev1.Kind) string {
|
||||
if p == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return string(*p)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Reference in a new issue