diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index e9c75c28a0..aa2e4eef46 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -387,7 +387,7 @@ func (cfg *Config) ParseFlags(args []string) error { 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) - app.Flag("label-filter", "Filter sources managed by external-dns via label selector when listing all resources; currently supported by source types CRD, ingress, service and openshift-route").Default(defaultConfig.LabelFilter).StringVar(&cfg.LabelFilter) + app.Flag("label-filter", "Filter sources managed by external-dns via label selector when listing all resources; currently supported by source types CRD, ingress, service, openshift-route and ambassador-host").Default(defaultConfig.LabelFilter).StringVar(&cfg.LabelFilter) app.Flag("fqdn-template", "A templated string that's used to generate DNS names from sources that don't define a hostname themselves, or to add a hostname suffix when paired with the fake source (optional). Accepts comma separated list for multiple global FQDN.").Default(defaultConfig.FQDNTemplate).StringVar(&cfg.FQDNTemplate) app.Flag("combine-fqdn-annotation", "Combine FQDN template and Annotations instead of overwriting").BoolVar(&cfg.CombineFQDNAndAnnotation) app.Flag("ignore-hostname-annotation", "Ignore hostname annotation when generating DNS names, valid only when using fqdn-template is set (optional, default: false)").BoolVar(&cfg.IgnoreHostnameAnnotation) diff --git a/source/ambassador_host.go b/source/ambassador_host.go index 52c08b5a65..f68ca0872d 100644 --- a/source/ambassador_host.go +++ b/source/ambassador_host.go @@ -60,6 +60,7 @@ type ambassadorHostSource struct { annotationFilter string ambassadorHostInformer informers.GenericInformer unstructuredConverter *unstructuredConverter + labelSelector labels.Selector } // NewAmbassadorHostSource creates a new ambassadorHostSource with the given config. @@ -69,6 +70,7 @@ func NewAmbassadorHostSource( kubeClient kubernetes.Interface, namespace string, annotationFilter string, + labelSelector labels.Selector, ) (Source, error) { var err error @@ -104,13 +106,14 @@ func NewAmbassadorHostSource( annotationFilter: annotationFilter, ambassadorHostInformer: ambassadorHostInformer, unstructuredConverter: uc, + labelSelector: labelSelector, }, nil } // Endpoints returns endpoint objects for each host-target combination that should be processed. // Retrieves all Hosts in the source's namespace(s). func (sc *ambassadorHostSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) { - hosts, err := sc.ambassadorHostInformer.Lister().ByNamespace(sc.namespace).List(labels.Everything()) + hosts, err := sc.ambassadorHostInformer.Lister().ByNamespace(sc.namespace).List(sc.labelSelector) if err != nil { return nil, err } diff --git a/source/ambassador_host_test.go b/source/ambassador_host_test.go index dbfa282918..0a607a78a1 100644 --- a/source/ambassador_host_test.go +++ b/source/ambassador_host_test.go @@ -27,6 +27,7 @@ import ( v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" fakeDynamic "k8s.io/client-go/dynamic/fake" "k8s.io/client-go/kubernetes/fake" @@ -61,14 +62,17 @@ func testAmbassadorSourceEndpoints(t *testing.T) { ambassadorHostItems []fakeAmbassadorHost expected []*endpoint.Endpoint expectError bool + labelSelector labels.Selector }{ { title: "no host", targetNamespace: "", + labelSelector: labels.Everything(), }, { title: "two simple hosts", targetNamespace: "", + labelSelector: labels.Everything(), loadBalancer: fakeAmbassadorLoadBalancerService{ ips: []string{"8.8.8.8"}, hostnames: []string{"lb.com"}, @@ -115,6 +119,7 @@ func testAmbassadorSourceEndpoints(t *testing.T) { { title: "two simple hosts on different namespaces", targetNamespace: "", + labelSelector: labels.Everything(), loadBalancer: fakeAmbassadorLoadBalancerService{ ips: []string{"8.8.8.8"}, hostnames: []string{"lb.com"}, @@ -161,6 +166,7 @@ func testAmbassadorSourceEndpoints(t *testing.T) { { title: "invalid non matching host ambassador service annotation", targetNamespace: "", + labelSelector: labels.Everything(), loadBalancer: fakeAmbassadorLoadBalancerService{ ips: []string{"8.8.8.8"}, hostnames: []string{"lb.com"}, @@ -184,6 +190,7 @@ func testAmbassadorSourceEndpoints(t *testing.T) { title: "valid matching annotation filter expression", targetNamespace: "", annotationFilter: "kubernetes.io/ingress.class in (external-ingress)", + labelSelector: labels.Everything(), loadBalancer: fakeAmbassadorLoadBalancerService{ ips: []string{"8.8.8.8"}, hostnames: []string{"lb.com"}, @@ -216,6 +223,7 @@ func testAmbassadorSourceEndpoints(t *testing.T) { title: "valid non-matching annotation filter expression", targetNamespace: "", annotationFilter: "kubernetes.io/ingress.class in (external-ingress)", + labelSelector: labels.Everything(), loadBalancer: fakeAmbassadorLoadBalancerService{ ips: []string{"8.8.8.8"}, hostnames: []string{"lb.com"}, @@ -239,6 +247,7 @@ func testAmbassadorSourceEndpoints(t *testing.T) { title: "invalid annotation filter expression", targetNamespace: "", annotationFilter: "kubernetes.io/ingress.class in (external ingress)", + labelSelector: labels.Everything(), loadBalancer: fakeAmbassadorLoadBalancerService{ ips: []string{"8.8.8.8"}, hostnames: []string{"lb.com"}, @@ -263,6 +272,7 @@ func testAmbassadorSourceEndpoints(t *testing.T) { title: "valid matching annotation filter label", targetNamespace: "", annotationFilter: "kubernetes.io/ingress.class=external-ingress", + labelSelector: labels.Everything(), loadBalancer: fakeAmbassadorLoadBalancerService{ ips: []string{"8.8.8.8"}, hostnames: []string{"lb.com"}, @@ -295,6 +305,7 @@ func testAmbassadorSourceEndpoints(t *testing.T) { title: "valid non-matching annotation filter label", targetNamespace: "", annotationFilter: "kubernetes.io/ingress.class=external-ingress", + labelSelector: labels.Everything(), loadBalancer: fakeAmbassadorLoadBalancerService{ ips: []string{"8.8.8.8"}, hostnames: []string{"lb.com"}, @@ -314,6 +325,207 @@ func testAmbassadorSourceEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{}, }, + { + title: "valid matching label filter expression", + targetNamespace: "", + // annotationFilter: "kubernetes.io/ingress.class in (external-ingress)", + labelSelector: labels.SelectorFromSet(labels.Set{"kubernetes.io/ingress.class": "external-ingress"}), + loadBalancer: fakeAmbassadorLoadBalancerService{ + ips: []string{"8.8.8.8"}, + hostnames: []string{"lb.com"}, + name: "emissary", + namespace: "emissary-ingress", + }, + ambassadorHostItems: []fakeAmbassadorHost{ + { + name: "fake1", + namespace: "testing1", + hostname: "fake1.org", + annotations: map[string]string{ + "external-dns.ambassador-service": "emissary-ingress/emissary", + "kubernetes.io/ingress.class": "external-ingress", + }, + labels: map[string]string{ + "kubernetes.io/ingress.class": "external-ingress", + }, + }, + }, + expected: []*endpoint.Endpoint{ + { + DNSName: "fake1.org", + Targets: endpoint.Targets{"8.8.8.8"}, + }, + { + DNSName: "fake1.org", + Targets: endpoint.Targets{"lb.com"}, + }, + }, + }, + { + title: "valid non-matching label filter expression", + targetNamespace: "", + // annotationFilter: "kubernetes.io/ingress.class in (external-ingress)", + labelSelector: labels.SelectorFromSet(labels.Set{"kubernetes.io/ingress.class": "external-ingress"}), + loadBalancer: fakeAmbassadorLoadBalancerService{ + ips: []string{"8.8.8.8"}, + hostnames: []string{"lb.com"}, + name: "emissary", + namespace: "emissary-ingress", + }, + ambassadorHostItems: []fakeAmbassadorHost{ + { + name: "fake1", + namespace: "testing1", + hostname: "fake1.org", + annotations: map[string]string{ + "external-dns.ambassador-service": "emissary-ingress/emissary", + "kubernetes.io/ingress.class": "internal-ingress", + }, + labels: map[string]string{ + "kubernetes.io/ingress.class": "internal-ingress", + }, + }, + }, + expected: []*endpoint.Endpoint{}, + }, + { + title: "valid matching label filter expression for single host", + targetNamespace: "", + // annotationFilter: "kubernetes.io/ingress.class in (external-ingress)", + labelSelector: labels.SelectorFromSet(labels.Set{"kubernetes.io/ingress.class": "external-ingress"}), + loadBalancer: fakeAmbassadorLoadBalancerService{ + ips: []string{"8.8.8.8"}, + hostnames: []string{"lb.com"}, + name: "emissary", + namespace: "emissary-ingress", + }, + ambassadorHostItems: []fakeAmbassadorHost{ + { + name: "fake1", + namespace: "testing1", + hostname: "fake1.org", + annotations: map[string]string{ + "external-dns.ambassador-service": "emissary-ingress/emissary", + "kubernetes.io/ingress.class": "external-ingress", + }, + labels: map[string]string{ + "kubernetes.io/ingress.class": "external-ingress", + }, + }, + { + name: "fake2", + namespace: "testing2", + hostname: "fake2.org", + annotations: map[string]string{ + "external-dns.ambassador-service": "emissary-ingress/emissary", + "kubernetes.io/ingress.class": "internal-ingress", + }, + labels: map[string]string{ + "kubernetes.io/ingress.class": "internal-ingress", + }, + }, + }, + expected: []*endpoint.Endpoint{ + { + DNSName: "fake1.org", + Targets: endpoint.Targets{"8.8.8.8"}, + }, + { + DNSName: "fake1.org", + Targets: endpoint.Targets{"lb.com"}, + }, + }, + }, + { + title: "valid matching label filter expression and matching annotation filter", + targetNamespace: "", + annotationFilter: "kubernetes.io/ingress.class in (external-ingress)", + labelSelector: labels.SelectorFromSet(labels.Set{"kubernetes.io/ingress.class": "external-ingress"}), + loadBalancer: fakeAmbassadorLoadBalancerService{ + ips: []string{"8.8.8.8"}, + hostnames: []string{"lb.com"}, + name: "emissary", + namespace: "emissary-ingress", + }, + ambassadorHostItems: []fakeAmbassadorHost{ + { + name: "fake1", + namespace: "testing1", + hostname: "fake1.org", + annotations: map[string]string{ + "external-dns.ambassador-service": "emissary-ingress/emissary", + "kubernetes.io/ingress.class": "external-ingress", + }, + labels: map[string]string{ + "kubernetes.io/ingress.class": "external-ingress", + }, + }, + }, + expected: []*endpoint.Endpoint{ + { + DNSName: "fake1.org", + Targets: endpoint.Targets{"8.8.8.8"}, + }, + { + DNSName: "fake1.org", + Targets: endpoint.Targets{"lb.com"}, + }, + }, + }, + { + title: "valid non matching label filter expression and valid matching annotation filter", + targetNamespace: "", + annotationFilter: "kubernetes.io/ingress.class in (external-ingress)", + labelSelector: labels.SelectorFromSet(labels.Set{"kubernetes.io/ingress.class": "external-ingress"}), + loadBalancer: fakeAmbassadorLoadBalancerService{ + ips: []string{"8.8.8.8"}, + hostnames: []string{"lb.com"}, + name: "emissary", + namespace: "emissary-ingress", + }, + ambassadorHostItems: []fakeAmbassadorHost{ + { + name: "fake1", + namespace: "testing1", + hostname: "fake1.org", + annotations: map[string]string{ + "external-dns.ambassador-service": "emissary-ingress/emissary", + "kubernetes.io/ingress.class": "external-ingress", + }, + labels: map[string]string{ + "kubernetes.io/ingress.class": "internal-ingress", + }, + }, + }, + expected: []*endpoint.Endpoint{}, + }, + { + title: "valid matching label filter expression and non matching annotation filter", + targetNamespace: "", + annotationFilter: "kubernetes.io/ingress.class in (external-ingress)", + labelSelector: labels.SelectorFromSet(labels.Set{"kubernetes.io/ingress.class": "external-ingress"}), + loadBalancer: fakeAmbassadorLoadBalancerService{ + ips: []string{"8.8.8.8"}, + hostnames: []string{"lb.com"}, + name: "emissary", + namespace: "emissary-ingress", + }, + ambassadorHostItems: []fakeAmbassadorHost{ + { + name: "fake1", + namespace: "testing1", + hostname: "fake1.org", + annotations: map[string]string{ + "external-dns.ambassador-service": "emissary-ingress/emissary", + "kubernetes.io/ingress.class": "internal-ingress", + }, + labels: map[string]string{ + "kubernetes.io/ingress.class": "external-ingress", + }, + }, + }, + expected: []*endpoint.Endpoint{}, + }, } { ti := ti t.Run(ti.title, func(t *testing.T) { @@ -345,6 +557,7 @@ func testAmbassadorSourceEndpoints(t *testing.T) { fakeKubernetesClient, ti.targetNamespace, ti.annotationFilter, + ti.labelSelector, ) require.NoError(t, err) @@ -419,6 +632,7 @@ type fakeAmbassadorHost struct { name string annotations map[string]string hostname string + labels map[string]string } func (ir fakeAmbassadorHost) Host() *ambassador.Host { @@ -431,6 +645,7 @@ func (ir fakeAmbassadorHost) Host() *ambassador.Host { Namespace: ir.namespace, Name: ir.name, Annotations: ir.annotations, + Labels: ir.labels, }, Spec: &spec, } diff --git a/source/store.go b/source/store.go index e39f4bb50a..6e63b29161 100644 --- a/source/store.go +++ b/source/store.go @@ -270,7 +270,7 @@ func BuildWithConfig(ctx context.Context, source string, p ClientGenerator, cfg if err != nil { return nil, err } - return NewAmbassadorHostSource(ctx, dynamicClient, kubernetesClient, cfg.Namespace, cfg.AnnotationFilter) + return NewAmbassadorHostSource(ctx, dynamicClient, kubernetesClient, cfg.Namespace, cfg.AnnotationFilter, cfg.LabelFilter) case "contour-httpproxy": dynamicClient, err := p.DynamicKubernetesClient() if err != nil {