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,
|
RequiredConsecutiveSuccesses: 0,
|
||||||
},
|
},
|
||||||
SupportedFeatures: sets.New[ksuite.SupportedFeature]().
|
SupportedFeatures: sets.New[ksuite.SupportedFeature]().
|
||||||
Insert(ksuite.GatewayCoreFeatures.UnsortedList()...),
|
Insert(ksuite.GatewayCoreFeatures.UnsortedList()...).
|
||||||
|
Insert(ksuite.ReferenceGrantCoreFeatures.UnsortedList()...),
|
||||||
EnableAllSupportedFeatures: false,
|
EnableAllSupportedFeatures: false,
|
||||||
RunTest: *k8sConformanceRunTest,
|
RunTest: *k8sConformanceRunTest,
|
||||||
// Until the feature are all supported, following tests are skipped.
|
// Until the feature are all supported, following tests are skipped.
|
||||||
SkipTests: []string{
|
SkipTests: []string{
|
||||||
"HTTPExactPathMatching",
|
"HTTPExactPathMatching",
|
||||||
"HTTPRouteHostnameIntersection",
|
"HTTPRouteHostnameIntersection",
|
||||||
"GatewaySecretReferenceGrantAllInNamespace",
|
|
||||||
"HTTPRouteListenerHostnameMatching",
|
"HTTPRouteListenerHostnameMatching",
|
||||||
"HTTPRouteRequestHeaderModifier",
|
"HTTPRouteRequestHeaderModifier",
|
||||||
"GatewaySecretInvalidReferenceGrant",
|
|
||||||
"GatewayClassObservedGenerationBump",
|
"GatewayClassObservedGenerationBump",
|
||||||
"HTTPRouteInvalidNonExistentBackendRef",
|
"HTTPRouteInvalidNonExistentBackendRef",
|
||||||
"GatewayWithAttachedRoutes",
|
"GatewayWithAttachedRoutes",
|
||||||
|
@ -171,14 +170,11 @@ func (s *K8sConformanceSuite) TestK8sGatewayAPIConformance() {
|
||||||
"HTTPRouteDisallowedKind",
|
"HTTPRouteDisallowedKind",
|
||||||
"HTTPRouteInvalidReferenceGrant",
|
"HTTPRouteInvalidReferenceGrant",
|
||||||
"HTTPRouteObservedGenerationBump",
|
"HTTPRouteObservedGenerationBump",
|
||||||
"GatewayInvalidRouteKind",
|
|
||||||
"TLSRouteSimpleSameNamespace",
|
"TLSRouteSimpleSameNamespace",
|
||||||
"TLSRouteInvalidReferenceGrant",
|
"TLSRouteInvalidReferenceGrant",
|
||||||
"HTTPRouteInvalidCrossNamespaceParentRef",
|
"HTTPRouteInvalidCrossNamespaceParentRef",
|
||||||
"HTTPRouteInvalidParentRefNotMatchingSectionName",
|
"HTTPRouteInvalidParentRefNotMatchingSectionName",
|
||||||
"GatewaySecretReferenceGrantSpecific",
|
|
||||||
"GatewayModifyListeners",
|
"GatewayModifyListeners",
|
||||||
"GatewaySecretMissingReferenceGrant",
|
|
||||||
"GatewayInvalidTLSConfiguration",
|
"GatewayInvalidTLSConfiguration",
|
||||||
"HTTPRouteInvalidCrossNamespaceBackendRef",
|
"HTTPRouteInvalidCrossNamespaceBackendRef",
|
||||||
"HTTPRouteMatchingAcrossRoutes",
|
"HTTPRouteMatchingAcrossRoutes",
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
gatev1 "sigs.k8s.io/gateway-api/apis/v1"
|
gatev1 "sigs.k8s.io/gateway-api/apis/v1"
|
||||||
gatev1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
|
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"
|
gateclientset "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned"
|
||||||
gateinformers "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions"
|
gateinformers "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions"
|
||||||
)
|
)
|
||||||
|
@ -34,14 +35,8 @@ func (reh *resourceEventHandler) OnAdd(obj interface{}, isInInitialList bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (reh *resourceEventHandler) OnUpdate(oldObj, newObj interface{}) {
|
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{}) {
|
func (reh *resourceEventHandler) OnDelete(obj interface{}) {
|
||||||
eventHandlerFunc(reh.ev, obj)
|
eventHandlerFunc(reh.ev, obj)
|
||||||
|
@ -59,6 +54,7 @@ type Client interface {
|
||||||
GetHTTPRoutes(namespaces []string) ([]*gatev1.HTTPRoute, error)
|
GetHTTPRoutes(namespaces []string) ([]*gatev1.HTTPRoute, error)
|
||||||
GetTCPRoutes(namespaces []string) ([]*gatev1alpha2.TCPRoute, error)
|
GetTCPRoutes(namespaces []string) ([]*gatev1alpha2.TCPRoute, error)
|
||||||
GetTLSRoutes(namespaces []string) ([]*gatev1alpha2.TLSRoute, error)
|
GetTLSRoutes(namespaces []string) ([]*gatev1alpha2.TLSRoute, error)
|
||||||
|
GetReferenceGrants(namespace string) ([]*gatev1beta1.ReferenceGrant, error)
|
||||||
GetService(namespace, name string) (*corev1.Service, bool, error)
|
GetService(namespace, name string) (*corev1.Service, bool, error)
|
||||||
GetSecret(namespace, name string) (*corev1.Secret, bool, error)
|
GetSecret(namespace, name string) (*corev1.Secret, bool, error)
|
||||||
GetEndpoints(namespace, name string) (*corev1.Endpoints, 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
|
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 {
|
for _, ns := range namespaces {
|
||||||
factoryGateway := gateinformers.NewSharedInformerFactoryWithOptions(c.csGateway, resyncPeriod, gateinformers.WithNamespace(ns))
|
factoryGateway := gateinformers.NewSharedInformerFactoryWithOptions(c.csGateway, resyncPeriod, gateinformers.WithNamespace(ns))
|
||||||
_, err = factoryGateway.Gateway().V1().Gateways().Informer().AddEventHandler(eventHandler)
|
_, err = factoryGateway.Gateway().V1().Gateways().Informer().AddEventHandler(eventHandler)
|
||||||
|
@ -210,6 +203,10 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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))
|
factoryKube := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns))
|
||||||
_, err = factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler)
|
_, err = factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler)
|
||||||
|
@ -363,6 +360,21 @@ func (c *clientWrapper) GetTLSRoutes(namespaces []string) ([]*gatev1alpha2.TLSRo
|
||||||
return tlsRoutes, nil
|
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 {
|
func (c *clientWrapper) GetGateways() []*gatev1.Gateway {
|
||||||
var result []*gatev1.Gateway
|
var result []*gatev1.Gateway
|
||||||
|
|
||||||
|
@ -388,7 +400,7 @@ func (c *clientWrapper) UpdateGatewayClassStatus(gatewayClass *gatev1.GatewayCla
|
||||||
var newConditions []metav1.Condition
|
var newConditions []metav1.Condition
|
||||||
for _, cond := range gc.Status.Conditions {
|
for _, cond := range gc.Status.Conditions {
|
||||||
// No update for identical condition.
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,7 +482,7 @@ func conditionsEquals(conditionsA, conditionsB []metav1.Condition) bool {
|
||||||
for _, conditionA := range conditionsA {
|
for _, conditionA := range conditionsA {
|
||||||
for _, conditionB := range conditionsB {
|
for _, conditionB := range conditionsB {
|
||||||
if conditionA.Type == conditionB.Type {
|
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
|
return false
|
||||||
}
|
}
|
||||||
conditionMatches++
|
conditionMatches++
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
kscheme "k8s.io/client-go/kubernetes/scheme"
|
kscheme "k8s.io/client-go/kubernetes/scheme"
|
||||||
gatev1 "sigs.k8s.io/gateway-api/apis/v1"
|
gatev1 "sigs.k8s.io/gateway-api/apis/v1"
|
||||||
gatev1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
|
gatev1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
|
||||||
|
gatev1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Client = (*clientMock)(nil)
|
var _ Client = (*clientMock)(nil)
|
||||||
|
@ -23,6 +24,11 @@ func init() {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = gatev1beta1.AddToScheme(kscheme.Scheme)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
err = gatev1.AddToScheme(kscheme.Scheme)
|
err = gatev1.AddToScheme(kscheme.Scheme)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -44,6 +50,7 @@ type clientMock struct {
|
||||||
httpRoutes []*gatev1.HTTPRoute
|
httpRoutes []*gatev1.HTTPRoute
|
||||||
tcpRoutes []*gatev1alpha2.TCPRoute
|
tcpRoutes []*gatev1alpha2.TCPRoute
|
||||||
tlsRoutes []*gatev1alpha2.TLSRoute
|
tlsRoutes []*gatev1alpha2.TLSRoute
|
||||||
|
referenceGrants []*gatev1beta1.ReferenceGrant
|
||||||
|
|
||||||
watchChan chan interface{}
|
watchChan chan interface{}
|
||||||
}
|
}
|
||||||
|
@ -78,6 +85,8 @@ func newClientMock(paths ...string) clientMock {
|
||||||
c.tcpRoutes = append(c.tcpRoutes, o)
|
c.tcpRoutes = append(c.tcpRoutes, o)
|
||||||
case *gatev1alpha2.TLSRoute:
|
case *gatev1alpha2.TLSRoute:
|
||||||
c.tlsRoutes = append(c.tlsRoutes, o)
|
c.tlsRoutes = append(c.tlsRoutes, o)
|
||||||
|
case *gatev1beta1.ReferenceGrant:
|
||||||
|
c.referenceGrants = append(c.referenceGrants, o)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("Unknown runtime object %+v %T", o, o))
|
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
|
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) {
|
func (c clientMock) GetService(namespace, name string) (*corev1.Service, bool, error) {
|
||||||
if c.apiServiceError != nil {
|
if c.apiServiceError != nil {
|
||||||
return nil, false, c.apiServiceError
|
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/ptr"
|
||||||
"k8s.io/utils/strings/slices"
|
"k8s.io/utils/strings/slices"
|
||||||
gatev1 "sigs.k8s.io/gateway-api/apis/v1"
|
gatev1 "sigs.k8s.io/gateway-api/apis/v1"
|
||||||
|
gatev1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
providerName = "kubernetesgateway"
|
providerName = "kubernetesgateway"
|
||||||
|
|
||||||
|
groupCore = "core"
|
||||||
|
|
||||||
kindGateway = "Gateway"
|
kindGateway = "Gateway"
|
||||||
kindTraefikService = "TraefikService"
|
kindTraefikService = "TraefikService"
|
||||||
kindHTTPRoute = "HTTPRoute"
|
kindHTTPRoute = "HTTPRoute"
|
||||||
|
@ -348,6 +351,7 @@ func (p *Provider) fillGatewayConf(ctx context.Context, client Client, gateway *
|
||||||
Name: listener.Name,
|
Name: listener.Name,
|
||||||
SupportedKinds: []gatev1.RouteGroupKind{},
|
SupportedKinds: []gatev1.RouteGroupKind{},
|
||||||
Conditions: []metav1.Condition{},
|
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)
|
supportedKinds, conditions := supportedRouteKinds(listener.Protocol)
|
||||||
|
@ -356,9 +360,8 @@ func (p *Provider) fillGatewayConf(ctx context.Context, client Client, gateway *
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
listenerStatuses[i].SupportedKinds = supportedKinds
|
|
||||||
|
|
||||||
routeKinds, conditions := getAllowedRouteKinds(gateway, listener, supportedKinds)
|
routeKinds, conditions := getAllowedRouteKinds(gateway, listener, supportedKinds)
|
||||||
|
listenerStatuses[i].SupportedKinds = routeKinds
|
||||||
if len(conditions) > 0 {
|
if len(conditions) > 0 {
|
||||||
listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions, conditions...)
|
listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions, conditions...)
|
||||||
continue
|
continue
|
||||||
|
@ -474,7 +477,7 @@ func (p *Provider) fillGatewayConf(ctx context.Context, client Client, gateway *
|
||||||
certificateRef := listener.TLS.CertificateRefs[0]
|
certificateRef := listener.TLS.CertificateRefs[0]
|
||||||
|
|
||||||
if certificateRef.Kind == nil || *certificateRef.Kind != "Secret" ||
|
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
|
// update "ResolvedRefs" status true with "InvalidCertificateRef" reason
|
||||||
listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions, metav1.Condition{
|
listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions, metav1.Condition{
|
||||||
Type: string(gatev1.ListenerConditionResolvedRefs),
|
Type: string(gatev1.ListenerConditionResolvedRefs),
|
||||||
|
@ -482,43 +485,74 @@ func (p *Provider) fillGatewayConf(ctx context.Context, client Client, gateway *
|
||||||
ObservedGeneration: gateway.Generation,
|
ObservedGeneration: gateway.Generation,
|
||||||
LastTransitionTime: metav1.Now(),
|
LastTransitionTime: metav1.Now(),
|
||||||
Reason: string(gatev1.ListenerReasonInvalidCertificateRef),
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Support ReferencePolicy to support cross namespace references.
|
certificateNamespace := gateway.Namespace
|
||||||
if certificateRef.Namespace != nil && string(*certificateRef.Namespace) != 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{
|
listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions, metav1.Condition{
|
||||||
Type: string(gatev1.ListenerConditionResolvedRefs),
|
Type: string(gatev1.ListenerConditionResolvedRefs),
|
||||||
Status: metav1.ConditionFalse,
|
Status: metav1.ConditionFalse,
|
||||||
ObservedGeneration: gateway.Generation,
|
ObservedGeneration: gateway.Generation,
|
||||||
LastTransitionTime: metav1.Now(),
|
LastTransitionTime: metav1.Now(),
|
||||||
Reason: string(gatev1.ListenerReasonInvalidCertificateRef),
|
Reason: string(gatev1.ListenerReasonRefNotPermitted),
|
||||||
Message: "Cross namespace secrets are not supported",
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
configKey := gateway.Namespace + "/" + string(certificateRef.Name)
|
configKey := certificateNamespace + "/" + string(certificateRef.Name)
|
||||||
if _, tlsExists := tlsConfigs[configKey]; !tlsExists {
|
if _, tlsExists := tlsConfigs[configKey]; !tlsExists {
|
||||||
tlsConf, err := getTLS(client, certificateRef.Name, gateway.Namespace)
|
tlsConf, err := getTLS(client, certificateRef.Name, certificateNamespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// update "ResolvedRefs" status true with "InvalidCertificateRef" reason
|
// update "ResolvedRefs" status false with "InvalidCertificateRef" reason
|
||||||
listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions, metav1.Condition{
|
// update "Programmed" status false with "Invalid" reason
|
||||||
|
listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions,
|
||||||
|
metav1.Condition{
|
||||||
Type: string(gatev1.ListenerConditionResolvedRefs),
|
Type: string(gatev1.ListenerConditionResolvedRefs),
|
||||||
Status: metav1.ConditionFalse,
|
Status: metav1.ConditionFalse,
|
||||||
ObservedGeneration: gateway.Generation,
|
ObservedGeneration: gateway.Generation,
|
||||||
LastTransitionTime: metav1.Now(),
|
LastTransitionTime: metav1.Now(),
|
||||||
Reason: string(gatev1.ListenerReasonInvalidCertificateRef),
|
Reason: string(gatev1.ListenerReasonInvalidCertificateRef),
|
||||||
Message: fmt.Sprintf("Error while retrieving certificate: %v", err),
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsConfigs[configKey] = tlsConf
|
tlsConfigs[configKey] = tlsConf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -548,15 +582,32 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listenerStatuses [
|
||||||
var result error
|
var result error
|
||||||
for i, listener := range listenerStatuses {
|
for i, listener := range listenerStatuses {
|
||||||
if len(listener.Conditions) == 0 {
|
if len(listener.Conditions) == 0 {
|
||||||
// GatewayConditionReady "Ready", GatewayConditionReason "ListenerReady"
|
listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions,
|
||||||
listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions, metav1.Condition{
|
metav1.Condition{
|
||||||
Type: string(gatev1.ListenerReasonAccepted),
|
Type: string(gatev1.ListenerConditionAccepted),
|
||||||
Status: metav1.ConditionTrue,
|
Status: metav1.ConditionTrue,
|
||||||
ObservedGeneration: gateway.Generation,
|
ObservedGeneration: gateway.Generation,
|
||||||
LastTransitionTime: metav1.Now(),
|
LastTransitionTime: metav1.Now(),
|
||||||
Reason: "ListenerReady",
|
Reason: string(gatev1.ListenerReasonAccepted),
|
||||||
Message: "No error found",
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
@ -565,6 +616,7 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listenerStatuses [
|
||||||
result = multierror.Append(result, errors.New(condition.Message))
|
result = multierror.Append(result, errors.New(condition.Message))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
gatewayStatus.Listeners = listenerStatuses
|
||||||
|
|
||||||
if result != nil {
|
if result != nil {
|
||||||
// GatewayConditionReady "Ready", GatewayConditionReason "ListenersNotValid"
|
// GatewayConditionReady "Ready", GatewayConditionReason "ListenersNotValid"
|
||||||
|
@ -580,8 +632,6 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listenerStatuses [
|
||||||
return gatewayStatus, result
|
return gatewayStatus, result
|
||||||
}
|
}
|
||||||
|
|
||||||
gatewayStatus.Listeners = listenerStatuses
|
|
||||||
|
|
||||||
gatewayStatus.Conditions = append(gatewayStatus.Conditions,
|
gatewayStatus.Conditions = append(gatewayStatus.Conditions,
|
||||||
// update "Accepted" status with "Accepted" reason
|
// update "Accepted" status with "Accepted" reason
|
||||||
metav1.Condition{
|
metav1.Condition{
|
||||||
|
@ -656,7 +706,7 @@ func getAllowedRouteKinds(gateway *gatev1.Gateway, listener gatev1.Listener, sup
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
routeKinds []gatev1.RouteGroupKind
|
routeKinds = []gatev1.RouteGroupKind{}
|
||||||
conditions []metav1.Condition
|
conditions []metav1.Condition
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -672,12 +722,12 @@ func getAllowedRouteKinds(gateway *gatev1.Gateway, listener gatev1.Listener, sup
|
||||||
|
|
||||||
if !isSupported {
|
if !isSupported {
|
||||||
conditions = append(conditions, metav1.Condition{
|
conditions = append(conditions, metav1.Condition{
|
||||||
Type: string(gatev1.ListenerConditionAccepted),
|
Type: string(gatev1.ListenerConditionResolvedRefs),
|
||||||
Status: metav1.ConditionTrue,
|
Status: metav1.ConditionFalse,
|
||||||
ObservedGeneration: gateway.Generation,
|
ObservedGeneration: gateway.Generation,
|
||||||
LastTransitionTime: metav1.Now(),
|
LastTransitionTime: metav1.Now(),
|
||||||
Reason: string(gatev1.ListenerReasonInvalidRouteKinds),
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
@ -712,7 +762,7 @@ func (p *Provider) gatewayHTTPRouteToHTTPConf(ctx context.Context, ep string, li
|
||||||
|
|
||||||
routes, err := client.GetHTTPRoutes(namespaces)
|
routes, err := client.GetHTTPRoutes(namespaces)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// update "ResolvedRefs" status true with "InvalidRoutesRef" reason
|
// update "ResolvedRefs" status true with "RefNotPermitted" reason
|
||||||
return []metav1.Condition{{
|
return []metav1.Condition{{
|
||||||
Type: string(gatev1.ListenerConditionResolvedRefs),
|
Type: string(gatev1.ListenerConditionResolvedRefs),
|
||||||
Status: metav1.ConditionFalse,
|
Status: metav1.ConditionFalse,
|
||||||
|
@ -757,7 +807,7 @@ func (p *Provider) gatewayHTTPRouteToHTTPConf(ctx context.Context, ep string, li
|
||||||
for _, routeRule := range route.Spec.Rules {
|
for _, routeRule := range route.Spec.Rules {
|
||||||
rule, err := extractRule(routeRule, hostRule)
|
rule, err := extractRule(routeRule, hostRule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// update "ResolvedRefs" status true with "DroppedRoutes" reason
|
// update "ResolvedRefs" status true with "UnsupportedPathOrHeaderType" reason
|
||||||
conditions = append(conditions, metav1.Condition{
|
conditions = append(conditions, metav1.Condition{
|
||||||
Type: string(gatev1.ListenerConditionResolvedRefs),
|
Type: string(gatev1.ListenerConditionResolvedRefs),
|
||||||
Status: metav1.ConditionFalse,
|
Status: metav1.ConditionFalse,
|
||||||
|
@ -1048,7 +1098,7 @@ func gatewayTLSRouteToTCPConf(ctx context.Context, ep string, listener gatev1.Li
|
||||||
Type: string(gatev1.GatewayClassConditionStatusAccepted),
|
Type: string(gatev1.GatewayClassConditionStatusAccepted),
|
||||||
Status: metav1.ConditionFalse,
|
Status: metav1.ConditionFalse,
|
||||||
ObservedGeneration: gateway.Generation,
|
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),
|
Message: fmt.Sprintf("No hostname match between listener: %v and route: %v", listener.Hostname, route.Spec.Hostnames),
|
||||||
LastTransitionTime: metav1.Now(),
|
LastTransitionTime: metav1.Now(),
|
||||||
})
|
})
|
||||||
|
@ -1059,7 +1109,7 @@ func gatewayTLSRouteToTCPConf(ctx context.Context, ep string, listener gatev1.Li
|
||||||
|
|
||||||
rule, err := hostSNIRule(hostnames)
|
rule, err := hostSNIRule(hostnames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// update "ResolvedRefs" status true with "DroppedRoutes" reason
|
// update "ResolvedRefs" status true with "InvalidHostnames" reason
|
||||||
conditions = append(conditions, metav1.Condition{
|
conditions = append(conditions, metav1.Condition{
|
||||||
Type: string(gatev1.ListenerConditionResolvedRefs),
|
Type: string(gatev1.ListenerConditionResolvedRefs),
|
||||||
Status: metav1.ConditionFalse,
|
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)
|
wrrService, subServices, err := loadTCPServices(client, route.Namespace, routeRule.BackendRefs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// update "ResolvedRefs" status true with "DroppedRoutes" reason
|
// update "ResolvedRefs" status true with "InvalidBackendRefs" reason
|
||||||
conditions = append(conditions, metav1.Condition{
|
conditions = append(conditions, metav1.Condition{
|
||||||
Type: string(gatev1.ListenerConditionResolvedRefs),
|
Type: string(gatev1.ListenerConditionResolvedRefs),
|
||||||
Status: metav1.ConditionFalse,
|
Status: metav1.ConditionFalse,
|
||||||
|
@ -1526,7 +1576,7 @@ func loadServices(client Client, namespace string, backendRefs []gatev1.HTTPBack
|
||||||
continue
|
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)
|
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
|
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)
|
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)
|
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"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/utils/ptr"
|
"k8s.io/utils/ptr"
|
||||||
gatev1 "sigs.k8s.io/gateway-api/apis/v1"
|
gatev1 "sigs.k8s.io/gateway-api/apis/v1"
|
||||||
|
gatev1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ provider.Provider = (*Provider)(nil)
|
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) {
|
func Test_hostRule(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
@ -5644,3 +5835,247 @@ func kindPtr(kind gatev1.Kind) *gatev1.Kind {
|
||||||
func pathMatchTypePtr(p gatev1.PathMatchType) *gatev1.PathMatchType { return &p }
|
func pathMatchTypePtr(p gatev1.PathMatchType) *gatev1.PathMatchType { return &p }
|
||||||
|
|
||||||
func headerMatchTypePtr(h gatev1.HeaderMatchType) *gatev1.HeaderMatchType { return &h }
|
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.
|
// MustParseYaml parses a YAML to objects.
|
||||||
func MustParseYaml(content []byte) []runtime.Object {
|
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")
|
files := strings.Split(string(content), "---\n")
|
||||||
retVal := make([]runtime.Object, 0, len(files))
|
retVal := make([]runtime.Object, 0, len(files))
|
||||||
|
|
Loading…
Reference in a new issue