mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-05 00:56:56 +02:00
gateway: enforce listener matching
This commit is contained in:
parent
71e45ce1d3
commit
4371589a14
@ -24,10 +24,13 @@ import (
|
||||
"text/template"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"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"
|
||||
"sigs.k8s.io/gateway-api/apis/v1alpha2"
|
||||
gateway "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned"
|
||||
@ -37,20 +40,27 @@ import (
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
const (
|
||||
gatewayGroup = "gateway.networking.k8s.io"
|
||||
gatewayKind = "Gateway"
|
||||
)
|
||||
|
||||
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
|
||||
// Metadata returns the Route's metadata.
|
||||
// Metadata returns the route's metadata.
|
||||
Metadata() *metav1.ObjectMeta
|
||||
// Hostnames returns the Route's specified hostnames.
|
||||
// Hostnames returns the route's specified hostnames.
|
||||
Hostnames() []v1alpha2.Hostname
|
||||
// Status returns the Route's status, including associated gateways.
|
||||
Status() v1alpha2.RouteStatus
|
||||
// Protocol returns the route's protocol type.
|
||||
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)
|
||||
Informer() cache.SharedIndexInformer
|
||||
}
|
||||
@ -78,7 +88,9 @@ type gatewayRouteSource struct {
|
||||
rtNamespace string
|
||||
rtLabels labels.Selector
|
||||
rtAnnotations labels.Selector
|
||||
rtInformer gatewayRouteInfomer
|
||||
rtInformer gatewayRouteInformer
|
||||
|
||||
nsInformer coreinformers.NamespaceInformer
|
||||
|
||||
fqdnTemplate *template.Template
|
||||
combineFQDNAnnotation bool
|
||||
@ -86,6 +98,8 @@ type gatewayRouteSource struct {
|
||||
}
|
||||
|
||||
func newGatewayRouteSource(clients ClientGenerator, config *Config, kind string, newInformerFn newGatewayRouteInformerFunc) (Source, error) {
|
||||
ctx := context.TODO()
|
||||
|
||||
gwLabels, err := getLabelSelector(config.GatewayLabelFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -109,25 +123,38 @@ func newGatewayRouteSource(clients ClientGenerator, config *Config, kind string,
|
||||
}
|
||||
|
||||
informerFactory := newGatewayInformerFactory(client, config.GatewayNamespace, gwLabels)
|
||||
gwInformer := informerFactory.Gateway().V1alpha2().Gateways() // TODO: gateway informer should be shared across gateway sources
|
||||
gwInformer.Informer() // Register with factory before starting
|
||||
gwInformer := informerFactory.Gateway().V1alpha2().Gateways() // TODO: Gateway informer should be shared across gateway sources.
|
||||
gwInformer.Informer() // Register with factory before starting.
|
||||
|
||||
rtInformerFactory := informerFactory
|
||||
if config.Namespace != config.GatewayNamespace || !selectorsEqual(rtLabels, gwLabels) {
|
||||
rtInformerFactory = newGatewayInformerFactory(client, config.Namespace, rtLabels)
|
||||
}
|
||||
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)
|
||||
kubeInformerFactory.Start(wait.NeverStop)
|
||||
if rtInformerFactory != informerFactory {
|
||||
rtInformerFactory.Start(wait.NeverStop)
|
||||
|
||||
if err := waitForCacheSync(context.Background(), rtInformerFactory); err != nil {
|
||||
if err := waitForCacheSync(ctx, rtInformerFactory); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
@ -142,6 +169,8 @@ func newGatewayRouteSource(clients ClientGenerator, config *Config, kind string,
|
||||
rtAnnotations: rtAnnotations,
|
||||
rtInformer: rtInformer,
|
||||
|
||||
nsInformer: nsInformer,
|
||||
|
||||
fqdnTemplate: tmpl,
|
||||
combineFQDNAnnotation: config.CombineFQDNAndAnnotation,
|
||||
ignoreHostnameAnnotation: config.IgnoreHostnameAnnotation,
|
||||
@ -150,9 +179,11 @@ func newGatewayRouteSource(clients ClientGenerator, config *Config, kind string,
|
||||
}
|
||||
|
||||
func (src *gatewayRouteSource) AddEventHandler(ctx context.Context, handler func()) {
|
||||
log.Debugf("Adding event handler for %s", src.rtKind)
|
||||
src.gwInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
|
||||
src.rtInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
|
||||
log.Debugf("Adding event handlers for %s", src.rtKind)
|
||||
eventHandler := 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) {
|
||||
@ -161,133 +192,255 @@ func (src *gatewayRouteSource) Endpoints(ctx context.Context) ([]*endpoint.Endpo
|
||||
if err != nil {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
endpoints = append(endpoints, eps...)
|
||||
}
|
||||
for _, ep := range endpoints {
|
||||
sort.Sort(ep.Targets)
|
||||
if len(hostTargets) == 0 {
|
||||
log.Debugf("No endpoints could be generated from %s %s/%s", src.rtKind, meta.Namespace, meta.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func (src *gatewayRouteSource) endpoints(rt gatewayRoute, gateways map[types.NamespacedName]*v1alpha2.Gateway) ([]*endpoint.Endpoint, error) {
|
||||
// Filter by annotations.
|
||||
meta := rt.Metadata()
|
||||
annotations := meta.Annotations
|
||||
if !src.rtAnnotations.Matches(labels.Set(meta.Annotations)) {
|
||||
return nil, nil
|
||||
}
|
||||
func namespacedName(namespace, name string) types.NamespacedName {
|
||||
return types.NamespacedName{Namespace: namespace, Name: name}
|
||||
}
|
||||
|
||||
// Check controller annotation to see if we are responsible.
|
||||
if v, ok := meta.Annotations[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)
|
||||
return nil, nil
|
||||
}
|
||||
type gatewayRouteResolver struct {
|
||||
src *gatewayRouteSource
|
||||
gws map[types.NamespacedName]gatewayListeners
|
||||
nss map[string]*corev1.Namespace
|
||||
}
|
||||
|
||||
// Get hostnames.
|
||||
hostnames, err := src.hostnames(rt)
|
||||
type gatewayListeners struct {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
if len(hostnames) == 0 {
|
||||
log.Debugf("No hostnames could be generated from %s %s/%s", src.rtKind, meta.Namespace, meta.Name)
|
||||
return nil, nil
|
||||
}
|
||||
hostTargets := make(map[string]endpoint.Targets)
|
||||
|
||||
// Get targets.
|
||||
targets := src.targets(rt, gateways)
|
||||
if len(targets) == 0 {
|
||||
log.Debugf("No targets could be generated from %s %s/%s", src.rtKind, meta.Namespace, meta.Name)
|
||||
return nil, nil
|
||||
meta := rt.Metadata()
|
||||
for _, rps := range rt.RouteStatus().Parents {
|
||||
// Confirm the Parent is the standard Gateway kind.
|
||||
ref := rps.ParentRef
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// Create endpoints.
|
||||
ttl, err := getTTLFromAnnotations(annotations)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
// If a Gateway has multiple matching Listeners for the same host, then we'll
|
||||
// add its IPs to the target list multiple times and should dedupe them.
|
||||
for host, targets := range hostTargets {
|
||||
hostTargets[host] = uniqueTargets(targets)
|
||||
}
|
||||
providerSpecific, setIdentifier := getProviderSpecificAnnotations(annotations)
|
||||
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
|
||||
return hostTargets, nil
|
||||
}
|
||||
|
||||
func (src *gatewayRouteSource) hostnames(rt gatewayRoute) ([]string, error) {
|
||||
func (c *gatewayRouteResolver) hosts(rt gatewayRoute) ([]string, error) {
|
||||
var hostnames []string
|
||||
for _, name := range rt.Hostnames() {
|
||||
hostnames = append(hostnames, string(name))
|
||||
}
|
||||
meta := rt.Metadata()
|
||||
// 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?
|
||||
if !src.ignoreHostnameAnnotation {
|
||||
hostnames = append(hostnames, getHostnamesFromAnnotations(meta.Annotations)...)
|
||||
if !c.src.ignoreHostnameAnnotation {
|
||||
hostnames = append(hostnames, getHostnamesFromAnnotations(rt.Metadata().Annotations)...)
|
||||
}
|
||||
// TODO: The combine-fqdn-annotation flag is similarly vague.
|
||||
if src.fqdnTemplate != nil && (len(hostnames) == 0 || src.combineFQDNAnnotation) {
|
||||
hosts, err := execTemplate(src.fqdnTemplate, rt.Object())
|
||||
if c.src.fqdnTemplate != nil && (len(hostnames) == 0 || c.src.combineFQDNAnnotation) {
|
||||
hosts, err := execTemplate(c.src.fqdnTemplate, rt.Object())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
func (src *gatewayRouteSource) targets(rt gatewayRoute, gateways map[types.NamespacedName]*v1alpha2.Gateway) endpoint.Targets {
|
||||
var targets endpoint.Targets
|
||||
func (c *gatewayRouteResolver) routeIsAllowed(gw *v1alpha2.Gateway, lis *v1alpha2.Listener, rt gatewayRoute) bool {
|
||||
meta := rt.Metadata()
|
||||
for _, rps := range rt.Status().Parents {
|
||||
ref := rps.ParentRef
|
||||
if (ref.Group != nil && *ref.Group != "gateway.networking.k8s.io") || (ref.Kind != nil && *ref.Kind != "Gateway") {
|
||||
log.Debugf("Unsupported parent %v/%v for %s %s/%s", ref.Group, ref.Kind, src.rtKind, meta.Namespace, meta.Name)
|
||||
continue
|
||||
allow := lis.AllowedRoutes
|
||||
|
||||
// Check the route's namespace.
|
||||
from := v1alpha2.NamespacesFromSame
|
||||
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
|
||||
if ref.Namespace != nil {
|
||||
namespace = string(*ref.Namespace)
|
||||
case v1alpha2.NamespacesFromSelector:
|
||||
selector, err := metav1.LabelSelectorAsSelector(allow.Namespaces.Selector)
|
||||
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{
|
||||
Namespace: namespace,
|
||||
Name: string(ref.Name),
|
||||
}]
|
||||
// Get namespace.
|
||||
ns, ok := c.nss[meta.Namespace]
|
||||
if !ok {
|
||||
log.Debugf("Gateway %s/%s not found for %s %s/%s", namespace, ref.Name, src.rtKind, meta.Namespace, meta.Name)
|
||||
continue
|
||||
log.Errorf("Namespace not found for %s %s/%s", c.src.rtKind, meta.Namespace, meta.Name)
|
||||
return false
|
||||
}
|
||||
if !gwRouteIsAdmitted(rps.Conditions) {
|
||||
log.Debugf("Gateway %s/%s has not admitted %s %s/%s", namespace, ref.Name, src.rtKind, meta.Namespace, meta.Name)
|
||||
continue
|
||||
if !selector.Matches(labels.Set(ns.Labels)) {
|
||||
return false
|
||||
}
|
||||
for _, addr := range gw.Status.Addresses {
|
||||
// TODO: Should we validate address type?
|
||||
// The spec says it should always be an IP.
|
||||
targets = append(targets, addr.Value)
|
||||
default:
|
||||
log.Debugf("Gateway %s/%s section %q has unknown namespace from %q", gw.Namespace, gw.Name, lis.Name, from)
|
||||
return false
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if v1alpha2.RouteConditionType(c.Type) == v1alpha2.ConditionRouteAccepted {
|
||||
return c.Status == metav1.ConditionTrue
|
||||
@ -296,15 +449,84 @@ func gwRouteIsAdmitted(conds []metav1.Condition) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func gatewaysByRef(list []*v1alpha2.Gateway) map[types.NamespacedName]*v1alpha2.Gateway {
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
func uniqueTargets(targets endpoint.Targets) endpoint.Targets {
|
||||
if len(targets) < 2 {
|
||||
return targets
|
||||
}
|
||||
set := make(map[types.NamespacedName]*v1alpha2.Gateway, len(list))
|
||||
for _, gw := range list {
|
||||
set[types.NamespacedName{Namespace: gw.Namespace, Name: gw.Name}] = gw
|
||||
sort.Strings([]string(targets))
|
||||
prev := targets[0]
|
||||
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 {
|
||||
|
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.
|
||||
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()}
|
||||
})
|
||||
}
|
||||
|
||||
type gatewayHTTPRoute struct{ route *v1alpha2.HTTPRoute }
|
||||
|
||||
func (rt *gatewayHTTPRoute) Object() kubeObject { return rt.route }
|
||||
func (rt *gatewayHTTPRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta }
|
||||
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) Object() kubeObject { return rt.route }
|
||||
func (rt *gatewayHTTPRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta }
|
||||
func (rt *gatewayHTTPRoute) Hostnames() []v1alpha2.Hostname { return rt.route.Spec.Hostnames }
|
||||
func (rt *gatewayHTTPRoute) Protocol() v1alpha2.ProtocolType { return v1alpha2.HTTPProtocolType }
|
||||
func (rt *gatewayHTTPRoute) RouteStatus() v1alpha2.RouteStatus { return rt.route.Status.RouteStatus }
|
||||
|
||||
type gatewayHTTPRouteInformer struct {
|
||||
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) {
|
||||
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 {
|
||||
return metav1.ObjectMeta{
|
||||
Name: name,
|
||||
@ -135,7 +135,10 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) {
|
||||
{
|
||||
ObjectMeta: objectMeta("gateway-namespace", "test"),
|
||||
Spec: v1alpha2.GatewaySpec{
|
||||
Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}},
|
||||
Listeners: []v1alpha2.Listener{{
|
||||
Protocol: v1alpha2.HTTPProtocolType,
|
||||
AllowedRoutes: allowAllNamespaces,
|
||||
}},
|
||||
},
|
||||
Status: gatewayStatus("1.2.3.4"),
|
||||
},
|
||||
@ -170,7 +173,10 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) {
|
||||
gateways: []*v1alpha2.Gateway{{
|
||||
ObjectMeta: objectMeta("gateway-namespace", "test"),
|
||||
Spec: v1alpha2.GatewaySpec{
|
||||
Listeners: []v1alpha2.Listener{{Protocol: v1alpha2.HTTPProtocolType}},
|
||||
Listeners: []v1alpha2.Listener{{
|
||||
Protocol: v1alpha2.HTTPProtocolType,
|
||||
AllowedRoutes: allowAllNamespaces,
|
||||
}},
|
||||
},
|
||||
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"),
|
||||
},
|
||||
},
|
||||
{
|
||||
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",
|
||||
config: Config{},
|
||||
@ -406,7 +550,7 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) {
|
||||
Status: gatewayStatus("1.2.3.4"),
|
||||
}},
|
||||
routes: []*v1alpha2.HTTPRoute{{
|
||||
ObjectMeta: objectMeta("default", "no-hostame"),
|
||||
ObjectMeta: objectMeta("default", "no-hostname"),
|
||||
Spec: v1alpha2.HTTPRouteSpec{
|
||||
Hostnames: nil,
|
||||
},
|
||||
@ -622,6 +766,175 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) {
|
||||
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 {
|
||||
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 }
|
||||
|
@ -26,17 +26,18 @@ import (
|
||||
|
||||
// NewGatewayTCPRouteSource creates a new Gateway TCPRoute source with the given config.
|
||||
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()}
|
||||
})
|
||||
}
|
||||
|
||||
type gatewayTCPRoute struct{ route *v1alpha2.TCPRoute }
|
||||
|
||||
func (rt *gatewayTCPRoute) Object() kubeObject { return rt.route }
|
||||
func (rt *gatewayTCPRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta }
|
||||
func (rt *gatewayTCPRoute) Hostnames() []v1alpha2.Hostname { return nil }
|
||||
func (rt *gatewayTCPRoute) Status() v1alpha2.RouteStatus { return rt.route.Status.RouteStatus }
|
||||
func (rt *gatewayTCPRoute) Object() kubeObject { return rt.route }
|
||||
func (rt *gatewayTCPRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta }
|
||||
func (rt *gatewayTCPRoute) Hostnames() []v1alpha2.Hostname { return nil }
|
||||
func (rt *gatewayTCPRoute) Protocol() v1alpha2.ProtocolType { return v1alpha2.TCPProtocolType }
|
||||
func (rt *gatewayTCPRoute) RouteStatus() v1alpha2.RouteStatus { return rt.route.Status.RouteStatus }
|
||||
|
||||
type gatewayTCPRouteInformer struct {
|
||||
informers_v1a2.TCPRouteInformer
|
||||
|
@ -21,7 +21,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/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/gateway-api/apis/v1alpha2"
|
||||
gatewayfake "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned/fake"
|
||||
@ -31,19 +33,34 @@ func TestGatewayTCPRouteSourceEndpoints(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gwClient := gatewayfake.NewSimpleClientset()
|
||||
kubeClient := kubefake.NewSimpleClientset()
|
||||
clients := new(MockClientGenerator)
|
||||
clients.On("GatewayClient").Return(gwClient, nil)
|
||||
clients.On("KubeClient").Return(kubeClient, nil)
|
||||
|
||||
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"}
|
||||
gw := &v1alpha2.Gateway{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "internal",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: v1alpha2.GatewaySpec{
|
||||
Listeners: []v1alpha2.Listener{{
|
||||
Protocol: v1alpha2.TCPProtocolType,
|
||||
}},
|
||||
},
|
||||
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")
|
||||
|
||||
rt := &v1alpha2.TCPRoute{
|
||||
|
@ -26,17 +26,18 @@ import (
|
||||
|
||||
// NewGatewayTLSRouteSource creates a new Gateway TLSRoute source with the given config.
|
||||
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()}
|
||||
})
|
||||
}
|
||||
|
||||
type gatewayTLSRoute struct{ route *v1alpha2.TLSRoute }
|
||||
|
||||
func (rt *gatewayTLSRoute) Object() kubeObject { return rt.route }
|
||||
func (rt *gatewayTLSRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta }
|
||||
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) Object() kubeObject { return rt.route }
|
||||
func (rt *gatewayTLSRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta }
|
||||
func (rt *gatewayTLSRoute) Hostnames() []v1alpha2.Hostname { return rt.route.Spec.Hostnames }
|
||||
func (rt *gatewayTLSRoute) Protocol() v1alpha2.ProtocolType { return v1alpha2.TLSProtocolType }
|
||||
func (rt *gatewayTLSRoute) RouteStatus() v1alpha2.RouteStatus { return rt.route.Status.RouteStatus }
|
||||
|
||||
type gatewayTLSRouteInformer struct {
|
||||
informers_v1a2.TLSRouteInformer
|
||||
|
@ -21,7 +21,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/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/gateway-api/apis/v1alpha2"
|
||||
gatewayfake "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned/fake"
|
||||
@ -31,19 +33,34 @@ func TestGatewayTLSRouteSourceEndpoints(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gwClient := gatewayfake.NewSimpleClientset()
|
||||
kubeClient := kubefake.NewSimpleClientset()
|
||||
clients := new(MockClientGenerator)
|
||||
clients.On("GatewayClient").Return(gwClient, nil)
|
||||
clients.On("KubeClient").Return(kubeClient, nil)
|
||||
|
||||
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"}
|
||||
gw := &v1alpha2.Gateway{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "internal",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: v1alpha2.GatewaySpec{
|
||||
Listeners: []v1alpha2.Listener{{
|
||||
Protocol: v1alpha2.TLSProtocolType,
|
||||
}},
|
||||
},
|
||||
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")
|
||||
|
||||
rt := &v1alpha2.TLSRoute{
|
||||
|
@ -26,17 +26,18 @@ import (
|
||||
|
||||
// NewGatewayUDPRouteSource creates a new Gateway UDPRoute source with the given config.
|
||||
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()}
|
||||
})
|
||||
}
|
||||
|
||||
type gatewayUDPRoute struct{ route *v1alpha2.UDPRoute }
|
||||
|
||||
func (rt *gatewayUDPRoute) Object() kubeObject { return rt.route }
|
||||
func (rt *gatewayUDPRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta }
|
||||
func (rt *gatewayUDPRoute) Hostnames() []v1alpha2.Hostname { return nil }
|
||||
func (rt *gatewayUDPRoute) Status() v1alpha2.RouteStatus { return rt.route.Status.RouteStatus }
|
||||
func (rt *gatewayUDPRoute) Object() kubeObject { return rt.route }
|
||||
func (rt *gatewayUDPRoute) Metadata() *metav1.ObjectMeta { return &rt.route.ObjectMeta }
|
||||
func (rt *gatewayUDPRoute) Hostnames() []v1alpha2.Hostname { return nil }
|
||||
func (rt *gatewayUDPRoute) Protocol() v1alpha2.ProtocolType { return v1alpha2.UDPProtocolType }
|
||||
func (rt *gatewayUDPRoute) RouteStatus() v1alpha2.RouteStatus { return rt.route.Status.RouteStatus }
|
||||
|
||||
type gatewayUDPRouteInformer struct {
|
||||
informers_v1a2.UDPRouteInformer
|
||||
|
@ -21,7 +21,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/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/gateway-api/apis/v1alpha2"
|
||||
gatewayfake "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned/fake"
|
||||
@ -31,19 +33,34 @@ func TestGatewayUDPRouteSourceEndpoints(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gwClient := gatewayfake.NewSimpleClientset()
|
||||
kubeClient := kubefake.NewSimpleClientset()
|
||||
clients := new(MockClientGenerator)
|
||||
clients.On("GatewayClient").Return(gwClient, nil)
|
||||
clients.On("KubeClient").Return(kubeClient, nil)
|
||||
|
||||
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"}
|
||||
gw := &v1alpha2.Gateway{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "internal",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: v1alpha2.GatewaySpec{
|
||||
Listeners: []v1alpha2.Listener{{
|
||||
Protocol: v1alpha2.UDPProtocolType,
|
||||
}},
|
||||
},
|
||||
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")
|
||||
|
||||
rt := &v1alpha2.UDPRoute{
|
||||
|
Loading…
Reference in New Issue
Block a user