mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-05 17:16:59 +02:00
gateway: enforce listener matching
This commit is contained in:
parent
71e45ce1d3
commit
4371589a14
@ -24,10 +24,13 @@ import (
|
|||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
kubeinformers "k8s.io/client-go/informers"
|
||||||
|
coreinformers "k8s.io/client-go/informers/core/v1"
|
||||||
cache "k8s.io/client-go/tools/cache"
|
cache "k8s.io/client-go/tools/cache"
|
||||||
"sigs.k8s.io/gateway-api/apis/v1alpha2"
|
"sigs.k8s.io/gateway-api/apis/v1alpha2"
|
||||||
gateway "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned"
|
gateway "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned"
|
||||||
@ -37,20 +40,27 @@ import (
|
|||||||
"sigs.k8s.io/external-dns/endpoint"
|
"sigs.k8s.io/external-dns/endpoint"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
gatewayGroup = "gateway.networking.k8s.io"
|
||||||
|
gatewayKind = "Gateway"
|
||||||
|
)
|
||||||
|
|
||||||
type gatewayRoute interface {
|
type gatewayRoute interface {
|
||||||
// Object returns the underlying Route object to be used by templates.
|
// Object returns the underlying route object to be used by templates.
|
||||||
Object() kubeObject
|
Object() kubeObject
|
||||||
// Metadata returns the Route's metadata.
|
// Metadata returns the route's metadata.
|
||||||
Metadata() *metav1.ObjectMeta
|
Metadata() *metav1.ObjectMeta
|
||||||
// Hostnames returns the Route's specified hostnames.
|
// Hostnames returns the route's specified hostnames.
|
||||||
Hostnames() []v1alpha2.Hostname
|
Hostnames() []v1alpha2.Hostname
|
||||||
// Status returns the Route's status, including associated gateways.
|
// Protocol returns the route's protocol type.
|
||||||
Status() v1alpha2.RouteStatus
|
Protocol() v1alpha2.ProtocolType
|
||||||
|
// RouteStatus returns the route's common status.
|
||||||
|
RouteStatus() v1alpha2.RouteStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
type newGatewayRouteInformerFunc func(informers.SharedInformerFactory) gatewayRouteInfomer
|
type newGatewayRouteInformerFunc func(informers.SharedInformerFactory) gatewayRouteInformer
|
||||||
|
|
||||||
type gatewayRouteInfomer interface {
|
type gatewayRouteInformer interface {
|
||||||
List(namespace string, selector labels.Selector) ([]gatewayRoute, error)
|
List(namespace string, selector labels.Selector) ([]gatewayRoute, error)
|
||||||
Informer() cache.SharedIndexInformer
|
Informer() cache.SharedIndexInformer
|
||||||
}
|
}
|
||||||
@ -78,7 +88,9 @@ type gatewayRouteSource struct {
|
|||||||
rtNamespace string
|
rtNamespace string
|
||||||
rtLabels labels.Selector
|
rtLabels labels.Selector
|
||||||
rtAnnotations labels.Selector
|
rtAnnotations labels.Selector
|
||||||
rtInformer gatewayRouteInfomer
|
rtInformer gatewayRouteInformer
|
||||||
|
|
||||||
|
nsInformer coreinformers.NamespaceInformer
|
||||||
|
|
||||||
fqdnTemplate *template.Template
|
fqdnTemplate *template.Template
|
||||||
combineFQDNAnnotation bool
|
combineFQDNAnnotation bool
|
||||||
@ -86,6 +98,8 @@ type gatewayRouteSource struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newGatewayRouteSource(clients ClientGenerator, config *Config, kind string, newInformerFn newGatewayRouteInformerFunc) (Source, error) {
|
func newGatewayRouteSource(clients ClientGenerator, config *Config, kind string, newInformerFn newGatewayRouteInformerFunc) (Source, error) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
gwLabels, err := getLabelSelector(config.GatewayLabelFilter)
|
gwLabels, err := getLabelSelector(config.GatewayLabelFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -109,25 +123,38 @@ func newGatewayRouteSource(clients ClientGenerator, config *Config, kind string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
informerFactory := newGatewayInformerFactory(client, config.GatewayNamespace, gwLabels)
|
informerFactory := newGatewayInformerFactory(client, config.GatewayNamespace, gwLabels)
|
||||||
gwInformer := informerFactory.Gateway().V1alpha2().Gateways() // TODO: gateway informer should be shared across gateway sources
|
gwInformer := informerFactory.Gateway().V1alpha2().Gateways() // TODO: Gateway informer should be shared across gateway sources.
|
||||||
gwInformer.Informer() // Register with factory before starting
|
gwInformer.Informer() // Register with factory before starting.
|
||||||
|
|
||||||
rtInformerFactory := informerFactory
|
rtInformerFactory := informerFactory
|
||||||
if config.Namespace != config.GatewayNamespace || !selectorsEqual(rtLabels, gwLabels) {
|
if config.Namespace != config.GatewayNamespace || !selectorsEqual(rtLabels, gwLabels) {
|
||||||
rtInformerFactory = newGatewayInformerFactory(client, config.Namespace, rtLabels)
|
rtInformerFactory = newGatewayInformerFactory(client, config.Namespace, rtLabels)
|
||||||
}
|
}
|
||||||
rtInformer := newInformerFn(rtInformerFactory)
|
rtInformer := newInformerFn(rtInformerFactory)
|
||||||
rtInformer.Informer() // Register with factory before starting
|
rtInformer.Informer() // Register with factory before starting.
|
||||||
|
|
||||||
|
kubeClient, err := clients.KubeClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
kubeInformerFactory := kubeinformers.NewSharedInformerFactory(kubeClient, 0)
|
||||||
|
nsInformer := kubeInformerFactory.Core().V1().Namespaces() // TODO: Namespace informer should be shared across gateway sources.
|
||||||
|
nsInformer.Informer() // Register with factory before starting.
|
||||||
|
|
||||||
informerFactory.Start(wait.NeverStop)
|
informerFactory.Start(wait.NeverStop)
|
||||||
|
kubeInformerFactory.Start(wait.NeverStop)
|
||||||
if rtInformerFactory != informerFactory {
|
if rtInformerFactory != informerFactory {
|
||||||
rtInformerFactory.Start(wait.NeverStop)
|
rtInformerFactory.Start(wait.NeverStop)
|
||||||
|
|
||||||
if err := waitForCacheSync(context.Background(), rtInformerFactory); err != nil {
|
if err := waitForCacheSync(ctx, rtInformerFactory); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := waitForCacheSync(context.Background(), informerFactory); err != nil {
|
if err := waitForCacheSync(ctx, informerFactory); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := waitForCacheSync(ctx, kubeInformerFactory); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,6 +169,8 @@ func newGatewayRouteSource(clients ClientGenerator, config *Config, kind string,
|
|||||||
rtAnnotations: rtAnnotations,
|
rtAnnotations: rtAnnotations,
|
||||||
rtInformer: rtInformer,
|
rtInformer: rtInformer,
|
||||||
|
|
||||||
|
nsInformer: nsInformer,
|
||||||
|
|
||||||
fqdnTemplate: tmpl,
|
fqdnTemplate: tmpl,
|
||||||
combineFQDNAnnotation: config.CombineFQDNAndAnnotation,
|
combineFQDNAnnotation: config.CombineFQDNAndAnnotation,
|
||||||
ignoreHostnameAnnotation: config.IgnoreHostnameAnnotation,
|
ignoreHostnameAnnotation: config.IgnoreHostnameAnnotation,
|
||||||
@ -150,9 +179,11 @@ func newGatewayRouteSource(clients ClientGenerator, config *Config, kind string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (src *gatewayRouteSource) AddEventHandler(ctx context.Context, handler func()) {
|
func (src *gatewayRouteSource) AddEventHandler(ctx context.Context, handler func()) {
|
||||||
log.Debugf("Adding event handler for %s", src.rtKind)
|
log.Debugf("Adding event handlers for %s", src.rtKind)
|
||||||
src.gwInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
|
eventHandler := eventHandlerFunc(handler)
|
||||||
src.rtInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
|
src.gwInformer.Informer().AddEventHandler(eventHandler)
|
||||||
|
src.rtInformer.Informer().AddEventHandler(eventHandler)
|
||||||
|
src.nsInformer.Informer().AddEventHandler(eventHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (src *gatewayRouteSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) {
|
func (src *gatewayRouteSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) {
|
||||||
@ -161,133 +192,255 @@ func (src *gatewayRouteSource) Endpoints(ctx context.Context) ([]*endpoint.Endpo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
gwList, err := src.gwInformer.Lister().Gateways(src.gwNamespace).List(src.gwLabels)
|
gateways, err := src.gwInformer.Lister().Gateways(src.gwNamespace).List(src.gwLabels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
gateways := gatewaysByRef(gwList)
|
namespaces, err := src.nsInformer.Lister().List(labels.Everything())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
kind := strings.ToLower(src.rtKind)
|
||||||
|
resolver := newGatewayRouteResolver(src, gateways, namespaces)
|
||||||
for _, rt := range routes {
|
for _, rt := range routes {
|
||||||
eps, err := src.endpoints(rt, gateways)
|
// Filter by annotations.
|
||||||
|
meta := rt.Metadata()
|
||||||
|
annots := meta.Annotations
|
||||||
|
if !src.rtAnnotations.Matches(labels.Set(annots)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check controller annotation to see if we are responsible.
|
||||||
|
if v, ok := annots[controllerAnnotationKey]; ok && v != controllerAnnotationValue {
|
||||||
|
log.Debugf("Skipping %s %s/%s because controller value does not match, found: %s, required: %s",
|
||||||
|
src.rtKind, meta.Namespace, meta.Name, v, controllerAnnotationValue)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Route hostnames and their targets.
|
||||||
|
hostTargets, err := resolver.resolve(rt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
endpoints = append(endpoints, eps...)
|
if len(hostTargets) == 0 {
|
||||||
}
|
log.Debugf("No endpoints could be generated from %s %s/%s", src.rtKind, meta.Namespace, meta.Name)
|
||||||
for _, ep := range endpoints {
|
continue
|
||||||
sort.Sort(ep.Targets)
|
}
|
||||||
|
|
||||||
|
// Create endpoints from hostnames and targets.
|
||||||
|
resourceKey := fmt.Sprintf("%s/%s/%s", kind, meta.Namespace, meta.Name)
|
||||||
|
providerSpecific, setIdentifier := getProviderSpecificAnnotations(annots)
|
||||||
|
ttl, err := getTTLFromAnnotations(annots)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
}
|
||||||
|
for host, targets := range hostTargets {
|
||||||
|
eps := endpointsForHostname(host, targets, ttl, providerSpecific, setIdentifier)
|
||||||
|
for _, ep := range eps {
|
||||||
|
ep.Labels[endpoint.ResourceLabelKey] = resourceKey
|
||||||
|
}
|
||||||
|
endpoints = append(endpoints, eps...)
|
||||||
|
}
|
||||||
|
log.Debugf("Endpoints generated from %s %s/%s: %v", src.rtKind, meta.Namespace, meta.Name, endpoints)
|
||||||
}
|
}
|
||||||
return endpoints, nil
|
return endpoints, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (src *gatewayRouteSource) endpoints(rt gatewayRoute, gateways map[types.NamespacedName]*v1alpha2.Gateway) ([]*endpoint.Endpoint, error) {
|
func namespacedName(namespace, name string) types.NamespacedName {
|
||||||
// Filter by annotations.
|
return types.NamespacedName{Namespace: namespace, Name: name}
|
||||||
meta := rt.Metadata()
|
}
|
||||||
annotations := meta.Annotations
|
|
||||||
if !src.rtAnnotations.Matches(labels.Set(meta.Annotations)) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check controller annotation to see if we are responsible.
|
type gatewayRouteResolver struct {
|
||||||
if v, ok := meta.Annotations[controllerAnnotationKey]; ok && v != controllerAnnotationValue {
|
src *gatewayRouteSource
|
||||||
log.Debugf("Skipping %s %s/%s because controller value does not match, found: %s, required: %s",
|
gws map[types.NamespacedName]gatewayListeners
|
||||||
src.rtKind, meta.Namespace, meta.Name, v, controllerAnnotationValue)
|
nss map[string]*corev1.Namespace
|
||||||
return nil, nil
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Get hostnames.
|
type gatewayListeners struct {
|
||||||
hostnames, err := src.hostnames(rt)
|
gateway *v1alpha2.Gateway
|
||||||
|
listeners map[v1alpha2.SectionName][]v1alpha2.Listener
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGatewayRouteResolver(src *gatewayRouteSource, gateways []*v1alpha2.Gateway, namespaces []*corev1.Namespace) *gatewayRouteResolver {
|
||||||
|
// Create Gateway Listener lookup table.
|
||||||
|
gws := make(map[types.NamespacedName]gatewayListeners, len(gateways))
|
||||||
|
for _, gw := range gateways {
|
||||||
|
lss := make(map[v1alpha2.SectionName][]v1alpha2.Listener, len(gw.Spec.Listeners)+1)
|
||||||
|
for i, lis := range gw.Spec.Listeners {
|
||||||
|
lss[lis.Name] = gw.Spec.Listeners[i : i+1]
|
||||||
|
}
|
||||||
|
lss[""] = gw.Spec.Listeners
|
||||||
|
gws[namespacedName(gw.Namespace, gw.Name)] = gatewayListeners{
|
||||||
|
gateway: gw,
|
||||||
|
listeners: lss,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create Namespace lookup table.
|
||||||
|
nss := make(map[string]*corev1.Namespace, len(namespaces))
|
||||||
|
for _, ns := range namespaces {
|
||||||
|
nss[ns.Name] = ns
|
||||||
|
}
|
||||||
|
return &gatewayRouteResolver{
|
||||||
|
src: src,
|
||||||
|
gws: gws,
|
||||||
|
nss: nss,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gatewayRouteResolver) resolve(rt gatewayRoute) (map[string]endpoint.Targets, error) {
|
||||||
|
rtHosts, err := c.hosts(rt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(hostnames) == 0 {
|
hostTargets := make(map[string]endpoint.Targets)
|
||||||
log.Debugf("No hostnames could be generated from %s %s/%s", src.rtKind, meta.Namespace, meta.Name)
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get targets.
|
meta := rt.Metadata()
|
||||||
targets := src.targets(rt, gateways)
|
for _, rps := range rt.RouteStatus().Parents {
|
||||||
if len(targets) == 0 {
|
// Confirm the Parent is the standard Gateway kind.
|
||||||
log.Debugf("No targets could be generated from %s %s/%s", src.rtKind, meta.Namespace, meta.Name)
|
ref := rps.ParentRef
|
||||||
return nil, nil
|
group := strVal((*string)(ref.Group), gatewayGroup)
|
||||||
|
kind := strVal((*string)(ref.Kind), gatewayKind)
|
||||||
|
if group != gatewayGroup || kind != gatewayKind {
|
||||||
|
log.Debugf("Unsupported parent %s/%s for %s %s/%s", group, kind, c.src.rtKind, meta.Namespace, meta.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Lookup the Gateway and its Listeners.
|
||||||
|
namespace := strVal((*string)(ref.Namespace), meta.Namespace)
|
||||||
|
gw, ok := c.gws[namespacedName(namespace, string(ref.Name))]
|
||||||
|
if !ok {
|
||||||
|
log.Debugf("Gateway %s/%s not found for %s %s/%s", namespace, ref.Name, c.src.rtKind, meta.Namespace, meta.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Confirm the Gateway has accepted the Route.
|
||||||
|
if !gwRouteIsAccepted(rps.Conditions) {
|
||||||
|
log.Debugf("Gateway %s/%s has not accepted %s %s/%s", namespace, ref.Name, c.src.rtKind, meta.Namespace, meta.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Match the Route to all possible Listeners.
|
||||||
|
match := false
|
||||||
|
section := sectionVal(ref.SectionName, "")
|
||||||
|
listeners := gw.listeners[section]
|
||||||
|
for i := range listeners {
|
||||||
|
// Confirm that the protocols match and the Listener allows the Route (based on namespace and kind).
|
||||||
|
lis := &listeners[i]
|
||||||
|
if !gwProtocolMatches(rt.Protocol(), lis.Protocol) || !c.routeIsAllowed(gw.gateway, lis, rt) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Find all overlapping hostnames between the Route and Listener.
|
||||||
|
// For {TCP,UDP}Routes, all annotation-generated hostnames should match since the Listener doesn't specify a hostname.
|
||||||
|
// For {HTTP,TLS}Routes, hostnames (including any annotation-generated) will be required to match any Listeners specified hostname.
|
||||||
|
gwHost := ""
|
||||||
|
if lis.Hostname != nil {
|
||||||
|
gwHost = string(*lis.Hostname)
|
||||||
|
}
|
||||||
|
for _, rtHost := range rtHosts {
|
||||||
|
if gwHost == "" && rtHost == "" {
|
||||||
|
// For {HTTP,TLS}Routes, this means the Route and the Listener both allow _any_ hostnames.
|
||||||
|
// For {TCP,UDP}Routes, this should always happen since neither specifies hostnames.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
host, ok := gwMatchingHost(gwHost, rtHost)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, addr := range gw.gateway.Status.Addresses {
|
||||||
|
hostTargets[host] = append(hostTargets[host], addr.Value)
|
||||||
|
}
|
||||||
|
match = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !match {
|
||||||
|
log.Debugf("Gateway %s/%s section %q does not match %s %s/%s hostnames %q", namespace, ref.Name, section, c.src.rtKind, meta.Namespace, meta.Name, rtHosts)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// If a Gateway has multiple matching Listeners for the same host, then we'll
|
||||||
// Create endpoints.
|
// add its IPs to the target list multiple times and should dedupe them.
|
||||||
ttl, err := getTTLFromAnnotations(annotations)
|
for host, targets := range hostTargets {
|
||||||
if err != nil {
|
hostTargets[host] = uniqueTargets(targets)
|
||||||
log.Warn(err)
|
|
||||||
}
|
}
|
||||||
providerSpecific, setIdentifier := getProviderSpecificAnnotations(annotations)
|
return hostTargets, nil
|
||||||
var endpoints []*endpoint.Endpoint
|
|
||||||
for _, hostname := range hostnames {
|
|
||||||
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier)...)
|
|
||||||
}
|
|
||||||
log.Debugf("Endpoints generated from %s %s/%s: %v", src.rtKind, meta.Namespace, meta.Name, endpoints)
|
|
||||||
|
|
||||||
kind := strings.ToLower(src.rtKind)
|
|
||||||
resourceKey := fmt.Sprintf("%s/%s/%s", kind, meta.Namespace, meta.Name)
|
|
||||||
for _, ep := range endpoints {
|
|
||||||
ep.Labels[endpoint.ResourceLabelKey] = resourceKey
|
|
||||||
}
|
|
||||||
return endpoints, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (src *gatewayRouteSource) hostnames(rt gatewayRoute) ([]string, error) {
|
func (c *gatewayRouteResolver) hosts(rt gatewayRoute) ([]string, error) {
|
||||||
var hostnames []string
|
var hostnames []string
|
||||||
for _, name := range rt.Hostnames() {
|
for _, name := range rt.Hostnames() {
|
||||||
hostnames = append(hostnames, string(name))
|
hostnames = append(hostnames, string(name))
|
||||||
}
|
}
|
||||||
meta := rt.Metadata()
|
|
||||||
// TODO: The ignore-hostname-annotation flag help says "valid only when using fqdn-template"
|
// TODO: The ignore-hostname-annotation flag help says "valid only when using fqdn-template"
|
||||||
// but other sources don't check if fqdn-template is set. Which should it be?
|
// but other sources don't check if fqdn-template is set. Which should it be?
|
||||||
if !src.ignoreHostnameAnnotation {
|
if !c.src.ignoreHostnameAnnotation {
|
||||||
hostnames = append(hostnames, getHostnamesFromAnnotations(meta.Annotations)...)
|
hostnames = append(hostnames, getHostnamesFromAnnotations(rt.Metadata().Annotations)...)
|
||||||
}
|
}
|
||||||
// TODO: The combine-fqdn-annotation flag is similarly vague.
|
// TODO: The combine-fqdn-annotation flag is similarly vague.
|
||||||
if src.fqdnTemplate != nil && (len(hostnames) == 0 || src.combineFQDNAnnotation) {
|
if c.src.fqdnTemplate != nil && (len(hostnames) == 0 || c.src.combineFQDNAnnotation) {
|
||||||
hosts, err := execTemplate(src.fqdnTemplate, rt.Object())
|
hosts, err := execTemplate(c.src.fqdnTemplate, rt.Object())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
hostnames = append(hostnames, hosts...)
|
hostnames = append(hostnames, hosts...)
|
||||||
}
|
}
|
||||||
|
// This means that the route doesn't specify a hostname and should use any provided by
|
||||||
|
// attached Gateway Listeners. This is only useful for {HTTP,TLS}Routes, but it doesn't
|
||||||
|
// break {TCP,UDP}Routes.
|
||||||
|
if len(rt.Hostnames()) == 0 {
|
||||||
|
hostnames = append(hostnames, "")
|
||||||
|
}
|
||||||
return hostnames, nil
|
return hostnames, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (src *gatewayRouteSource) targets(rt gatewayRoute, gateways map[types.NamespacedName]*v1alpha2.Gateway) endpoint.Targets {
|
func (c *gatewayRouteResolver) routeIsAllowed(gw *v1alpha2.Gateway, lis *v1alpha2.Listener, rt gatewayRoute) bool {
|
||||||
var targets endpoint.Targets
|
|
||||||
meta := rt.Metadata()
|
meta := rt.Metadata()
|
||||||
for _, rps := range rt.Status().Parents {
|
allow := lis.AllowedRoutes
|
||||||
ref := rps.ParentRef
|
|
||||||
if (ref.Group != nil && *ref.Group != "gateway.networking.k8s.io") || (ref.Kind != nil && *ref.Kind != "Gateway") {
|
// Check the route's namespace.
|
||||||
log.Debugf("Unsupported parent %v/%v for %s %s/%s", ref.Group, ref.Kind, src.rtKind, meta.Namespace, meta.Name)
|
from := v1alpha2.NamespacesFromSame
|
||||||
continue
|
if allow != nil && allow.Namespaces != nil && allow.Namespaces.From != nil {
|
||||||
|
from = *allow.Namespaces.From
|
||||||
|
}
|
||||||
|
switch from {
|
||||||
|
case v1alpha2.NamespacesFromAll:
|
||||||
|
// OK
|
||||||
|
case v1alpha2.NamespacesFromSame:
|
||||||
|
if gw.Namespace != meta.Namespace {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
namespace := meta.Namespace
|
case v1alpha2.NamespacesFromSelector:
|
||||||
if ref.Namespace != nil {
|
selector, err := metav1.LabelSelectorAsSelector(allow.Namespaces.Selector)
|
||||||
namespace = string(*ref.Namespace)
|
if err != nil {
|
||||||
|
log.Debugf("Gateway %s/%s section %q has invalid namespace selector: %v", gw.Namespace, gw.Name, lis.Name, err)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
gw, ok := gateways[types.NamespacedName{
|
// Get namespace.
|
||||||
Namespace: namespace,
|
ns, ok := c.nss[meta.Namespace]
|
||||||
Name: string(ref.Name),
|
|
||||||
}]
|
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Debugf("Gateway %s/%s not found for %s %s/%s", namespace, ref.Name, src.rtKind, meta.Namespace, meta.Name)
|
log.Errorf("Namespace not found for %s %s/%s", c.src.rtKind, meta.Namespace, meta.Name)
|
||||||
continue
|
return false
|
||||||
}
|
}
|
||||||
if !gwRouteIsAdmitted(rps.Conditions) {
|
if !selector.Matches(labels.Set(ns.Labels)) {
|
||||||
log.Debugf("Gateway %s/%s has not admitted %s %s/%s", namespace, ref.Name, src.rtKind, meta.Namespace, meta.Name)
|
return false
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
for _, addr := range gw.Status.Addresses {
|
default:
|
||||||
// TODO: Should we validate address type?
|
log.Debugf("Gateway %s/%s section %q has unknown namespace from %q", gw.Namespace, gw.Name, lis.Name, from)
|
||||||
// The spec says it should always be an IP.
|
return false
|
||||||
targets = append(targets, addr.Value)
|
}
|
||||||
|
|
||||||
|
// Check the route's kind, if any are specified by the listener.
|
||||||
|
// TODO: Do we need to consider SupportedKinds in the ListenerStatus instead of the Spec?
|
||||||
|
// We only support core kinds and already check the protocol... Does this matter at all?
|
||||||
|
if allow == nil || len(allow.Kinds) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
gvk := rt.Object().GetObjectKind().GroupVersionKind()
|
||||||
|
for _, gk := range allow.Kinds {
|
||||||
|
group := strVal((*string)(gk.Group), gatewayGroup)
|
||||||
|
if gvk.Group == group && gvk.Kind == string(gk.Kind) {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return targets
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func gwRouteIsAdmitted(conds []metav1.Condition) bool {
|
func gwRouteIsAccepted(conds []metav1.Condition) bool {
|
||||||
for _, c := range conds {
|
for _, c := range conds {
|
||||||
if v1alpha2.RouteConditionType(c.Type) == v1alpha2.ConditionRouteAccepted {
|
if v1alpha2.RouteConditionType(c.Type) == v1alpha2.ConditionRouteAccepted {
|
||||||
return c.Status == metav1.ConditionTrue
|
return c.Status == metav1.ConditionTrue
|
||||||
@ -296,15 +449,84 @@ func gwRouteIsAdmitted(conds []metav1.Condition) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func gatewaysByRef(list []*v1alpha2.Gateway) map[types.NamespacedName]*v1alpha2.Gateway {
|
func uniqueTargets(targets endpoint.Targets) endpoint.Targets {
|
||||||
if len(list) == 0 {
|
if len(targets) < 2 {
|
||||||
return nil
|
return targets
|
||||||
}
|
}
|
||||||
set := make(map[types.NamespacedName]*v1alpha2.Gateway, len(list))
|
sort.Strings([]string(targets))
|
||||||
for _, gw := range list {
|
prev := targets[0]
|
||||||
set[types.NamespacedName{Namespace: gw.Namespace, Name: gw.Name}] = gw
|
n := 1
|
||||||
|
for _, v := range targets[1:] {
|
||||||
|
if v == prev {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
prev = v
|
||||||
|
targets[n] = v
|
||||||
|
n++
|
||||||
}
|
}
|
||||||
return set
|
return targets[:n]
|
||||||
|
}
|
||||||
|
|
||||||
|
// gwProtocolMatches returns whether a and b are the same protocol,
|
||||||
|
// where HTTP and HTTPS are considered the same.
|
||||||
|
func gwProtocolMatches(a, b v1alpha2.ProtocolType) bool {
|
||||||
|
if a == v1alpha2.HTTPSProtocolType {
|
||||||
|
a = v1alpha2.HTTPProtocolType
|
||||||
|
}
|
||||||
|
if b == v1alpha2.HTTPSProtocolType {
|
||||||
|
b = v1alpha2.HTTPProtocolType
|
||||||
|
}
|
||||||
|
return a == b
|
||||||
|
}
|
||||||
|
|
||||||
|
// gwMatchingHost returns the most-specific overlapping host and a bool indicating if one was found.
|
||||||
|
// For example, if one host is "*.foo.com" and the other is "bar.foo.com", "bar.foo.com" will be returned.
|
||||||
|
// An empty string matches anything.
|
||||||
|
func gwMatchingHost(gwHost, rtHost string) (string, bool) {
|
||||||
|
gwHost = toLowerCaseASCII(gwHost) // TODO: trim "." suffix?
|
||||||
|
rtHost = toLowerCaseASCII(rtHost) // TODO: trim "." suffix?
|
||||||
|
|
||||||
|
if gwHost == "" {
|
||||||
|
return rtHost, true
|
||||||
|
}
|
||||||
|
if rtHost == "" {
|
||||||
|
return gwHost, true
|
||||||
|
}
|
||||||
|
|
||||||
|
gwParts := strings.Split(gwHost, ".")
|
||||||
|
rtParts := strings.Split(rtHost, ".")
|
||||||
|
if len(gwParts) != len(rtParts) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
host := rtHost
|
||||||
|
for i, gwPart := range gwParts {
|
||||||
|
switch rtPart := rtParts[i]; {
|
||||||
|
case rtPart == gwPart:
|
||||||
|
// continue
|
||||||
|
case i == 0 && gwPart == "*":
|
||||||
|
// continue
|
||||||
|
case i == 0 && rtPart == "*":
|
||||||
|
host = gwHost // gwHost is more specific
|
||||||
|
default:
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return host, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func strVal(ptr *string, def string) string {
|
||||||
|
if ptr == nil || *ptr == "" {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
return *ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func sectionVal(ptr *v1alpha2.SectionName, def v1alpha2.SectionName) v1alpha2.SectionName {
|
||||||
|
if ptr == nil || *ptr == "" {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
return *ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectorsEqual(a, b labels.Selector) bool {
|
func selectorsEqual(a, b labels.Selector) bool {
|
||||||
|
45
source/gateway_hostname.go
Normal file
45
source/gateway_hostname.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// See:
|
||||||
|
// - https://golang.org/LICENSE
|
||||||
|
// - https://golang.org/src/crypto/x509/verify.go
|
||||||
|
|
||||||
|
package source
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// toLowerCaseASCII returns a lower-case version of in. See RFC 6125 6.4.1. We use
|
||||||
|
// an explicitly ASCII function to avoid any sharp corners resulting from
|
||||||
|
// performing Unicode operations on DNS labels.
|
||||||
|
func toLowerCaseASCII(in string) string {
|
||||||
|
// If the string is already lower-case then there's nothing to do.
|
||||||
|
isAlreadyLowerCase := true
|
||||||
|
for _, c := range in {
|
||||||
|
if c == utf8.RuneError {
|
||||||
|
// If we get a UTF-8 error then there might be
|
||||||
|
// upper-case ASCII bytes in the invalid sequence.
|
||||||
|
isAlreadyLowerCase = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if 'A' <= c && c <= 'Z' {
|
||||||
|
isAlreadyLowerCase = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isAlreadyLowerCase {
|
||||||
|
return in
|
||||||
|
}
|
||||||
|
|
||||||
|
out := []byte(in)
|
||||||
|
for i, c := range out {
|
||||||
|
if 'A' <= c && c <= 'Z' {
|
||||||
|
out[i] += 'a' - 'A'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(out)
|
||||||
|
}
|
@ -26,17 +26,18 @@ import (
|
|||||||
|
|
||||||
// NewGatewayHTTPRouteSource creates a new Gateway HTTPRoute source with the given config.
|
// NewGatewayHTTPRouteSource creates a new Gateway HTTPRoute source with the given config.
|
||||||
func NewGatewayHTTPRouteSource(clients ClientGenerator, config *Config) (Source, error) {
|
func NewGatewayHTTPRouteSource(clients ClientGenerator, config *Config) (Source, error) {
|
||||||
return newGatewayRouteSource(clients, config, "HTTPRoute", func(factory informers.SharedInformerFactory) gatewayRouteInfomer {
|
return newGatewayRouteSource(clients, config, "HTTPRoute", func(factory informers.SharedInformerFactory) gatewayRouteInformer {
|
||||||
return &gatewayHTTPRouteInformer{factory.Gateway().V1alpha2().HTTPRoutes()}
|
return &gatewayHTTPRouteInformer{factory.Gateway().V1alpha2().HTTPRoutes()}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type gatewayHTTPRoute struct{ route *v1alpha2.HTTPRoute }
|
type gatewayHTTPRoute struct{ route *v1alpha2.HTTPRoute }
|
||||||
|
|
||||||
func (rt *gatewayHTTPRoute) Object() kubeObject { return rt.route }
|
func (rt *gatewayHTTPRoute) Object() kubeObject { return rt.route }
|
||||||
func (rt *gatewayHTTPRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta }
|
func (rt *gatewayHTTPRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta }
|
||||||
func (rt *gatewayHTTPRoute) Hostnames() []v1alpha2.Hostname { return rt.route.Spec.Hostnames }
|
func (rt *gatewayHTTPRoute) Hostnames() []v1alpha2.Hostname { return rt.route.Spec.Hostnames }
|
||||||
func (rt *gatewayHTTPRoute) Status() v1alpha2.RouteStatus { return rt.route.Status.RouteStatus }
|
func (rt *gatewayHTTPRoute) Protocol() v1alpha2.ProtocolType { return v1alpha2.HTTPProtocolType }
|
||||||
|
func (rt *gatewayHTTPRoute) RouteStatus() v1alpha2.RouteStatus { return rt.route.Status.RouteStatus }
|
||||||
|
|
||||||
type gatewayHTTPRouteInformer struct {
|
type gatewayHTTPRouteInformer struct {
|
||||||
informers_v1a2.HTTPRouteInformer
|
informers_v1a2.HTTPRouteInformer
|
||||||
|
@ -91,17 +91,17 @@ func newTestEndpointWithTTL(dnsName, recordType string, ttl int64, targets ...st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func joinTargets(targets ...[]string) []string {
|
|
||||||
var s []string
|
|
||||||
for _, v := range targets {
|
|
||||||
s = append(s, v...)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) {
|
func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
fromAll := v1alpha2.NamespacesFromAll
|
||||||
|
fromSame := v1alpha2.NamespacesFromSame
|
||||||
|
fromSelector := v1alpha2.NamespacesFromSelector
|
||||||
|
allowAllNamespaces := &v1alpha2.AllowedRoutes{
|
||||||
|
Namespaces: &v1alpha2.RouteNamespaces{
|
||||||
|
From: &fromAll,
|
||||||
|
},
|
||||||
|
}
|
||||||
objectMeta := func(namespace, name string) metav1.ObjectMeta {
|
objectMeta := func(namespace, name string) metav1.ObjectMeta {
|
||||||
return metav1.ObjectMeta{
|
return metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
@ -135,7 +135,10 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ObjectMeta: objectMeta("gateway-namespace", "test"),
|
ObjectMeta: objectMeta("gateway-namespace", "test"),
|
||||||
Spec: v1alpha2.GatewaySpec{
|
Spec: v1alpha2.GatewaySpec{
|
||||||
Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}},
|
Listeners: []v1alpha2.Listener{{
|
||||||
|
Protocol: v1alpha2.HTTPProtocolType,
|
||||||
|
AllowedRoutes: allowAllNamespaces,
|
||||||
|
}},
|
||||||
},
|
},
|
||||||
Status: gatewayStatus("1.2.3.4"),
|
Status: gatewayStatus("1.2.3.4"),
|
||||||
},
|
},
|
||||||
@ -170,7 +173,10 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) {
|
|||||||
gateways: []*v1alpha2.Gateway{{
|
gateways: []*v1alpha2.Gateway{{
|
||||||
ObjectMeta: objectMeta("gateway-namespace", "test"),
|
ObjectMeta: objectMeta("gateway-namespace", "test"),
|
||||||
Spec: v1alpha2.GatewaySpec{
|
Spec: v1alpha2.GatewaySpec{
|
||||||
Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}},
|
Listeners: []v1alpha2.Listener{{
|
||||||
|
Protocol: v1alpha2.HTTPProtocolType,
|
||||||
|
AllowedRoutes: allowAllNamespaces,
|
||||||
|
}},
|
||||||
},
|
},
|
||||||
Status: gatewayStatus("1.2.3.4"),
|
Status: gatewayStatus("1.2.3.4"),
|
||||||
}},
|
}},
|
||||||
@ -380,6 +386,144 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) {
|
|||||||
newTestEndpoint("test.example.internal", "A", "1.2.3.4", "2.3.4.5"),
|
newTestEndpoint("test.example.internal", "A", "1.2.3.4", "2.3.4.5"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "MultipleListeners",
|
||||||
|
config: Config{},
|
||||||
|
namespaces: namespaces("default"),
|
||||||
|
gateways: []*v1alpha2.Gateway{{
|
||||||
|
ObjectMeta: objectMeta("default", "one"),
|
||||||
|
Spec: v1alpha2.GatewaySpec{
|
||||||
|
Listeners: []v1alpha2.Listener{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Protocol: v1alpha2.HTTPProtocolType,
|
||||||
|
Hostname: hostnamePtr("foo.example.internal"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "bar",
|
||||||
|
Protocol: v1alpha2.HTTPProtocolType,
|
||||||
|
Hostname: hostnamePtr("bar.example.internal"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Status: gatewayStatus("1.2.3.4"),
|
||||||
|
}},
|
||||||
|
routes: []*v1alpha2.HTTPRoute{{
|
||||||
|
ObjectMeta: objectMeta("default", "test"),
|
||||||
|
Spec: v1alpha2.HTTPRouteSpec{
|
||||||
|
Hostnames: hostnames("*.example.internal"),
|
||||||
|
},
|
||||||
|
Status: httpRouteStatus(
|
||||||
|
gatewayParentRef("default", "one"),
|
||||||
|
),
|
||||||
|
}},
|
||||||
|
endpoints: []*endpoint.Endpoint{
|
||||||
|
newTestEndpoint("foo.example.internal", "A", "1.2.3.4"),
|
||||||
|
newTestEndpoint("bar.example.internal", "A", "1.2.3.4"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "WildcardInGateway",
|
||||||
|
config: Config{},
|
||||||
|
namespaces: namespaces("default"),
|
||||||
|
gateways: []*v1alpha2.Gateway{{
|
||||||
|
ObjectMeta: objectMeta("default", "test"),
|
||||||
|
Spec: v1alpha2.GatewaySpec{
|
||||||
|
Listeners: []v1alpha2.Listener{{
|
||||||
|
Protocol: v1alpha2.HTTPProtocolType,
|
||||||
|
Hostname: hostnamePtr("*.example.internal"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
Status: gatewayStatus("1.2.3.4"),
|
||||||
|
}},
|
||||||
|
routes: []*v1alpha2.HTTPRoute{{
|
||||||
|
ObjectMeta: objectMeta("default", "no-hostname"),
|
||||||
|
Spec: v1alpha2.HTTPRouteSpec{
|
||||||
|
Hostnames: []v1alpha2.Hostname{
|
||||||
|
"foo.example.internal",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Status: httpRouteStatus(gatewayParentRef("default", "test")),
|
||||||
|
}},
|
||||||
|
endpoints: []*endpoint.Endpoint{
|
||||||
|
newTestEndpoint("foo.example.internal", "A", "1.2.3.4")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "WildcardInRoute",
|
||||||
|
config: Config{},
|
||||||
|
namespaces: namespaces("default"),
|
||||||
|
gateways: []*v1alpha2.Gateway{{
|
||||||
|
ObjectMeta: objectMeta("default", "test"),
|
||||||
|
Spec: v1alpha2.GatewaySpec{
|
||||||
|
Listeners: []v1alpha2.Listener{{
|
||||||
|
Protocol: v1alpha2.HTTPProtocolType,
|
||||||
|
Hostname: hostnamePtr("foo.example.internal"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
Status: gatewayStatus("1.2.3.4"),
|
||||||
|
}},
|
||||||
|
routes: []*v1alpha2.HTTPRoute{{
|
||||||
|
ObjectMeta: objectMeta("default", "no-hostname"),
|
||||||
|
Spec: v1alpha2.HTTPRouteSpec{
|
||||||
|
Hostnames: []v1alpha2.Hostname{
|
||||||
|
"*.example.internal",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Status: httpRouteStatus(gatewayParentRef("default", "test")),
|
||||||
|
}},
|
||||||
|
endpoints: []*endpoint.Endpoint{
|
||||||
|
newTestEndpoint("foo.example.internal", "A", "1.2.3.4")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "WildcardInRouteAndGateway",
|
||||||
|
config: Config{},
|
||||||
|
namespaces: namespaces("default"),
|
||||||
|
gateways: []*v1alpha2.Gateway{{
|
||||||
|
ObjectMeta: objectMeta("default", "test"),
|
||||||
|
Spec: v1alpha2.GatewaySpec{
|
||||||
|
Listeners: []v1alpha2.Listener{{
|
||||||
|
Protocol: v1alpha2.HTTPProtocolType,
|
||||||
|
Hostname: hostnamePtr("*.example.internal"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
Status: gatewayStatus("1.2.3.4"),
|
||||||
|
}},
|
||||||
|
routes: []*v1alpha2.HTTPRoute{{
|
||||||
|
ObjectMeta: objectMeta("default", "no-hostname"),
|
||||||
|
Spec: v1alpha2.HTTPRouteSpec{
|
||||||
|
Hostnames: []v1alpha2.Hostname{
|
||||||
|
"*.example.internal",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Status: httpRouteStatus(gatewayParentRef("default", "test")),
|
||||||
|
}},
|
||||||
|
endpoints: []*endpoint.Endpoint{
|
||||||
|
newTestEndpoint("*.example.internal", "A", "1.2.3.4")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "NoRouteHostname",
|
||||||
|
config: Config{},
|
||||||
|
namespaces: namespaces("default"),
|
||||||
|
gateways: []*v1alpha2.Gateway{{
|
||||||
|
ObjectMeta: objectMeta("default", "test"),
|
||||||
|
Spec: v1alpha2.GatewaySpec{
|
||||||
|
Listeners: []v1alpha2.Listener{{
|
||||||
|
Protocol: v1alpha2.HTTPProtocolType,
|
||||||
|
Hostname: hostnamePtr("foo.example.internal"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
Status: gatewayStatus("1.2.3.4"),
|
||||||
|
}},
|
||||||
|
routes: []*v1alpha2.HTTPRoute{{
|
||||||
|
ObjectMeta: objectMeta("default", "no-hostname"),
|
||||||
|
Spec: v1alpha2.HTTPRouteSpec{
|
||||||
|
Hostnames: nil,
|
||||||
|
},
|
||||||
|
Status: httpRouteStatus(gatewayParentRef("default", "test")),
|
||||||
|
}},
|
||||||
|
endpoints: []*endpoint.Endpoint{
|
||||||
|
newTestEndpoint("foo.example.internal", "A", "1.2.3.4")},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "NoGateways",
|
title: "NoGateways",
|
||||||
config: Config{},
|
config: Config{},
|
||||||
@ -406,7 +550,7 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) {
|
|||||||
Status: gatewayStatus("1.2.3.4"),
|
Status: gatewayStatus("1.2.3.4"),
|
||||||
}},
|
}},
|
||||||
routes: []*v1alpha2.HTTPRoute{{
|
routes: []*v1alpha2.HTTPRoute{{
|
||||||
ObjectMeta: objectMeta("default", "no-hostame"),
|
ObjectMeta: objectMeta("default", "no-hostname"),
|
||||||
Spec: v1alpha2.HTTPRouteSpec{
|
Spec: v1alpha2.HTTPRouteSpec{
|
||||||
Hostnames: nil,
|
Hostnames: nil,
|
||||||
},
|
},
|
||||||
@ -622,6 +766,175 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) {
|
|||||||
WithSetIdentifier("test-set-identifier"),
|
WithSetIdentifier("test-set-identifier"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "DifferentHostnameDifferentGateway",
|
||||||
|
config: Config{},
|
||||||
|
namespaces: namespaces("default"),
|
||||||
|
gateways: []*v1alpha2.Gateway{
|
||||||
|
{
|
||||||
|
ObjectMeta: objectMeta("default", "one"),
|
||||||
|
Spec: v1alpha2.GatewaySpec{
|
||||||
|
Listeners: []v1alpha2.Listener{{
|
||||||
|
Hostname: hostnamePtr("*.one.internal"),
|
||||||
|
Protocol: v1alpha2.HTTPProtocolType,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
Status: gatewayStatus("1.2.3.4"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ObjectMeta: objectMeta("default", "two"),
|
||||||
|
Spec: v1alpha2.GatewaySpec{
|
||||||
|
Listeners: []v1alpha2.Listener{{
|
||||||
|
Hostname: hostnamePtr("*.two.internal"),
|
||||||
|
Protocol: v1alpha2.HTTPProtocolType,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
Status: gatewayStatus("2.3.4.5"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
routes: []*v1alpha2.HTTPRoute{{
|
||||||
|
ObjectMeta: objectMeta("default", "test"),
|
||||||
|
Spec: v1alpha2.HTTPRouteSpec{
|
||||||
|
Hostnames: hostnames("test.one.internal", "test.two.internal"),
|
||||||
|
},
|
||||||
|
Status: httpRouteStatus(
|
||||||
|
gatewayParentRef("default", "one"),
|
||||||
|
gatewayParentRef("default", "two"),
|
||||||
|
),
|
||||||
|
}},
|
||||||
|
endpoints: []*endpoint.Endpoint{
|
||||||
|
newTestEndpoint("test.one.internal", "A", "1.2.3.4"),
|
||||||
|
newTestEndpoint("test.two.internal", "A", "2.3.4.5"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "AllowedRoutesSameNamespace",
|
||||||
|
config: Config{},
|
||||||
|
namespaces: namespaces("same-namespace", "other-namespace"),
|
||||||
|
gateways: []*v1alpha2.Gateway{{
|
||||||
|
ObjectMeta: objectMeta("same-namespace", "test"),
|
||||||
|
Spec: v1alpha2.GatewaySpec{
|
||||||
|
Listeners: []v1alpha2.Listener{{
|
||||||
|
Protocol: v1alpha2.HTTPProtocolType,
|
||||||
|
AllowedRoutes: &v1alpha2.AllowedRoutes{
|
||||||
|
Namespaces: &v1alpha2.RouteNamespaces{
|
||||||
|
From: &fromSame,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
Status: gatewayStatus("1.2.3.4"),
|
||||||
|
}},
|
||||||
|
routes: []*v1alpha2.HTTPRoute{
|
||||||
|
{
|
||||||
|
ObjectMeta: objectMeta("same-namespace", "test"),
|
||||||
|
Spec: v1alpha2.HTTPRouteSpec{
|
||||||
|
Hostnames: hostnames("same-namespace.example.internal"),
|
||||||
|
},
|
||||||
|
Status: httpRouteStatus(gatewayParentRef("same-namespace", "test")),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ObjectMeta: objectMeta("other-namespace", "test"),
|
||||||
|
Spec: v1alpha2.HTTPRouteSpec{
|
||||||
|
Hostnames: hostnames("other-namespace.example.internal"),
|
||||||
|
},
|
||||||
|
Status: httpRouteStatus(gatewayParentRef("same-namespace", "test")),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
endpoints: []*endpoint.Endpoint{
|
||||||
|
newTestEndpoint("same-namespace.example.internal", "A", "1.2.3.4"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "AllowedRoutesNamespaceSelector",
|
||||||
|
config: Config{},
|
||||||
|
namespaces: []*corev1.Namespace{
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "default",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
Labels: map[string]string{"team": "foo"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "bar",
|
||||||
|
Labels: map[string]string{"team": "bar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gateways: []*v1alpha2.Gateway{{
|
||||||
|
ObjectMeta: objectMeta("default", "test"),
|
||||||
|
Spec: v1alpha2.GatewaySpec{
|
||||||
|
Listeners: []v1alpha2.Listener{{
|
||||||
|
Protocol: v1alpha2.HTTPProtocolType,
|
||||||
|
AllowedRoutes: &v1alpha2.AllowedRoutes{
|
||||||
|
Namespaces: &v1alpha2.RouteNamespaces{
|
||||||
|
From: &fromSelector,
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{"team": "foo"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
Status: gatewayStatus("1.2.3.4"),
|
||||||
|
}},
|
||||||
|
routes: []*v1alpha2.HTTPRoute{
|
||||||
|
{
|
||||||
|
ObjectMeta: objectMeta("foo", "test"),
|
||||||
|
Spec: v1alpha2.HTTPRouteSpec{
|
||||||
|
Hostnames: hostnames("foo.example.internal"),
|
||||||
|
},
|
||||||
|
Status: httpRouteStatus(gatewayParentRef("default", "test")),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ObjectMeta: objectMeta("bar", "test"),
|
||||||
|
Spec: v1alpha2.HTTPRouteSpec{
|
||||||
|
Hostnames: hostnames("bar.example.internal"),
|
||||||
|
},
|
||||||
|
Status: httpRouteStatus(gatewayParentRef("default", "test")),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
endpoints: []*endpoint.Endpoint{
|
||||||
|
newTestEndpoint("foo.example.internal", "A", "1.2.3.4"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "MissingNamespace",
|
||||||
|
config: Config{},
|
||||||
|
namespaces: nil,
|
||||||
|
gateways: []*v1alpha2.Gateway{{
|
||||||
|
ObjectMeta: objectMeta("default", "test"),
|
||||||
|
Spec: v1alpha2.GatewaySpec{
|
||||||
|
Listeners: []v1alpha2.Listener{{
|
||||||
|
Protocol: v1alpha2.HTTPProtocolType,
|
||||||
|
AllowedRoutes: &v1alpha2.AllowedRoutes{
|
||||||
|
Namespaces: &v1alpha2.RouteNamespaces{
|
||||||
|
// Namespace selector triggers namespace lookup.
|
||||||
|
From: &fromSelector,
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{"foo": "bar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
Status: gatewayStatus("1.2.3.4"),
|
||||||
|
}},
|
||||||
|
routes: []*v1alpha2.HTTPRoute{{
|
||||||
|
ObjectMeta: objectMeta("default", "test"),
|
||||||
|
Spec: v1alpha2.HTTPRouteSpec{
|
||||||
|
Hostnames: hostnames("example.internal"),
|
||||||
|
},
|
||||||
|
Status: httpRouteStatus(gatewayParentRef("default", "test")),
|
||||||
|
}},
|
||||||
|
endpoints: nil,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
tt := tt
|
tt := tt
|
||||||
@ -659,6 +972,4 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func strPtr(val string) *string { return &val }
|
|
||||||
|
|
||||||
func hostnamePtr(val v1alpha2.Hostname) *v1alpha2.Hostname { return &val }
|
func hostnamePtr(val v1alpha2.Hostname) *v1alpha2.Hostname { return &val }
|
||||||
|
@ -26,17 +26,18 @@ import (
|
|||||||
|
|
||||||
// NewGatewayTCPRouteSource creates a new Gateway TCPRoute source with the given config.
|
// NewGatewayTCPRouteSource creates a new Gateway TCPRoute source with the given config.
|
||||||
func NewGatewayTCPRouteSource(clients ClientGenerator, config *Config) (Source, error) {
|
func NewGatewayTCPRouteSource(clients ClientGenerator, config *Config) (Source, error) {
|
||||||
return newGatewayRouteSource(clients, config, "TCPRoute", func(factory informers.SharedInformerFactory) gatewayRouteInfomer {
|
return newGatewayRouteSource(clients, config, "TCPRoute", func(factory informers.SharedInformerFactory) gatewayRouteInformer {
|
||||||
return &gatewayTCPRouteInformer{factory.Gateway().V1alpha2().TCPRoutes()}
|
return &gatewayTCPRouteInformer{factory.Gateway().V1alpha2().TCPRoutes()}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type gatewayTCPRoute struct{ route *v1alpha2.TCPRoute }
|
type gatewayTCPRoute struct{ route *v1alpha2.TCPRoute }
|
||||||
|
|
||||||
func (rt *gatewayTCPRoute) Object() kubeObject { return rt.route }
|
func (rt *gatewayTCPRoute) Object() kubeObject { return rt.route }
|
||||||
func (rt *gatewayTCPRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta }
|
func (rt *gatewayTCPRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta }
|
||||||
func (rt *gatewayTCPRoute) Hostnames() []v1alpha2.Hostname { return nil }
|
func (rt *gatewayTCPRoute) Hostnames() []v1alpha2.Hostname { return nil }
|
||||||
func (rt *gatewayTCPRoute) Status() v1alpha2.RouteStatus { return rt.route.Status.RouteStatus }
|
func (rt *gatewayTCPRoute) Protocol() v1alpha2.ProtocolType { return v1alpha2.TCPProtocolType }
|
||||||
|
func (rt *gatewayTCPRoute) RouteStatus() v1alpha2.RouteStatus { return rt.route.Status.RouteStatus }
|
||||||
|
|
||||||
type gatewayTCPRouteInformer struct {
|
type gatewayTCPRouteInformer struct {
|
||||||
informers_v1a2.TCPRouteInformer
|
informers_v1a2.TCPRouteInformer
|
||||||
|
@ -21,7 +21,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||||
"sigs.k8s.io/external-dns/endpoint"
|
"sigs.k8s.io/external-dns/endpoint"
|
||||||
"sigs.k8s.io/gateway-api/apis/v1alpha2"
|
"sigs.k8s.io/gateway-api/apis/v1alpha2"
|
||||||
gatewayfake "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned/fake"
|
gatewayfake "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned/fake"
|
||||||
@ -31,19 +33,34 @@ func TestGatewayTCPRouteSourceEndpoints(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
gwClient := gatewayfake.NewSimpleClientset()
|
gwClient := gatewayfake.NewSimpleClientset()
|
||||||
|
kubeClient := kubefake.NewSimpleClientset()
|
||||||
clients := new(MockClientGenerator)
|
clients := new(MockClientGenerator)
|
||||||
clients.On("GatewayClient").Return(gwClient, nil)
|
clients.On("GatewayClient").Return(gwClient, nil)
|
||||||
|
clients.On("KubeClient").Return(kubeClient, nil)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
ns := &corev1.Namespace{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := kubeClient.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{})
|
||||||
|
require.NoError(t, err, "failed to create Namespace")
|
||||||
|
|
||||||
ips := []string{"10.64.0.1", "10.64.0.2"}
|
ips := []string{"10.64.0.1", "10.64.0.2"}
|
||||||
gw := &v1alpha2.Gateway{
|
gw := &v1alpha2.Gateway{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "internal",
|
Name: "internal",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
},
|
},
|
||||||
|
Spec: v1alpha2.GatewaySpec{
|
||||||
|
Listeners: []v1alpha2.Listener{{
|
||||||
|
Protocol: v1alpha2.TCPProtocolType,
|
||||||
|
}},
|
||||||
|
},
|
||||||
Status: gatewayStatus(ips...),
|
Status: gatewayStatus(ips...),
|
||||||
}
|
}
|
||||||
_, err := gwClient.GatewayV1alpha2().Gateways(gw.Namespace).Create(ctx, gw, metav1.CreateOptions{})
|
_, err = gwClient.GatewayV1alpha2().Gateways(gw.Namespace).Create(ctx, gw, metav1.CreateOptions{})
|
||||||
require.NoError(t, err, "failed to create Gateway")
|
require.NoError(t, err, "failed to create Gateway")
|
||||||
|
|
||||||
rt := &v1alpha2.TCPRoute{
|
rt := &v1alpha2.TCPRoute{
|
||||||
|
@ -26,17 +26,18 @@ import (
|
|||||||
|
|
||||||
// NewGatewayTLSRouteSource creates a new Gateway TLSRoute source with the given config.
|
// NewGatewayTLSRouteSource creates a new Gateway TLSRoute source with the given config.
|
||||||
func NewGatewayTLSRouteSource(clients ClientGenerator, config *Config) (Source, error) {
|
func NewGatewayTLSRouteSource(clients ClientGenerator, config *Config) (Source, error) {
|
||||||
return newGatewayRouteSource(clients, config, "TLSRoute", func(factory informers.SharedInformerFactory) gatewayRouteInfomer {
|
return newGatewayRouteSource(clients, config, "TLSRoute", func(factory informers.SharedInformerFactory) gatewayRouteInformer {
|
||||||
return &gatewayTLSRouteInformer{factory.Gateway().V1alpha2().TLSRoutes()}
|
return &gatewayTLSRouteInformer{factory.Gateway().V1alpha2().TLSRoutes()}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type gatewayTLSRoute struct{ route *v1alpha2.TLSRoute }
|
type gatewayTLSRoute struct{ route *v1alpha2.TLSRoute }
|
||||||
|
|
||||||
func (rt *gatewayTLSRoute) Object() kubeObject { return rt.route }
|
func (rt *gatewayTLSRoute) Object() kubeObject { return rt.route }
|
||||||
func (rt *gatewayTLSRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta }
|
func (rt *gatewayTLSRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta }
|
||||||
func (rt *gatewayTLSRoute) Hostnames() []v1alpha2.Hostname { return rt.route.Spec.Hostnames }
|
func (rt *gatewayTLSRoute) Hostnames() []v1alpha2.Hostname { return rt.route.Spec.Hostnames }
|
||||||
func (rt *gatewayTLSRoute) Status() v1alpha2.RouteStatus { return rt.route.Status.RouteStatus }
|
func (rt *gatewayTLSRoute) Protocol() v1alpha2.ProtocolType { return v1alpha2.TLSProtocolType }
|
||||||
|
func (rt *gatewayTLSRoute) RouteStatus() v1alpha2.RouteStatus { return rt.route.Status.RouteStatus }
|
||||||
|
|
||||||
type gatewayTLSRouteInformer struct {
|
type gatewayTLSRouteInformer struct {
|
||||||
informers_v1a2.TLSRouteInformer
|
informers_v1a2.TLSRouteInformer
|
||||||
|
@ -21,7 +21,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||||
"sigs.k8s.io/external-dns/endpoint"
|
"sigs.k8s.io/external-dns/endpoint"
|
||||||
"sigs.k8s.io/gateway-api/apis/v1alpha2"
|
"sigs.k8s.io/gateway-api/apis/v1alpha2"
|
||||||
gatewayfake "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned/fake"
|
gatewayfake "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned/fake"
|
||||||
@ -31,19 +33,34 @@ func TestGatewayTLSRouteSourceEndpoints(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
gwClient := gatewayfake.NewSimpleClientset()
|
gwClient := gatewayfake.NewSimpleClientset()
|
||||||
|
kubeClient := kubefake.NewSimpleClientset()
|
||||||
clients := new(MockClientGenerator)
|
clients := new(MockClientGenerator)
|
||||||
clients.On("GatewayClient").Return(gwClient, nil)
|
clients.On("GatewayClient").Return(gwClient, nil)
|
||||||
|
clients.On("KubeClient").Return(kubeClient, nil)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
ns := &corev1.Namespace{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := kubeClient.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{})
|
||||||
|
require.NoError(t, err, "failed to create Namespace")
|
||||||
|
|
||||||
ips := []string{"10.64.0.1", "10.64.0.2"}
|
ips := []string{"10.64.0.1", "10.64.0.2"}
|
||||||
gw := &v1alpha2.Gateway{
|
gw := &v1alpha2.Gateway{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "internal",
|
Name: "internal",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
},
|
},
|
||||||
|
Spec: v1alpha2.GatewaySpec{
|
||||||
|
Listeners: []v1alpha2.Listener{{
|
||||||
|
Protocol: v1alpha2.TLSProtocolType,
|
||||||
|
}},
|
||||||
|
},
|
||||||
Status: gatewayStatus(ips...),
|
Status: gatewayStatus(ips...),
|
||||||
}
|
}
|
||||||
_, err := gwClient.GatewayV1alpha2().Gateways(gw.Namespace).Create(ctx, gw, metav1.CreateOptions{})
|
_, err = gwClient.GatewayV1alpha2().Gateways(gw.Namespace).Create(ctx, gw, metav1.CreateOptions{})
|
||||||
require.NoError(t, err, "failed to create Gateway")
|
require.NoError(t, err, "failed to create Gateway")
|
||||||
|
|
||||||
rt := &v1alpha2.TLSRoute{
|
rt := &v1alpha2.TLSRoute{
|
||||||
|
@ -26,17 +26,18 @@ import (
|
|||||||
|
|
||||||
// NewGatewayUDPRouteSource creates a new Gateway UDPRoute source with the given config.
|
// NewGatewayUDPRouteSource creates a new Gateway UDPRoute source with the given config.
|
||||||
func NewGatewayUDPRouteSource(clients ClientGenerator, config *Config) (Source, error) {
|
func NewGatewayUDPRouteSource(clients ClientGenerator, config *Config) (Source, error) {
|
||||||
return newGatewayRouteSource(clients, config, "UDPRoute", func(factory informers.SharedInformerFactory) gatewayRouteInfomer {
|
return newGatewayRouteSource(clients, config, "UDPRoute", func(factory informers.SharedInformerFactory) gatewayRouteInformer {
|
||||||
return &gatewayUDPRouteInformer{factory.Gateway().V1alpha2().UDPRoutes()}
|
return &gatewayUDPRouteInformer{factory.Gateway().V1alpha2().UDPRoutes()}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type gatewayUDPRoute struct{ route *v1alpha2.UDPRoute }
|
type gatewayUDPRoute struct{ route *v1alpha2.UDPRoute }
|
||||||
|
|
||||||
func (rt *gatewayUDPRoute) Object() kubeObject { return rt.route }
|
func (rt *gatewayUDPRoute) Object() kubeObject { return rt.route }
|
||||||
func (rt *gatewayUDPRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta }
|
func (rt *gatewayUDPRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta }
|
||||||
func (rt *gatewayUDPRoute) Hostnames() []v1alpha2.Hostname { return nil }
|
func (rt *gatewayUDPRoute) Hostnames() []v1alpha2.Hostname { return nil }
|
||||||
func (rt *gatewayUDPRoute) Status() v1alpha2.RouteStatus { return rt.route.Status.RouteStatus }
|
func (rt *gatewayUDPRoute) Protocol() v1alpha2.ProtocolType { return v1alpha2.UDPProtocolType }
|
||||||
|
func (rt *gatewayUDPRoute) RouteStatus() v1alpha2.RouteStatus { return rt.route.Status.RouteStatus }
|
||||||
|
|
||||||
type gatewayUDPRouteInformer struct {
|
type gatewayUDPRouteInformer struct {
|
||||||
informers_v1a2.UDPRouteInformer
|
informers_v1a2.UDPRouteInformer
|
||||||
|
@ -21,7 +21,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||||
"sigs.k8s.io/external-dns/endpoint"
|
"sigs.k8s.io/external-dns/endpoint"
|
||||||
"sigs.k8s.io/gateway-api/apis/v1alpha2"
|
"sigs.k8s.io/gateway-api/apis/v1alpha2"
|
||||||
gatewayfake "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned/fake"
|
gatewayfake "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned/fake"
|
||||||
@ -31,19 +33,34 @@ func TestGatewayUDPRouteSourceEndpoints(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
gwClient := gatewayfake.NewSimpleClientset()
|
gwClient := gatewayfake.NewSimpleClientset()
|
||||||
|
kubeClient := kubefake.NewSimpleClientset()
|
||||||
clients := new(MockClientGenerator)
|
clients := new(MockClientGenerator)
|
||||||
clients.On("GatewayClient").Return(gwClient, nil)
|
clients.On("GatewayClient").Return(gwClient, nil)
|
||||||
|
clients.On("KubeClient").Return(kubeClient, nil)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
ns := &corev1.Namespace{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := kubeClient.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{})
|
||||||
|
require.NoError(t, err, "failed to create Namespace")
|
||||||
|
|
||||||
ips := []string{"10.64.0.1", "10.64.0.2"}
|
ips := []string{"10.64.0.1", "10.64.0.2"}
|
||||||
gw := &v1alpha2.Gateway{
|
gw := &v1alpha2.Gateway{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "internal",
|
Name: "internal",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
},
|
},
|
||||||
|
Spec: v1alpha2.GatewaySpec{
|
||||||
|
Listeners: []v1alpha2.Listener{{
|
||||||
|
Protocol: v1alpha2.UDPProtocolType,
|
||||||
|
}},
|
||||||
|
},
|
||||||
Status: gatewayStatus(ips...),
|
Status: gatewayStatus(ips...),
|
||||||
}
|
}
|
||||||
_, err := gwClient.GatewayV1alpha2().Gateways(gw.Namespace).Create(ctx, gw, metav1.CreateOptions{})
|
_, err = gwClient.GatewayV1alpha2().Gateways(gw.Namespace).Create(ctx, gw, metav1.CreateOptions{})
|
||||||
require.NoError(t, err, "failed to create Gateway")
|
require.NoError(t, err, "failed to create Gateway")
|
||||||
|
|
||||||
rt := &v1alpha2.UDPRoute{
|
rt := &v1alpha2.UDPRoute{
|
||||||
|
Loading…
Reference in New Issue
Block a user