diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 21b423dad..a5fbf8b2f 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -379,7 +379,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("skipper-routegroup-groupversion", "The resource version for skipper routegroup").Default(source.DefaultRoutegroupVersion).StringVar(&cfg.SkipperRouteGroupVersion) // Flags related to processing source - app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, node, fake, connector, gateway-httproute, istio-gateway, istio-virtualservice, cloudfoundry, contour-ingressroute, contour-httpproxy, gloo-proxy, crd, empty, skipper-routegroup, openshift-route, ambassador-host, kong-tcpingress)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "pod", "gateway-httproute", "istio-gateway", "istio-virtualservice", "cloudfoundry", "contour-ingressroute", "contour-httpproxy", "gloo-proxy", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route", "ambassador-host", "kong-tcpingress") + app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, node, fake, connector, gateway-httproute, gateway-tlsroute, istio-gateway, istio-virtualservice, cloudfoundry, contour-ingressroute, contour-httpproxy, gloo-proxy, crd, empty, skipper-routegroup, openshift-route, ambassador-host, kong-tcpingress)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "pod", "gateway-httproute", "gateway-tlsroute", "istio-gateway", "istio-virtualservice", "cloudfoundry", "contour-ingressroute", "contour-httpproxy", "gloo-proxy", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route", "ambassador-host", "kong-tcpingress") app.Flag("openshift-router-name", "if source is openshift-route then you can pass the ingress controller name. Based on this name external-dns will select the respective router from the route status and map that routerCanonicalHostname to the route host while creating a CNAME record.").StringVar(&cfg.OCPRouterName) app.Flag("namespace", "Limit sources of endpoints to a specific namespace (default: all namespaces)").Default(defaultConfig.Namespace).StringVar(&cfg.Namespace) app.Flag("annotation-filter", "Filter sources managed by external-dns via annotation using label selector semantics (default: all sources)").Default(defaultConfig.AnnotationFilter).StringVar(&cfg.AnnotationFilter) diff --git a/source/gateway_tlsroute.go b/source/gateway_tlsroute.go new file mode 100644 index 000000000..cf9dd857f --- /dev/null +++ b/source/gateway_tlsroute.go @@ -0,0 +1,55 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package source + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "sigs.k8s.io/gateway-api/apis/v1alpha2" + informers "sigs.k8s.io/gateway-api/pkg/client/informers/gateway/externalversions" + informers_v1a2 "sigs.k8s.io/gateway-api/pkg/client/informers/gateway/externalversions/apis/v1alpha2" +) + +// 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 &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 } + +type gatewayTLSRouteInformer struct { + informers_v1a2.TLSRouteInformer +} + +func (inf gatewayTLSRouteInformer) List(namespace string, selector labels.Selector) ([]gatewayRoute, error) { + list, err := inf.TLSRouteInformer.Lister().TLSRoutes(namespace).List(selector) + if err != nil { + return nil, err + } + routes := make([]gatewayRoute, len(list)) + for i, rt := range list { + routes[i] = &gatewayTLSRoute{rt} + } + return routes, nil +} diff --git a/source/gateway_tlsroute_test.go b/source/gateway_tlsroute_test.go new file mode 100644 index 000000000..ee944c3bc --- /dev/null +++ b/source/gateway_tlsroute_test.go @@ -0,0 +1,80 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package source + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "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" +) + +func TestGatewayTLSRouteSourceEndpoints(t *testing.T) { + t.Parallel() + + gwClient := gatewayfake.NewSimpleClientset() + clients := new(MockClientGenerator) + clients.On("GatewayClient").Return(gwClient, nil) + + ctx := context.Background() + ips := []string{"10.64.0.1", "10.64.0.2"} + gw := &v1alpha2.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "internal", + Namespace: "default", + }, + Status: gatewayStatus(ips...), + } + _, err := gwClient.GatewayV1alpha2().Gateways(gw.Namespace).Create(ctx, gw, metav1.CreateOptions{}) + require.NoError(t, err, "failed to create Gateway") + + rt := &v1alpha2.TLSRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "api", + Namespace: "default", + Annotations: map[string]string{ + hostnameAnnotationKey: "api-annotation.foobar.internal", + }, + }, + Spec: v1alpha2.TLSRouteSpec{ + Hostnames: []v1alpha2.Hostname{"api-hostnames.foobar.internal"}, + }, + Status: v1alpha2.TLSRouteStatus{ + RouteStatus: routeStatus(gatewayParentRef("default", "internal")), + }, + } + _, err = gwClient.GatewayV1alpha2().TLSRoutes(rt.Namespace).Create(ctx, rt, metav1.CreateOptions{}) + require.NoError(t, err, "failed to create TLSRoute") + + src, err := NewGatewayTLSRouteSource(clients, &Config{ + FQDNTemplate: "{{.Name}}-template.foobar.internal", + CombineFQDNAndAnnotation: true, + }) + require.NoError(t, err, "failed to create Gateway TLSRoute Source") + + endpoints, err := src.Endpoints(ctx) + require.NoError(t, err, "failed to get Endpoints") + validateEndpoints(t, endpoints, []*endpoint.Endpoint{ + newTestEndpoint("api-annotation.foobar.internal", "A", ips...), + newTestEndpoint("api-hostnames.foobar.internal", "A", ips...), + newTestEndpoint("api-template.foobar.internal", "A", ips...), + }) +} diff --git a/source/store.go b/source/store.go index 713982bf0..2da3e9527 100644 --- a/source/store.go +++ b/source/store.go @@ -229,6 +229,8 @@ func BuildWithConfig(ctx context.Context, source string, p ClientGenerator, cfg return NewPodSource(ctx, client, cfg.Namespace, cfg.Compatibility) case "gateway-httproute": return NewGatewayHTTPRouteSource(p, cfg) + case "gateway-tlsroute": + return NewGatewayTLSRouteSource(p, cfg) case "istio-gateway": kubernetesClient, err := p.KubeClient() if err != nil {