From 51cf0d931c7c44218c50ca3cd55fbdcbd6727073 Mon Sep 17 00:00:00 2001 From: "Eric R. Rath" Date: Fri, 31 Jul 2020 13:17:28 -0700 Subject: [PATCH 001/117] OCI provider: add support for OCI IAM instance principal authentication Oracle Cloud Infrastructure (OCI) supports "instance princpal" authentication. From : > After you set up the required resources and policies, an application running > on an instance can call Oracle Cloud Infrastructure public services, removing > the need to configure user credentials or a configuration file. This change adds support to the OCI provider for instance principal authentication when external-dns is run on an OCI instance (e.g. in OCI OKE). Existing support for key/fingerprint-based authentication is unchanged. --- main.go | 15 ++++++++++++- pkg/apis/externaldns/types.go | 4 ++++ provider/oci/oci.go | 41 +++++++++++++++++++++++------------ provider/oci/oci_test.go | 15 ++++++++++++- 4 files changed, 59 insertions(+), 16 deletions(-) diff --git a/main.go b/main.go index 60bf72977..142c31a73 100644 --- a/main.go +++ b/main.go @@ -18,6 +18,7 @@ package main import ( "context" + "fmt" "net/http" "os" "os/signal" @@ -271,7 +272,19 @@ func main() { ) case "oci": var config *oci.OCIConfig - config, err = oci.LoadOCIConfig(cfg.OCIConfigFile) + // if the instance-principals flag was set, and a compartment OCID was provided, then ignore the + // OCI config file, and provide a config that uses instance principal authentication. + if cfg.OCIAuthInstancePrincipal { + if len(cfg.OCICompartmentOCID) == 0 { + err = fmt.Errorf("OCI IAM instance principal authentication requested, but no compartment OCID provided!") + } else { + authConfig := oci.OCIAuthConfig{UseInstancePrincipal: true} + config = &oci.OCIConfig{Auth: authConfig, CompartmentID: cfg.OCICompartmentOCID} + } + } else { + config, err = oci.LoadOCIConfig(cfg.OCIConfigFile) + } + if err == nil { p, err = oci.NewOCIProvider(*config, domainFilter, zoneIDFilter, cfg.DryRun) } diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index be557ef1e..1787ec8b6 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -97,6 +97,8 @@ type Config struct { DynPassword string `secure:"yes"` DynMinTTLSeconds int OCIConfigFile string + OCICompartmentOCID string + OCIAuthInstancePrincipal bool InMemoryZones []string OVHEndpoint string OVHApiRateLimit int @@ -358,6 +360,8 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("dyn-password", "When using the Dyn provider, specify the pasword").Default("").StringVar(&cfg.DynPassword) app.Flag("dyn-min-ttl", "Minimal TTL (in seconds) for records. This value will be used if the provided TTL for a service/ingress is lower than this.").IntVar(&cfg.DynMinTTLSeconds) app.Flag("oci-config-file", "When using the OCI provider, specify the OCI configuration file (required when --provider=oci").Default(defaultConfig.OCIConfigFile).StringVar(&cfg.OCIConfigFile) + app.Flag("oci-compartment-ocid", "When using the OCI provider, specify the OCID of the OCI compartment containing all managed zones and records. Required when using OCI IAM instance principal authentication.").StringVar(&cfg.OCICompartmentOCID) + app.Flag("oci-auth-instance-principal", "When using the OCI provider, specify whether OCI IAM instance principal authentication should be used (instead of key-based auth via the OCI config file).").Default(strconv.FormatBool(defaultConfig.OCIAuthInstancePrincipal)).BoolVar(&cfg.OCIAuthInstancePrincipal) app.Flag("rcodezero-txt-encrypt", "When using the Rcodezero provider with txt registry option, set if TXT rrs are encrypted (default: false)").Default(strconv.FormatBool(defaultConfig.RcodezeroTXTEncrypt)).BoolVar(&cfg.RcodezeroTXTEncrypt) app.Flag("inmemory-zone", "Provide a list of pre-configured zones for the inmemory provider; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.InMemoryZones) app.Flag("ovh-endpoint", "When using the OVH provider, specify the endpoint (default: ovh-eu)").Default(defaultConfig.OVHEndpoint).StringVar(&cfg.OVHEndpoint) diff --git a/provider/oci/oci.go b/provider/oci/oci.go index 759caebf1..627e29467 100644 --- a/provider/oci/oci.go +++ b/provider/oci/oci.go @@ -22,6 +22,7 @@ import ( "strings" "github.com/oracle/oci-go-sdk/common" + "github.com/oracle/oci-go-sdk/common/auth" "github.com/oracle/oci-go-sdk/dns" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -36,12 +37,13 @@ const ociRecordTTL = 300 // OCIAuthConfig holds connection parameters for the OCI API. type OCIAuthConfig struct { - Region string `yaml:"region"` - TenancyID string `yaml:"tenancy"` - UserID string `yaml:"user"` - PrivateKey string `yaml:"key"` - Fingerprint string `yaml:"fingerprint"` - Passphrase string `yaml:"passphrase"` + Region string `yaml:"region"` + TenancyID string `yaml:"tenancy"` + UserID string `yaml:"user"` + PrivateKey string `yaml:"key"` + Fingerprint string `yaml:"fingerprint"` + Passphrase string `yaml:"passphrase"` + UseInstancePrincipal bool `yaml:"useInstancePrincipal"` } // OCIConfig holds the configuration for the OCI Provider. @@ -87,14 +89,25 @@ func LoadOCIConfig(path string) (*OCIConfig, error) { // NewOCIProvider initializes a new OCI DNS based Provider. func NewOCIProvider(cfg OCIConfig, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool) (*OCIProvider, error) { var client ociDNSClient - client, err := dns.NewDnsClientWithConfigurationProvider(common.NewRawConfigurationProvider( - cfg.Auth.TenancyID, - cfg.Auth.UserID, - cfg.Auth.Region, - cfg.Auth.Fingerprint, - cfg.Auth.PrivateKey, - &cfg.Auth.Passphrase, - )) + var err error + var configProvider common.ConfigurationProvider + if cfg.Auth.UseInstancePrincipal { + configProvider, err = auth.InstancePrincipalConfigurationProvider() + if err != nil { + return nil, errors.Wrap(err, "error creating OCI instance principal config provider") + } + } else { + configProvider = common.NewRawConfigurationProvider( + cfg.Auth.TenancyID, + cfg.Auth.UserID, + cfg.Auth.Region, + cfg.Auth.Fingerprint, + cfg.Auth.PrivateKey, + &cfg.Auth.Passphrase, + ) + } + + client, err = dns.NewDnsClientWithConfigurationProvider(configProvider) if err != nil { return nil, errors.Wrap(err, "initializing OCI DNS API client") } diff --git a/provider/oci/oci_test.go b/provider/oci/oci_test.go index 5f890a4b5..437745b62 100644 --- a/provider/oci/oci_test.go +++ b/provider/oci/oci_test.go @@ -19,6 +19,7 @@ package oci import ( "context" "sort" + "strings" "testing" "github.com/oracle/oci-go-sdk/common" @@ -166,6 +167,17 @@ hKRtDhmSdWBo3tJK12RrAe4t7CUe8gMgTvU7ExlcA3xQkseFPx9K }, }, }, + "instance-principal": { + // testing the InstancePrincipalConfigurationProvider is tricky outside of an OCI context, because it tries + // to request a token from the internal OCI systems; this test-case just confirms that the expected error is + // observed, confirming that the instance-principal provider was instantiated. + config: OCIConfig{ + Auth: OCIAuthConfig{ + UseInstancePrincipal: true, + }, + }, + err: errors.New("error creating OCI instance principal config provider: failed to create a new key provider for instance principal"), + }, "invalid": { config: OCIConfig{ Auth: OCIAuthConfig{ @@ -191,7 +203,8 @@ hKRtDhmSdWBo3tJK12RrAe4t7CUe8gMgTvU7ExlcA3xQkseFPx9K if err == nil { require.NoError(t, err) } else { - require.Equal(t, tc.err.Error(), err.Error()) + // have to use prefix testing because the expected instance-principal error strings vary after a known prefix + require.Truef(t, strings.HasPrefix(err.Error(), tc.err.Error()), "observed: %s", err.Error()) } }) } From 9d90d082bdb3bb42e696d787c1e475a53eae112c Mon Sep 17 00:00:00 2001 From: "Eric R. Rath" Date: Fri, 31 Jul 2020 13:56:31 -0700 Subject: [PATCH 002/117] OCI provider: updated tutorial with info about instance principal auth --- docs/tutorials/oracle.md | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/docs/tutorials/oracle.md b/docs/tutorials/oracle.md index 98918816c..920e11395 100644 --- a/docs/tutorials/oracle.md +++ b/docs/tutorials/oracle.md @@ -6,16 +6,25 @@ Make sure to use the latest version of ExternalDNS for this tutorial. ## Creating an OCI DNS Zone -Create a DNS zone which will contain the managed DNS records. Let's use `example.com` as an reference here. +Create a DNS zone which will contain the managed DNS records. Let's use +`example.com` as a reference here. Make note of the OCID of the compartment +in which you created the zone; you'll need to provide that later. For more information about OCI DNS see the documentation [here][1]. ## Deploy ExternalDNS Connect your `kubectl` client to the cluster you want to test ExternalDNS with. +The OCI provider supports two authentication options: key-based and instance +principals. + +### Key-based + We first need to create a config file containing the information needed to connect with the OCI API. -Create a new file (oci.yaml) and modify the contents to match the example below. Be sure to adjust the values to match your own credentials: +Create a new file (oci.yaml) and modify the contents to match the example +below. Be sure to adjust the values to match your own credentials, and the OCID +of the compartment containing the zone: ```yaml auth: @@ -35,7 +44,29 @@ Create a secret using the config file above: $ kubectl create secret generic external-dns-config --from-file=oci.yaml ``` -### Manifest (for clusters with RBAC enabled) +### OCI IAM Instance Principal + +If you're running ExternalDNS within OCI, you can use OCI IAM instance +principals to authenticate with OCI. This obviates the need to create the +secret with your credentials. You'll need to ensure an OCI IAM policy exists +with a statement granting the `manage dns` permission on zones and records in +the target compartment to the dynamic group covering your instance running +ExternalDNS. +E.g.: + +``` +Allow dynamic-group to manage dns in compartment id +``` + +You'll also need to add the `--oci-instance-principals=true` flag to enable +this type of authentication. Finally, you'll need to add the +`--oci-compartment-ocid=ocid1.compartment.oc1...` flag to provide the OCID of +the compartment containing the zone to be managed. + +For more information about OCI IAM instance principals, see the documentation [here][2]. +For more information about OCI IAM policy details for the DNS service, see the documentation [here][3]. + +## Manifest (for clusters with RBAC enabled) Apply the following manifest to deploy ExternalDNS. @@ -157,3 +188,6 @@ $ kubectl apply -f nginx.yaml ``` [1]: https://docs.cloud.oracle.com/iaas/Content/DNS/Concepts/dnszonemanagement.htm +[2]: https://docs.cloud.oracle.com/iaas/Content/Identity/Reference/dnspolicyreference.htm +[3]: https://docs.cloud.oracle.com/iaas/Content/Identity/Tasks/callingservicesfrominstances.htm + From c5ddb1e1a98de5641898516cdddb488f3274137f Mon Sep 17 00:00:00 2001 From: "Eric R. Rath" Date: Fri, 31 Jul 2020 14:02:50 -0700 Subject: [PATCH 003/117] added entry to changelog under 'unreleased' --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0893ab6d..5caddf947 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - Fix: add serviceaccount name in kustomize deployment (#1689) @jmthvt - Updates Oracle OCI SDK to latest (#1687) @ericrrath - UltraDNS Provider (#1635) @kbhandari +- Oracle OCI provider: add support for instance principal authentication (#1700) @ericrrath ## v0.7.2 - 2020-06-03 From 07cfb7fdfbc7c256fd9b7058aea95bbda9d8f8b7 Mon Sep 17 00:00:00 2001 From: "Eric R. Rath" Date: Fri, 31 Jul 2020 14:18:37 -0700 Subject: [PATCH 004/117] fixed linting error - no punc in errors --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 142c31a73..185fbc3b5 100644 --- a/main.go +++ b/main.go @@ -276,7 +276,7 @@ func main() { // OCI config file, and provide a config that uses instance principal authentication. if cfg.OCIAuthInstancePrincipal { if len(cfg.OCICompartmentOCID) == 0 { - err = fmt.Errorf("OCI IAM instance principal authentication requested, but no compartment OCID provided!") + err = fmt.Errorf("instance principal authentication requested, but no compartment OCID provided") } else { authConfig := oci.OCIAuthConfig{UseInstancePrincipal: true} config = &oci.OCIConfig{Auth: authConfig, CompartmentID: cfg.OCICompartmentOCID} From c4898b7e980035c3978876f5d434ca61b58715de Mon Sep 17 00:00:00 2001 From: Dave Salisbury Date: Sun, 18 Apr 2021 14:13:53 +1000 Subject: [PATCH 005/117] Plumb in filtering on ingress class name --- main.go | 1 + pkg/apis/externaldns/types.go | 3 +++ source/ingress.go | 32 +++++++++++++++++++++++++++++++- source/store.go | 3 ++- 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index 5843a583f..b10c3372a 100644 --- a/main.go +++ b/main.go @@ -104,6 +104,7 @@ func main() { Namespace: cfg.Namespace, AnnotationFilter: cfg.AnnotationFilter, LabelFilter: cfg.LabelFilter, + IngressClassNameFilter: cfg.IngressClassNameFilter, FQDNTemplate: cfg.FQDNTemplate, CombineFQDNAndAnnotation: cfg.CombineFQDNAndAnnotation, IgnoreHostnameAnnotation: cfg.IgnoreHostnameAnnotation, diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 89e053b86..e9153ab7c 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -53,6 +53,7 @@ type Config struct { Namespace string AnnotationFilter string LabelFilter string + IngressClassNameFilter []string FQDNTemplate string CombineFQDNAndAnnotation bool IgnoreHostnameAnnotation bool @@ -186,6 +187,7 @@ var defaultConfig = &Config{ Namespace: "", AnnotationFilter: "", LabelFilter: "", + IngressClassNameFilter: nil, FQDNTemplate: "", CombineFQDNAndAnnotation: false, IgnoreHostnameAnnotation: false, @@ -362,6 +364,7 @@ func (cfg *Config) ParseFlags(args []string) error { 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 only supported by source CRD").Default(defaultConfig.LabelFilter).StringVar(&cfg.LabelFilter) + app.Flag("ingress-class-filter", "Filter ingresses to just these ingress class(es)").StringsVar(&cfg.IngressClassNameFilter) 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/ingress.go b/source/ingress.go index e81f524d1..01b987300 100644 --- a/source/ingress.go +++ b/source/ingress.go @@ -54,6 +54,7 @@ type ingressSource struct { client kubernetes.Interface namespace string annotationFilter string + ingressClassNameFilter []string fqdnTemplate *template.Template combineFQDNAnnotation bool ignoreHostnameAnnotation bool @@ -63,7 +64,7 @@ type ingressSource struct { } // NewIngressSource creates a new ingressSource with the given config. -func NewIngressSource(kubeClient kubernetes.Interface, namespace, annotationFilter string, fqdnTemplate string, combineFqdnAnnotation bool, ignoreHostnameAnnotation bool, ignoreIngressTLSSpec bool, ignoreIngressRulesSpec bool) (Source, error) { +func NewIngressSource(kubeClient kubernetes.Interface, namespace, annotationFilter string, fqdnTemplate string, combineFqdnAnnotation bool, ignoreHostnameAnnotation bool, ignoreIngressTLSSpec bool, ignoreIngressRulesSpec bool, ingressClassNameFilter []string) (Source, error) { tmpl, err := parseTemplate(fqdnTemplate) if err != nil { return nil, err @@ -94,6 +95,7 @@ func NewIngressSource(kubeClient kubernetes.Interface, namespace, annotationFilt client: kubeClient, namespace: namespace, annotationFilter: annotationFilter, + ingressClassNameFilter: ingressClassNameFilter, fqdnTemplate: tmpl, combineFQDNAnnotation: combineFqdnAnnotation, ignoreHostnameAnnotation: ignoreHostnameAnnotation, @@ -116,6 +118,11 @@ func (sc *ingressSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, e return nil, err } + ingresses, err = sc.filterByIngressClass(ingresses) + if err != nil { + return nil, err + } + endpoints := []*endpoint.Endpoint{} for _, ing := range ingresses { @@ -210,6 +217,29 @@ func (sc *ingressSource) filterByAnnotations(ingresses []*networkv1.Ingress) ([] return filteredList, nil } +// filterByIngressClass filters a list of ingresses based on a required ingress +// class +func (sc *ingressSource) filterByIngressClass(ingresses []*v1beta1.Ingress) ([]*v1beta1.Ingress, error) { + // if no class is specified then there's nothing to do + if sc.ingressClassNameFilter == nil { + return ingresses, nil + } + + filteredList := []*v1beta1.Ingress{} + + for _, ingress := range ingresses { + for _, nameFilter := range sc.ingressClassNameFilter { + // include ingress if its annotations match the selector + if ingress.Spec.IngressClassName != nil && nameFilter == *ingress.Spec.IngressClassName { + filteredList = append(filteredList, ingress) + break + } + } + } + + return filteredList, nil +} + func (sc *ingressSource) setResourceLabel(ingress *networkv1.Ingress, endpoints []*endpoint.Endpoint) { for _, ep := range endpoints { ep.Labels[endpoint.ResourceLabelKey] = fmt.Sprintf("ingress/%s/%s", ingress.Namespace, ingress.Name) diff --git a/source/store.go b/source/store.go index 3b8372255..5cb2d47bf 100644 --- a/source/store.go +++ b/source/store.go @@ -43,6 +43,7 @@ type Config struct { Namespace string AnnotationFilter string LabelFilter string + IngressClassNameFilter []string FQDNTemplate string CombineFQDNAndAnnotation bool IgnoreHostnameAnnotation bool @@ -189,7 +190,7 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err if err != nil { return nil, err } - return NewIngressSource(client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation, cfg.IgnoreIngressTLSSpec, cfg.IgnoreIngressRulesSpec) + return NewIngressSource(client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation, cfg.IgnoreIngressTLSSpec, cfg.IgnoreIngressRulesSpec, cfg.IngressClassNameFilter) case "pod": client, err := p.KubeClient() if err != nil { From 115e2501af1e9b4dc3a37bbc08a648e9031e24c5 Mon Sep 17 00:00:00 2001 From: Dave Salisbury Date: Sun, 18 Apr 2021 16:27:01 +1000 Subject: [PATCH 006/117] Add ingressClassNameFilter testing --- source/ingress_test.go | 47 +++++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/source/ingress_test.go b/source/ingress_test.go index c9d35494c..4b22be091 100644 --- a/source/ingress_test.go +++ b/source/ingress_test.go @@ -63,6 +63,7 @@ func (suite *IngressSuite) SetupTest() { false, false, false, + []string{}, ) suite.NoError(err, "should initialize ingress source") } @@ -144,6 +145,7 @@ func TestNewIngressSource(t *testing.T) { false, false, false, + []string{}, ) if ti.expectError { assert.Error(t, err) @@ -358,6 +360,7 @@ func testIngressEndpoints(t *testing.T) { ignoreHostnameAnnotation bool ignoreIngressTLSSpec bool ignoreIngressRulesSpec bool + ingressClassNameFilter []string }{ { title: "no ingress", @@ -1169,6 +1172,33 @@ func testIngressEndpoints(t *testing.T) { }, }, }, + { + title: "ingressClassName filtering", + targetNamespace: "", + ingressClassNameFilter: []string{"public"}, + ingressItems: []fakeIngress{ + { + name: "fake-public", + namespace: namespace, + tlsdnsnames: [][]string{{"example.org"}}, + ips: []string{"1.2.3.4"}, + ingressClassName: "public", + }, + { + name: "fake-internal", + namespace: namespace, + tlsdnsnames: [][]string{{"int.example.org"}}, + ips: []string{"2.3.4.5"}, + ingressClassName: "internal", + }, + }, + expected: []*endpoint.Endpoint{ + { + DNSName: "example.org", + Targets: endpoint.Targets{"1.2.3.4"}, + }, + }, + }, } { ti := ti t.Run(ti.title, func(t *testing.T) { @@ -1189,6 +1219,7 @@ func testIngressEndpoints(t *testing.T) { ti.ignoreHostnameAnnotation, ti.ignoreIngressTLSSpec, ti.ignoreIngressRulesSpec, + ti.ingressClassNameFilter, ) // Informer cache has all of the ingresses. Retrieve and validate their endpoints. res, err := source.Endpoints(context.Background()) @@ -1204,13 +1235,14 @@ func testIngressEndpoints(t *testing.T) { // ingress specific helper functions type fakeIngress struct { - dnsnames []string - tlsdnsnames [][]string - ips []string - hostnames []string - namespace string - name string - annotations map[string]string + dnsnames []string + tlsdnsnames [][]string + ips []string + hostnames []string + namespace string + name string + annotations map[string]string + ingressClassName string } func (ing fakeIngress) Ingress() *networkv1.Ingress { @@ -1222,6 +1254,7 @@ func (ing fakeIngress) Ingress() *networkv1.Ingress { }, Spec: networkv1.IngressSpec{ Rules: []networkv1.IngressRule{}, + IngressClassName: &ing.ingressClassName, }, Status: networkv1.IngressStatus{ LoadBalancer: v1.LoadBalancerStatus{ From c823eba1392ecd493c5e33f598799a41d657ddd7 Mon Sep 17 00:00:00 2001 From: Dave Salisbury Date: Sat, 2 Oct 2021 14:34:34 +1000 Subject: [PATCH 007/117] ingress source: update code for networkv1 --- source/ingress.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/ingress.go b/source/ingress.go index 01b987300..184ce299c 100644 --- a/source/ingress.go +++ b/source/ingress.go @@ -219,13 +219,13 @@ func (sc *ingressSource) filterByAnnotations(ingresses []*networkv1.Ingress) ([] // filterByIngressClass filters a list of ingresses based on a required ingress // class -func (sc *ingressSource) filterByIngressClass(ingresses []*v1beta1.Ingress) ([]*v1beta1.Ingress, error) { +func (sc *ingressSource) filterByIngressClass(ingresses []*networkv1.Ingress) ([]*networkv1.Ingress, error) { // if no class is specified then there's nothing to do if sc.ingressClassNameFilter == nil { return ingresses, nil } - filteredList := []*v1beta1.Ingress{} + filteredList := []*networkv1.Ingress{} for _, ingress := range ingresses { for _, nameFilter := range sc.ingressClassNameFilter { From 0b6c67fe56d5dee40cafa4d2922cb80716477a07 Mon Sep 17 00:00:00 2001 From: Dave Salisbury Date: Sat, 2 Oct 2021 14:34:49 +1000 Subject: [PATCH 008/117] cli args: rename ingress-class-filter to ingress-classes --- pkg/apis/externaldns/types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index e9153ab7c..4210f1df8 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -364,7 +364,7 @@ func (cfg *Config) ParseFlags(args []string) error { 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 only supported by source CRD").Default(defaultConfig.LabelFilter).StringVar(&cfg.LabelFilter) - app.Flag("ingress-class-filter", "Filter ingresses to just these ingress class(es)").StringsVar(&cfg.IngressClassNameFilter) + app.Flag("ingress-classes", "Restrict ingress source to just these classes (defaults to any class)").StringsVar(&cfg.IngressClassNameFilter) 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) From da71b3fff860ee65e71ca5d89196e821a775ca43 Mon Sep 17 00:00:00 2001 From: Dave Salisbury Date: Sat, 2 Oct 2021 15:13:45 +1000 Subject: [PATCH 009/117] ingress source: improve class name filtering and logging --- source/ingress.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/source/ingress.go b/source/ingress.go index 184ce299c..e3aa63ff2 100644 --- a/source/ingress.go +++ b/source/ingress.go @@ -220,7 +220,7 @@ func (sc *ingressSource) filterByAnnotations(ingresses []*networkv1.Ingress) ([] // filterByIngressClass filters a list of ingresses based on a required ingress // class func (sc *ingressSource) filterByIngressClass(ingresses []*networkv1.Ingress) ([]*networkv1.Ingress, error) { - // if no class is specified then there's nothing to do + // if no class filter is specified then there's nothing to do if sc.ingressClassNameFilter == nil { return ingresses, nil } @@ -228,13 +228,22 @@ func (sc *ingressSource) filterByIngressClass(ingresses []*networkv1.Ingress) ([ filteredList := []*networkv1.Ingress{} for _, ingress := range ingresses { + // we have a filter class but this ingress doesn't have its class set + if ingress.Spec.IngressClassName == nil { + log.Debugf("Ignoring ingress %s/%s because ingressClassName is not set", ingress.Namespace, ingress.Name) + } + + var matched = false; for _, nameFilter := range sc.ingressClassNameFilter { - // include ingress if its annotations match the selector - if ingress.Spec.IngressClassName != nil && nameFilter == *ingress.Spec.IngressClassName { + if nameFilter == *ingress.Spec.IngressClassName { filteredList = append(filteredList, ingress) + matched = true; break } } + if matched == false { + log.Debugf("Ignoring ingress %s/%s because ingressClassName '%s' is not in specified list", ingress.Namespace, ingress.Name, *ingress.Spec.IngressClassName) + } } return filteredList, nil From 8da6f99857dc405bc4a0c50a7c416a86e421ea36 Mon Sep 17 00:00:00 2001 From: Dave Salisbury Date: Sat, 2 Oct 2021 15:21:37 +1000 Subject: [PATCH 010/117] Rename ingressClassNameFilter to ingressClassNames ...and update the help text to specify use more clearly --- main.go | 2 +- pkg/apis/externaldns/types.go | 6 +++--- source/ingress.go | 10 +++++----- source/ingress_test.go | 2 +- source/store.go | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/main.go b/main.go index b10c3372a..bc26e0f4d 100644 --- a/main.go +++ b/main.go @@ -104,7 +104,7 @@ func main() { Namespace: cfg.Namespace, AnnotationFilter: cfg.AnnotationFilter, LabelFilter: cfg.LabelFilter, - IngressClassNameFilter: cfg.IngressClassNameFilter, + IngressClassNames: cfg.IngressClassNames, FQDNTemplate: cfg.FQDNTemplate, CombineFQDNAndAnnotation: cfg.CombineFQDNAndAnnotation, IgnoreHostnameAnnotation: cfg.IgnoreHostnameAnnotation, diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 4210f1df8..f9df5d933 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -53,7 +53,7 @@ type Config struct { Namespace string AnnotationFilter string LabelFilter string - IngressClassNameFilter []string + IngressClassNames []string FQDNTemplate string CombineFQDNAndAnnotation bool IgnoreHostnameAnnotation bool @@ -187,7 +187,7 @@ var defaultConfig = &Config{ Namespace: "", AnnotationFilter: "", LabelFilter: "", - IngressClassNameFilter: nil, + IngressClassNames: nil, FQDNTemplate: "", CombineFQDNAndAnnotation: false, IgnoreHostnameAnnotation: false, @@ -364,7 +364,7 @@ func (cfg *Config) ParseFlags(args []string) error { 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 only supported by source CRD").Default(defaultConfig.LabelFilter).StringVar(&cfg.LabelFilter) - app.Flag("ingress-classes", "Restrict ingress source to just these classes (defaults to any class)").StringsVar(&cfg.IngressClassNameFilter) + app.Flag("ingress-class", "Require an ingress to have this class name (defaults to any class; specify multiple times to allow more than one class)").StringsVar(&cfg.IngressClassNames) 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/ingress.go b/source/ingress.go index e3aa63ff2..0f27d6bb8 100644 --- a/source/ingress.go +++ b/source/ingress.go @@ -54,7 +54,7 @@ type ingressSource struct { client kubernetes.Interface namespace string annotationFilter string - ingressClassNameFilter []string + ingressClassNames []string fqdnTemplate *template.Template combineFQDNAnnotation bool ignoreHostnameAnnotation bool @@ -64,7 +64,7 @@ type ingressSource struct { } // NewIngressSource creates a new ingressSource with the given config. -func NewIngressSource(kubeClient kubernetes.Interface, namespace, annotationFilter string, fqdnTemplate string, combineFqdnAnnotation bool, ignoreHostnameAnnotation bool, ignoreIngressTLSSpec bool, ignoreIngressRulesSpec bool, ingressClassNameFilter []string) (Source, error) { +func NewIngressSource(kubeClient kubernetes.Interface, namespace, annotationFilter string, fqdnTemplate string, combineFqdnAnnotation bool, ignoreHostnameAnnotation bool, ignoreIngressTLSSpec bool, ignoreIngressRulesSpec bool, ingressClassNames []string) (Source, error) { tmpl, err := parseTemplate(fqdnTemplate) if err != nil { return nil, err @@ -95,7 +95,7 @@ func NewIngressSource(kubeClient kubernetes.Interface, namespace, annotationFilt client: kubeClient, namespace: namespace, annotationFilter: annotationFilter, - ingressClassNameFilter: ingressClassNameFilter, + ingressClassNames: ingressClassNames, fqdnTemplate: tmpl, combineFQDNAnnotation: combineFqdnAnnotation, ignoreHostnameAnnotation: ignoreHostnameAnnotation, @@ -221,7 +221,7 @@ func (sc *ingressSource) filterByAnnotations(ingresses []*networkv1.Ingress) ([] // class func (sc *ingressSource) filterByIngressClass(ingresses []*networkv1.Ingress) ([]*networkv1.Ingress, error) { // if no class filter is specified then there's nothing to do - if sc.ingressClassNameFilter == nil { + if sc.ingressClassNames == nil { return ingresses, nil } @@ -234,7 +234,7 @@ func (sc *ingressSource) filterByIngressClass(ingresses []*networkv1.Ingress) ([ } var matched = false; - for _, nameFilter := range sc.ingressClassNameFilter { + for _, nameFilter := range sc.ingressClassNames { if nameFilter == *ingress.Spec.IngressClassName { filteredList = append(filteredList, ingress) matched = true; diff --git a/source/ingress_test.go b/source/ingress_test.go index 4b22be091..804f35fa9 100644 --- a/source/ingress_test.go +++ b/source/ingress_test.go @@ -360,7 +360,7 @@ func testIngressEndpoints(t *testing.T) { ignoreHostnameAnnotation bool ignoreIngressTLSSpec bool ignoreIngressRulesSpec bool - ingressClassNameFilter []string + ingressClassNames []string }{ { title: "no ingress", diff --git a/source/store.go b/source/store.go index 5cb2d47bf..9e5743ffb 100644 --- a/source/store.go +++ b/source/store.go @@ -43,7 +43,7 @@ type Config struct { Namespace string AnnotationFilter string LabelFilter string - IngressClassNameFilter []string + IngressClassNames []string FQDNTemplate string CombineFQDNAndAnnotation bool IgnoreHostnameAnnotation bool @@ -190,7 +190,7 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err if err != nil { return nil, err } - return NewIngressSource(client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation, cfg.IgnoreIngressTLSSpec, cfg.IgnoreIngressRulesSpec, cfg.IngressClassNameFilter) + return NewIngressSource(client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation, cfg.IgnoreIngressTLSSpec, cfg.IgnoreIngressRulesSpec, cfg.IngressClassNames) case "pod": client, err := p.KubeClient() if err != nil { From ac0c4be36ad8a308287e178f090c66cf15c64ecc Mon Sep 17 00:00:00 2001 From: Dave Salisbury Date: Sat, 2 Oct 2021 15:23:48 +1000 Subject: [PATCH 011/117] Update tests for ingress class filtering --- source/ingress_test.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/source/ingress_test.go b/source/ingress_test.go index 804f35fa9..9f1590a4c 100644 --- a/source/ingress_test.go +++ b/source/ingress_test.go @@ -1175,7 +1175,7 @@ func testIngressEndpoints(t *testing.T) { { title: "ingressClassName filtering", targetNamespace: "", - ingressClassNameFilter: []string{"public"}, + ingressClassNames: []string{"public", "dmz"}, ingressItems: []fakeIngress{ { name: "fake-public", @@ -1191,12 +1191,23 @@ func testIngressEndpoints(t *testing.T) { ips: []string{"2.3.4.5"}, ingressClassName: "internal", }, + { + name: "fake-dmz", + namespace: namespace, + tlsdnsnames: [][]string{{"dmz.example.org"}}, + ips: []string{"3.4.5.6"}, + ingressClassName: "dmz", + }, }, expected: []*endpoint.Endpoint{ { DNSName: "example.org", Targets: endpoint.Targets{"1.2.3.4"}, }, + { + DNSName: "dmz.example.org", + Targets: endpoint.Targets{"3.4.5.6"}, + }, }, }, } { @@ -1219,7 +1230,7 @@ func testIngressEndpoints(t *testing.T) { ti.ignoreHostnameAnnotation, ti.ignoreIngressTLSSpec, ti.ignoreIngressRulesSpec, - ti.ingressClassNameFilter, + ti.ingressClassNames, ) // Informer cache has all of the ingresses. Retrieve and validate their endpoints. res, err := source.Endpoints(context.Background()) From 71a672fe720c5ca99e7b019e086ceb563ec52560 Mon Sep 17 00:00:00 2001 From: Dave Salisbury Date: Sat, 2 Oct 2021 16:02:33 +1000 Subject: [PATCH 012/117] ingress source: check for duplicate classname configs we don't want to get incompatible restrictions by letting someone set "--ingress-class" and --annotation-filter="kubernetes.io/ingress.class" at the same time --- source/ingress.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/source/ingress.go b/source/ingress.go index 0f27d6bb8..bb0bf2961 100644 --- a/source/ingress.go +++ b/source/ingress.go @@ -18,6 +18,7 @@ package source import ( "context" + "errors" "fmt" "sort" "strings" @@ -70,6 +71,21 @@ func NewIngressSource(kubeClient kubernetes.Interface, namespace, annotationFilt return nil, err } + // ensure that ingress class is only set in either the ingressClassNames or + // annotationFilter but not both + if ingressClassNames != nil && annotationFilter != "" { + selector, err := getLabelSelector(annotationFilter) + if err != nil { + return nil, err + } + + requirements, _ := selector.Requirements() + for _, requirement := range requirements { + if requirement.Key() == "kubernetes.io/ingress.class" { + return nil, errors.New("--ingress-class is mutually exclusive with kubernetes.io/ingress.class annotation") + } + } + } // Use shared informer to listen for add/update/delete of ingresses in the specified namespace. // Set resync period to 0, to prevent processing when nothing has changed. informerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 0, kubeinformers.WithNamespace(namespace)) From 901effbca5207e2f8e49d6e2e066be9e8470b0be Mon Sep 17 00:00:00 2001 From: Dave Salisbury Date: Sat, 2 Oct 2021 16:32:40 +1000 Subject: [PATCH 013/117] ingress source: ingressClassNames now feed into annotation filter --- source/ingress.go | 27 +++++++++++++++++++-------- source/ingress_test.go | 13 +++++++++++++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/source/ingress.go b/source/ingress.go index bb0bf2961..58fb119be 100644 --- a/source/ingress.go +++ b/source/ingress.go @@ -27,6 +27,7 @@ import ( log "github.com/sirupsen/logrus" networkv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/selection" "k8s.io/apimachinery/pkg/util/wait" kubeinformers "k8s.io/client-go/informers" netinformers "k8s.io/client-go/informers/networking/v1" @@ -241,24 +242,34 @@ func (sc *ingressSource) filterByIngressClass(ingresses []*networkv1.Ingress) ([ return ingresses, nil } + classNameReq, err := labels.NewRequirement("kubernetes.io/ingress.class", selection.In, sc.ingressClassNames) + if err != nil { + return nil, errors.New("Failed to create selector requirement from ingress class names") + } + + selector := labels.NewSelector() + selector = selector.Add(*classNameReq) + filteredList := []*networkv1.Ingress{} for _, ingress := range ingresses { - // we have a filter class but this ingress doesn't have its class set - if ingress.Spec.IngressClassName == nil { - log.Debugf("Ignoring ingress %s/%s because ingressClassName is not set", ingress.Namespace, ingress.Name) - } - var matched = false; + for _, nameFilter := range sc.ingressClassNames { - if nameFilter == *ingress.Spec.IngressClassName { - filteredList = append(filteredList, ingress) + if ingress.Spec.IngressClassName != nil && nameFilter == *ingress.Spec.IngressClassName { matched = true; + } else if matchLabelSelector(selector, ingress.Annotations) { + matched = true; + } + + if matched == true { + filteredList = append(filteredList, ingress) break } } + if matched == false { - log.Debugf("Ignoring ingress %s/%s because ingressClassName '%s' is not in specified list", ingress.Namespace, ingress.Name, *ingress.Spec.IngressClassName) + log.Debugf("Discarding ingress %s/%s because it does not match required ingress classes %v", ingress.Namespace, ingress.Name, sc.ingressClassNames) } } diff --git a/source/ingress_test.go b/source/ingress_test.go index 9f1590a4c..9cf60d963 100644 --- a/source/ingress_test.go +++ b/source/ingress_test.go @@ -1198,6 +1198,15 @@ func testIngressEndpoints(t *testing.T) { ips: []string{"3.4.5.6"}, ingressClassName: "dmz", }, + { + name: "annotated-dmz", + namespace: namespace, + tlsdnsnames: [][]string{{"annodmz.example.org"}}, + ips: []string{"4.5.6.7"}, + annotations: map[string]string{ + "kubernetes.io/ingress.class": "dmz", + }, + }, }, expected: []*endpoint.Endpoint{ { @@ -1208,6 +1217,10 @@ func testIngressEndpoints(t *testing.T) { DNSName: "dmz.example.org", Targets: endpoint.Targets{"3.4.5.6"}, }, + { + DNSName: "annodmz.example.org", + Targets: endpoint.Targets{"4.5.6.7"}, + }, }, }, } { From 807e213590b01e01e52060f564e63fbb8ce7bba5 Mon Sep 17 00:00:00 2001 From: Dave Salisbury Date: Sat, 2 Oct 2021 16:37:02 +1000 Subject: [PATCH 014/117] docs: add info about new --ingress-class --- docs/faq.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 67662a782..2ce8ab48a 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -254,13 +254,16 @@ Sometimes you need to run an internal and an external dns service. The internal one should provision hostnames used on the internal network (perhaps inside a VPC), and the external one to expose DNS to the internet. -To do this with ExternalDNS you can use the `--annotation-filter` to specifically tie an instance of ExternalDNS to +To do this with ExternalDNS you can use the `--ingress-class` to specifically tie an instance of ExternalDNS to an instance of a ingress controller. Let's assume you have two ingress controllers `nginx-internal` and `nginx-external` -then you can start two ExternalDNS providers one with `--annotation-filter=kubernetes.io/ingress.class in (nginx-internal)` -and one with `--annotation-filter=kubernetes.io/ingress.class in (nginx-external)`. +then you can start two ExternalDNS providers one with `--ingress-class=nginx-internal` and one with `--ingress-class=nginx-external`. -If you need to search for multiple values of said annotation, you can provide a comma separated list, like so: -`--annotation-filter=kubernetes.io/ingress.class in (nginx-internal, alb-ingress-internal)`. +If you need to search for multiple ingress classes, you can specify the argument multiple times, like so: +`--ingress-class=nginx-internal --ingress-class=alb-ingress-internal`. + +The `--ingress-class` argument will check both the ingressClassName field as well as the deprecated `kubernetes.io/ingress.class` annotation. + +Note: the `--ingress-class` argument cannot be used at the same time as a `kubernetes.io/ingress.class` annotation filter; if you do this an error will be raised. Beware when using multiple sources, e.g. `--source=service --source=ingress`, `--annotation-filter` will filter every given source objects. If you need to filter only one specific source you have to run a separated external dns service containing only the wanted `--source` and `--annotation-filter`. From 8ec342a1c6ac3c95540ee87c4f3c94afe9f15635 Mon Sep 17 00:00:00 2001 From: Dave Salisbury Date: Sat, 2 Oct 2021 16:41:39 +1000 Subject: [PATCH 015/117] docs: format ingressClassName as inline code --- docs/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.md b/docs/faq.md index 2ce8ab48a..2ce3ed481 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -261,7 +261,7 @@ then you can start two ExternalDNS providers one with `--ingress-class=nginx-int If you need to search for multiple ingress classes, you can specify the argument multiple times, like so: `--ingress-class=nginx-internal --ingress-class=alb-ingress-internal`. -The `--ingress-class` argument will check both the ingressClassName field as well as the deprecated `kubernetes.io/ingress.class` annotation. +The `--ingress-class` argument will check both the `ingressClassName` field as well as the deprecated `kubernetes.io/ingress.class` annotation. Note: the `--ingress-class` argument cannot be used at the same time as a `kubernetes.io/ingress.class` annotation filter; if you do this an error will be raised. From a24217a720278d84eee2d9881e952f87a3d307e1 Mon Sep 17 00:00:00 2001 From: Dave Salisbury Date: Sat, 2 Oct 2021 16:49:51 +1000 Subject: [PATCH 016/117] ingress source: use shorthand bool comparison --- source/ingress.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/ingress.go b/source/ingress.go index 58fb119be..b92e4475d 100644 --- a/source/ingress.go +++ b/source/ingress.go @@ -262,13 +262,13 @@ func (sc *ingressSource) filterByIngressClass(ingresses []*networkv1.Ingress) ([ matched = true; } - if matched == true { + if matched { filteredList = append(filteredList, ingress) break } } - if matched == false { + if !matched { log.Debugf("Discarding ingress %s/%s because it does not match required ingress classes %v", ingress.Namespace, ingress.Name, sc.ingressClassNames) } } From 0aa3d55450cf06161583156ff63b61c73473fff2 Mon Sep 17 00:00:00 2001 From: Dave Salisbury Date: Sat, 2 Oct 2021 16:50:35 +1000 Subject: [PATCH 017/117] ingress source: pass back err when requirement fails --- source/ingress.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/ingress.go b/source/ingress.go index b92e4475d..371ba9248 100644 --- a/source/ingress.go +++ b/source/ingress.go @@ -244,7 +244,7 @@ func (sc *ingressSource) filterByIngressClass(ingresses []*networkv1.Ingress) ([ classNameReq, err := labels.NewRequirement("kubernetes.io/ingress.class", selection.In, sc.ingressClassNames) if err != nil { - return nil, errors.New("Failed to create selector requirement from ingress class names") + return nil, err } selector := labels.NewSelector() From 6c4a450b254bdfc23f4243f6bef657e406ecb028 Mon Sep 17 00:00:00 2001 From: Dave Salisbury Date: Sat, 2 Oct 2021 16:54:42 +1000 Subject: [PATCH 018/117] gofmt simplify new ingress-class changes --- pkg/apis/externaldns/types.go | 2 +- source/ingress.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index f9df5d933..a59e356be 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -364,7 +364,7 @@ func (cfg *Config) ParseFlags(args []string) error { 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 only supported by source CRD").Default(defaultConfig.LabelFilter).StringVar(&cfg.LabelFilter) - app.Flag("ingress-class", "Require an ingress to have this class name (defaults to any class; specify multiple times to allow more than one class)").StringsVar(&cfg.IngressClassNames) + app.Flag("ingress-class", "Require an ingress to have this class name (defaults to any class; specify multiple times to allow more than one class)").StringsVar(&cfg.IngressClassNames) 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/ingress.go b/source/ingress.go index 371ba9248..33a16c3b8 100644 --- a/source/ingress.go +++ b/source/ingress.go @@ -56,7 +56,7 @@ type ingressSource struct { client kubernetes.Interface namespace string annotationFilter string - ingressClassNames []string + ingressClassNames []string fqdnTemplate *template.Template combineFQDNAnnotation bool ignoreHostnameAnnotation bool @@ -135,7 +135,7 @@ func (sc *ingressSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, e return nil, err } - ingresses, err = sc.filterByIngressClass(ingresses) + ingresses, err = sc.filterByIngressClass(ingresses) if err != nil { return nil, err } @@ -253,13 +253,13 @@ func (sc *ingressSource) filterByIngressClass(ingresses []*networkv1.Ingress) ([ filteredList := []*networkv1.Ingress{} for _, ingress := range ingresses { - var matched = false; + var matched = false for _, nameFilter := range sc.ingressClassNames { if ingress.Spec.IngressClassName != nil && nameFilter == *ingress.Spec.IngressClassName { - matched = true; + matched = true } else if matchLabelSelector(selector, ingress.Annotations) { - matched = true; + matched = true } if matched { From 31bc5bb0778d9dd06ab93b0c26b08fd50eb5ddba Mon Sep 17 00:00:00 2001 From: Dave Salisbury Date: Sat, 2 Oct 2021 17:08:54 +1000 Subject: [PATCH 019/117] ingress source: fix broken NewIngressSource test and add an extra one for the mutual exclusivity of ingressClassNames and ingress.class annotationFilters --- source/ingress_test.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/source/ingress_test.go b/source/ingress_test.go index 9cf60d963..f79a7b222 100644 --- a/source/ingress_test.go +++ b/source/ingress_test.go @@ -100,6 +100,7 @@ func TestNewIngressSource(t *testing.T) { fqdnTemplate string combineFQDNAndAnnotation bool expectError bool + ingressClassNames []string }{ { title: "invalid template", @@ -131,6 +132,17 @@ func TestNewIngressSource(t *testing.T) { expectError: false, annotationFilter: "kubernetes.io/ingress.class=nginx", }, + { + title: "non-empty ingress class name list", + expectError: false, + ingressClassNames: []string{"internal", "external"}, + }, + { + title: "ingress class name and annotation filter jointly specified", + expectError: true, + ingressClassNames: []string{"internal", "external"}, + annotationFilter: "kubernetes.io/ingress.class=nginx", + }, } { ti := ti t.Run(ti.title, func(t *testing.T) { @@ -145,7 +157,7 @@ func TestNewIngressSource(t *testing.T) { false, false, false, - []string{}, + ti.ingressClassNames, ) if ti.expectError { assert.Error(t, err) From d9bf8fb32998cac65527cf0d0cd8bc5f513079db Mon Sep 17 00:00:00 2001 From: "Eric R. Rath" Date: Fri, 6 Aug 2021 15:02:22 -0700 Subject: [PATCH 020/117] CRD source: add event-handler support When the --events flag is passed at startup, Source.AddEventHandler() is called on each configured source. Most sources provide AddEventHandler() implementations that invoke the reconciliation loop when the configured source changes, but the CRD source had a no-op implementation. I.e. when a custom resource was created, updated, or deleted, external-dns remained unware, and the reconciliation loop would not fire until the configured interval had passed. This change adds an informer (on the CRD specified by --crd-source-apiversion and --crd-source-kind=DNSEndpoint), and a Source.AddEventHandler() implementation that calls Informer.AddEventHandler(). Now when a custom resource is created, updated, or deleted, the reconciliation loop is invoked. --- source/crd.go | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/source/crd.go b/source/crd.go index 705d770bc..b4d8cf3c6 100644 --- a/source/crd.go +++ b/source/crd.go @@ -19,6 +19,9 @@ package source import ( "context" "fmt" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/tools/cache" "os" "strings" @@ -44,6 +47,7 @@ type crdSource struct { codec runtime.ParameterCodec annotationFilter string labelSelector labels.Selector + informer *cache.SharedInformer } func addKnownTypes(scheme *runtime.Scheme, groupVersion schema.GroupVersion) error { @@ -104,17 +108,51 @@ func NewCRDClientForAPIVersionKind(client kubernetes.Interface, kubeConfig, apiS // NewCRDSource creates a new crdSource with the given config. func NewCRDSource(crdClient rest.Interface, namespace, kind string, annotationFilter string, labelSelector labels.Selector, scheme *runtime.Scheme) (Source, error) { - return &crdSource{ + sourceCrd := crdSource{ crdResource: strings.ToLower(kind) + "s", namespace: namespace, annotationFilter: annotationFilter, labelSelector: labelSelector, crdClient: crdClient, codec: runtime.NewParameterCodec(scheme), - }, nil + } + // external-dns already runs its sync-handler periodically (controlled by `--interval` flag) to ensure any + // missed or dropped events are handled. specify a resync period 0 to avoid unnecessary sync handler invocations. + informer := cache.NewSharedInformer( + &cache.ListWatch{ + ListFunc: func(lo metav1.ListOptions) (result runtime.Object, err error) { + return sourceCrd.List(context.TODO(), &lo) + }, + WatchFunc: func(lo metav1.ListOptions) (watch.Interface, error) { + return sourceCrd.watch(context.TODO(), &lo) + }, + }, + &endpoint.DNSEndpoint{}, + 0) + sourceCrd.informer = &informer + go informer.Run(wait.NeverStop) + return &sourceCrd, nil } func (cs *crdSource) AddEventHandler(ctx context.Context, handler func()) { + log.Debug("Adding event handler for CRD") + + // Right now there is no way to remove event handler from informer, see: + // https://github.com/kubernetes/kubernetes/issues/79610 + informer := *cs.informer + informer.AddEventHandler( + cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + handler() + }, + UpdateFunc: func(old interface{}, new interface{}) { + handler() + }, + DeleteFunc: func(obj interface{}) { + handler() + }, + }, + ) } // Endpoints returns endpoint objects. @@ -189,6 +227,15 @@ func (cs *crdSource) setResourceLabel(crd *endpoint.DNSEndpoint, endpoints []*en } } +func (cs *crdSource) watch(ctx context.Context, opts *metav1.ListOptions) (watch.Interface, error) { + opts.Watch = true + return cs.crdClient.Get(). + Namespace(cs.namespace). + Resource(cs.crdResource). + VersionedParams(opts, cs.codec). + Watch(ctx) +} + func (cs *crdSource) List(ctx context.Context, opts *metav1.ListOptions) (result *endpoint.DNSEndpointList, err error) { result = &endpoint.DNSEndpointList{} err = cs.crdClient.Get(). From 5146dab6fa739be3860b30881d495ea83b77acb7 Mon Sep 17 00:00:00 2001 From: "Eric R. Rath" Date: Thu, 12 Aug 2021 12:32:15 -0700 Subject: [PATCH 021/117] ran goimports on crd.go to fix linter warning --- source/crd.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/crd.go b/source/crd.go index b4d8cf3c6..c15227592 100644 --- a/source/crd.go +++ b/source/crd.go @@ -19,11 +19,12 @@ package source import ( "context" "fmt" + "os" + "strings" + "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/tools/cache" - "os" - "strings" log "github.com/sirupsen/logrus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" From 585d752ca4921ad02a1fd54e203672ecc1dc1a1c Mon Sep 17 00:00:00 2001 From: "Eric R. Rath" Date: Fri, 13 Aug 2021 11:45:22 -0700 Subject: [PATCH 022/117] Don't run CRD informer during tests This change disables the CRD source's informer during tests. I made the mistake of not running `make test` before the previous commit, and thus didn't realize that leaving the informer enabled during the tests introduced a race condition: WARNING: DATA RACE Write at 0x00c0005aa130 by goroutine 59: k8s.io/client-go/rest/fake.(*RESTClient).do() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/rest/fake/fake.go:113 +0x69 k8s.io/client-go/rest/fake.(*RESTClient).do-fm() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/rest/fake/fake.go:109 +0x64 k8s.io/client-go/rest/fake.roundTripperFunc.RoundTrip() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/rest/fake/fake.go:43 +0x3d net/http.send() /usr/local/go/src/net/http/client.go:251 +0x6da net/http.(*Client).send() /usr/local/go/src/net/http/client.go:175 +0x1d5 net/http.(*Client).do() /usr/local/go/src/net/http/client.go:717 +0x2cb net/http.(*Client).Do() /usr/local/go/src/net/http/client.go:585 +0x68b k8s.io/client-go/rest.(*Request).request() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/rest/request.go:855 +0x209 k8s.io/client-go/rest.(*Request).Do() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/rest/request.go:928 +0xf0 sigs.k8s.io/external-dns/source.(*crdSource).List() /Users/erath/go/src/github.com/ericrrath/external-dns/source/crd.go:250 +0x28c sigs.k8s.io/external-dns/source.NewCRDSource.func1() /Users/erath/go/src/github.com/ericrrath/external-dns/source/crd.go:125 +0x10a k8s.io/client-go/tools/cache.(*ListWatch).List() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/tools/cache/listwatch.go:106 +0x94 k8s.io/client-go/tools/cache.(*Reflector).ListAndWatch.func1.1.2() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/tools/cache/reflector.go:233 +0xf4 k8s.io/client-go/tools/pager.SimplePageFunc.func1() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/tools/pager/pager.go:40 +0x94 k8s.io/client-go/tools/pager.(*ListPager).List() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/tools/pager/pager.go:91 +0x1f4 k8s.io/client-go/tools/cache.(*Reflector).ListAndWatch.func1.1() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/tools/cache/reflector.go:258 +0x2b7 Previous write at 0x00c0005aa130 by goroutine 37: k8s.io/client-go/rest/fake.(*RESTClient).do() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/rest/fake/fake.go:113 +0x69 k8s.io/client-go/rest/fake.(*RESTClient).do-fm() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/rest/fake/fake.go:109 +0x64 k8s.io/client-go/rest/fake.roundTripperFunc.RoundTrip() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/rest/fake/fake.go:43 +0x3d net/http.send() /usr/local/go/src/net/http/client.go:251 +0x6da net/http.(*Client).send() /usr/local/go/src/net/http/client.go:175 +0x1d5 net/http.(*Client).do() /usr/local/go/src/net/http/client.go:717 +0x2cb net/http.(*Client).Do() /usr/local/go/src/net/http/client.go:585 +0x68b k8s.io/client-go/rest.(*Request).request() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/rest/request.go:855 +0x209 k8s.io/client-go/rest.(*Request).Do() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/rest/request.go:928 +0xf0 sigs.k8s.io/external-dns/source.(*crdSource).List() /Users/erath/go/src/github.com/ericrrath/external-dns/source/crd.go:250 +0x28c sigs.k8s.io/external-dns/source.(*crdSource).Endpoints() /Users/erath/go/src/github.com/ericrrath/external-dns/source/crd.go:171 +0x13c4 sigs.k8s.io/external-dns/source.testCRDSourceEndpoints.func1() /Users/erath/go/src/github.com/ericrrath/external-dns/source/crd_test.go:388 +0x4f6 testing.tRunner() /usr/local/go/src/testing/testing.go:1193 +0x202 Goroutine 59 (running) created at: k8s.io/client-go/tools/cache.(*Reflector).ListAndWatch.func1() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/tools/cache/reflector.go:224 +0x36f k8s.io/client-go/tools/cache.(*Reflector).ListAndWatch() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/tools/cache/reflector.go:316 +0x1ab k8s.io/client-go/tools/cache.(*Reflector).Run.func1() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/tools/cache/reflector.go:177 +0x4a k8s.io/apimachinery/pkg/util/wait.BackoffUntil.func1() /Users/erath/go/pkg/mod/k8s.io/apimachinery@v0.18.8/pkg/util/wait/wait.go:155 +0x75 k8s.io/apimachinery/pkg/util/wait.BackoffUntil() /Users/erath/go/pkg/mod/k8s.io/apimachinery@v0.18.8/pkg/util/wait/wait.go:156 +0xba k8s.io/client-go/tools/cache.(*Reflector).Run() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/tools/cache/reflector.go:176 +0xee k8s.io/client-go/tools/cache.(*Reflector).Run-fm() /Users/erath/go/pkg/mod/k8s.io/client-go@v0.18.8/tools/cache/reflector.go:174 +0x54 k8s.io/apimachinery/pkg/util/wait.(*Group).StartWithChannel.func1() /Users/erath/go/pkg/mod/k8s.io/apimachinery@v0.18.8/pkg/util/wait/wait.go:56 +0x45 k8s.io/apimachinery/pkg/util/wait.(*Group).Start.func1() /Users/erath/go/pkg/mod/k8s.io/apimachinery@v0.18.8/pkg/util/wait/wait.go:73 +0x6d Goroutine 37 (running) created at: testing.(*T).Run() /usr/local/go/src/testing/testing.go:1238 +0x5d7 sigs.k8s.io/external-dns/source.testCRDSourceEndpoints() /Users/erath/go/src/github.com/ericrrath/external-dns/source/crd_test.go:376 +0x1fcf testing.tRunner() /usr/local/go/src/testing/testing.go:1193 +0x202 It looks like client-go's fake.RESTClient (used by crd_test.go) is known to cause race conditions when used with informers: . None of the CRD tests _depend_ on the informer yet, so disabling the informer at least allows the existing tests to pass without race conditions. I'll look into further changes that 1) test the new event-handler behavior, and 2) allow all tests to pass without race conditions. --- source/crd.go | 67 ++++++++++++++++++++++++---------------------- source/crd_test.go | 2 +- source/store.go | 2 +- 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/source/crd.go b/source/crd.go index c15227592..b32b93e6e 100644 --- a/source/crd.go +++ b/source/crd.go @@ -108,7 +108,7 @@ func NewCRDClientForAPIVersionKind(client kubernetes.Interface, kubeConfig, apiS } // NewCRDSource creates a new crdSource with the given config. -func NewCRDSource(crdClient rest.Interface, namespace, kind string, annotationFilter string, labelSelector labels.Selector, scheme *runtime.Scheme) (Source, error) { +func NewCRDSource(crdClient rest.Interface, namespace, kind string, annotationFilter string, labelSelector labels.Selector, scheme *runtime.Scheme, startInformer bool) (Source, error) { sourceCrd := crdSource{ crdResource: strings.ToLower(kind) + "s", namespace: namespace, @@ -117,43 +117,46 @@ func NewCRDSource(crdClient rest.Interface, namespace, kind string, annotationFi crdClient: crdClient, codec: runtime.NewParameterCodec(scheme), } - // external-dns already runs its sync-handler periodically (controlled by `--interval` flag) to ensure any - // missed or dropped events are handled. specify a resync period 0 to avoid unnecessary sync handler invocations. - informer := cache.NewSharedInformer( - &cache.ListWatch{ - ListFunc: func(lo metav1.ListOptions) (result runtime.Object, err error) { - return sourceCrd.List(context.TODO(), &lo) + if startInformer { + // external-dns already runs its sync-handler periodically (controlled by `--interval` flag) to ensure any + // missed or dropped events are handled. specify a resync period 0 to avoid unnecessary sync handler invocations. + informer := cache.NewSharedInformer( + &cache.ListWatch{ + ListFunc: func(lo metav1.ListOptions) (result runtime.Object, err error) { + return sourceCrd.List(context.TODO(), &lo) + }, + WatchFunc: func(lo metav1.ListOptions) (watch.Interface, error) { + return sourceCrd.watch(context.TODO(), &lo) + }, }, - WatchFunc: func(lo metav1.ListOptions) (watch.Interface, error) { - return sourceCrd.watch(context.TODO(), &lo) - }, - }, - &endpoint.DNSEndpoint{}, - 0) - sourceCrd.informer = &informer - go informer.Run(wait.NeverStop) + &endpoint.DNSEndpoint{}, + 0) + sourceCrd.informer = &informer + go informer.Run(wait.NeverStop) + } return &sourceCrd, nil } func (cs *crdSource) AddEventHandler(ctx context.Context, handler func()) { - log.Debug("Adding event handler for CRD") - - // Right now there is no way to remove event handler from informer, see: - // https://github.com/kubernetes/kubernetes/issues/79610 - informer := *cs.informer - informer.AddEventHandler( - cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - handler() + if cs.informer != nil { + log.Debug("Adding event handler for CRD") + // Right now there is no way to remove event handler from informer, see: + // https://github.com/kubernetes/kubernetes/issues/79610 + informer := *cs.informer + informer.AddEventHandler( + cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + handler() + }, + UpdateFunc: func(old interface{}, new interface{}) { + handler() + }, + DeleteFunc: func(obj interface{}) { + handler() + }, }, - UpdateFunc: func(old interface{}, new interface{}) { - handler() - }, - DeleteFunc: func(obj interface{}) { - handler() - }, - }, - ) + ) + } } // Endpoints returns endpoint objects. diff --git a/source/crd_test.go b/source/crd_test.go index a88fb4616..55b375f21 100644 --- a/source/crd_test.go +++ b/source/crd_test.go @@ -387,7 +387,7 @@ func testCRDSourceEndpoints(t *testing.T) { labelSelector, err := labels.Parse(ti.labelFilter) require.NoError(t, err) - cs, err := NewCRDSource(restClient, ti.namespace, ti.kind, ti.annotationFilter, labelSelector, scheme) + cs, err := NewCRDSource(restClient, ti.namespace, ti.kind, ti.annotationFilter, labelSelector, scheme, false) require.NoError(t, err) receivedEndpoints, err := cs.Endpoints(context.Background()) diff --git a/source/store.go b/source/store.go index 942400aef..3f15572d0 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 NewCRDSource(crdClient, cfg.Namespace, cfg.CRDSourceKind, cfg.AnnotationFilter, cfg.LabelFilter, scheme) + return NewCRDSource(crdClient, cfg.Namespace, cfg.CRDSourceKind, cfg.AnnotationFilter, cfg.LabelFilter, scheme, true) case "skipper-routegroup": apiServerURL := cfg.APIServerURL tokenPath := "" From 56a8d60fff0555443e965f26505070f832d4e071 Mon Sep 17 00:00:00 2001 From: "Eric R. Rath" Date: Thu, 2 Sep 2021 09:21:21 -0700 Subject: [PATCH 023/117] Review feedback njuettner suggested using a var instead of boolean literals for the startInformer arg to NewCRDSource; good idea. --- source/crd_test.go | 7 ++++++- source/store.go | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/source/crd_test.go b/source/crd_test.go index 55b375f21..e285a80da 100644 --- a/source/crd_test.go +++ b/source/crd_test.go @@ -387,7 +387,12 @@ func testCRDSourceEndpoints(t *testing.T) { labelSelector, err := labels.Parse(ti.labelFilter) require.NoError(t, err) - cs, err := NewCRDSource(restClient, ti.namespace, ti.kind, ti.annotationFilter, labelSelector, scheme, false) + // At present, client-go's fake.RESTClient (used by crd_test.go) is known to cause race conditions when used + // with informers: https://github.com/kubernetes/kubernetes/issues/95372 + // So don't start the informer during testing. + startInformer := false + + cs, err := NewCRDSource(restClient, ti.namespace, ti.kind, ti.annotationFilter, labelSelector, scheme, startInformer) require.NoError(t, err) receivedEndpoints, err := cs.Endpoints(context.Background()) diff --git a/source/store.go b/source/store.go index 3f15572d0..da7ce2a65 100644 --- a/source/store.go +++ b/source/store.go @@ -270,7 +270,8 @@ func BuildWithConfig(ctx context.Context, source string, p ClientGenerator, cfg if err != nil { return nil, err } - return NewCRDSource(crdClient, cfg.Namespace, cfg.CRDSourceKind, cfg.AnnotationFilter, cfg.LabelFilter, scheme, true) + startInformer := true + return NewCRDSource(crdClient, cfg.Namespace, cfg.CRDSourceKind, cfg.AnnotationFilter, cfg.LabelFilter, scheme, startInformer) case "skipper-routegroup": apiServerURL := cfg.APIServerURL tokenPath := "" From 99c10f56c634ffcaaa6a4f775d156fbdd81222c9 Mon Sep 17 00:00:00 2001 From: Dave Salisbury Date: Mon, 14 Feb 2022 16:37:28 +1100 Subject: [PATCH 024/117] ingress source: improve mutual exclusive error message --- source/ingress.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/ingress.go b/source/ingress.go index 005739210..56833e15c 100644 --- a/source/ingress.go +++ b/source/ingress.go @@ -83,7 +83,7 @@ func NewIngressSource(ctx context.Context, kubeClient kubernetes.Interface, name requirements, _ := selector.Requirements() for _, requirement := range requirements { if requirement.Key() == "kubernetes.io/ingress.class" { - return nil, errors.New("--ingress-class is mutually exclusive with kubernetes.io/ingress.class annotation") + return nil, errors.New("--ingress-class is mutually exclusive with the kubernetes.io/ingress.class annotation filter") } } } From 15f27aafd3517531978685a3039a054df2c0b985 Mon Sep 17 00:00:00 2001 From: Dave Salisbury Date: Mon, 14 Feb 2022 16:48:56 +1100 Subject: [PATCH 025/117] faq: extend notes about annotation filter risks --- docs/faq.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 9cda9c59f..10a36422f 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -269,10 +269,12 @@ The `--ingress-class` argument will check both the `ingressClassName` field as w Note: the `--ingress-class` argument cannot be used at the same time as a `kubernetes.io/ingress.class` annotation filter; if you do this an error will be raised. -Beware when using multiple sources, e.g. `--source=service --source=ingress`, `--annotation-filter` will filter every given source objects. -If you need to filter only one specific source you have to run a separated external dns service containing only the wanted `--source` and `--annotation-filter`. +If you use annotations to indicate different ingress classes in your cluster, you can instead use an `--annotation-filter` argument to restrict which objects ExternalDNS considers; for example, `--annotation-filter=kubernetes.io/ingress.class in (public,dmz)`. -**Note:** Filtering based on annotation means that the external-dns controller will receive all resources of that kind and then filter on the client-side. +However, beware when using annotation filters with multiple sources, e.g. `--source=service --source=ingress`, since `--annotation-filter` will filter every given source objects. +If you need to use annotation filters against a specific source you have to run a separated external dns service containing only the wanted `--source` and `--annotation-filter`. + +**Note:** Filtering based on annotation or ingress class name means that the external-dns controller will receive all resources of that kind and then filter on the client-side. In larger clusters with many resources which change frequently this can cause performance issues. If only some resources need to be managed by an instance of external-dns then label filtering can be used instead of annotation filtering. This means that only those resources which match the selector specified in `--label-filter` will be passed to the controller. From f76382a5adb1e92e821978b6b9ee298cb9514ac0 Mon Sep 17 00:00:00 2001 From: Dave Salisbury Date: Mon, 14 Feb 2022 16:57:01 +1100 Subject: [PATCH 026/117] docs: update public-private-route53 example for ingress-class filtering --- docs/tutorials/public-private-route53.md | 32 ++++++++++++++++-------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/docs/tutorials/public-private-route53.md b/docs/tutorials/public-private-route53.md index 764a7e747..b8110ecbd 100644 --- a/docs/tutorials/public-private-route53.md +++ b/docs/tutorials/public-private-route53.md @@ -213,7 +213,7 @@ spec: Consult [AWS ExternalDNS setup docs](aws.md) for installation guidelines. -In ExternalDNS containers args, make sure to specify `annotation-filter` and `aws-zone-type`: +In ExternalDNS containers args, make sure to specify `aws-zone-type` and either `ingress-class` or `annotation-filter` (depending on whether your cluster makes use of `ingressClassName`): ```yaml apiVersion: apps/v1beta2 @@ -241,7 +241,9 @@ spec: - --provider=aws - --registry=txt - --txt-owner-id=external-dns - - --annotation-filter=kubernetes.io/ingress.class in (external-ingress) + - --ingress-class=external-ingress + # ... or, if you use annotations for ingress classes + # - --annotation-filter=kubernetes.io/ingress.class in (external-ingress) - --aws-zone-type=public image: k8s.gcr.io/external-dns/external-dns:v0.7.6 name: external-dns-public @@ -251,7 +253,7 @@ spec: Consult [AWS ExternalDNS setup docs](aws.md) for installation guidelines. -In ExternalDNS containers args, make sure to specify `annotation-filter` and `aws-zone-type`: +In ExternalDNS containers args, make sure to specify `aws-zone-type` and either `ingress-class` or `annotation-filter` (depending on whether your cluster makes use of `ingressClassName`): ```yaml apiVersion: apps/v1beta2 @@ -279,7 +281,9 @@ spec: - --provider=aws - --registry=txt - --txt-owner-id=dev.k8s.nexus - - --annotation-filter=kubernetes.io/ingress.class in (internal-ingress) + - --ingress-class=internal-ingress + # ... or, if you use annotations for ingress classes + # - --annotation-filter=kubernetes.io/ingress.class in (internal-ingress) - --aws-zone-type=private image: k8s.gcr.io/external-dns/external-dns:v0.7.6 name: external-dns-private @@ -287,20 +291,23 @@ spec: ## Create application Service definitions -For this setup to work, you've to create two Service definitions for your application. +For this setup to work, you need to create two Ingress definitions for your application. -At first, create public Service definition: +At first, create public Ingress definition (make sure to un-comment either the `annotations` or `ingressClassName` lines): ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - annotations: - kubernetes.io/ingress.class: "external-ingress" + # uncomment if you use annotations for ingress classes + # annotations: + # kubernetes.io/ingress.class: "external-ingress" labels: app: app name: app-public spec: + # uncomment if you use ingressClassName + # ingressClassName: external-ingress rules: - host: app.domain.com http: @@ -310,18 +317,21 @@ spec: servicePort: 80 ``` -Then create private Service definition: +Then create private Ingress definition (again, make sure to un-comment either the `annotations` or `ingressClassName` lines): ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - annotations: - kubernetes.io/ingress.class: "internal-ingress" + # uncomment if you use annotations for ingress classes + # annotations: + # kubernetes.io/ingress.class: "internal-ingress" labels: app: app name: app-private spec: + # uncomment if you use ingressClassName + # ingressClassName: internal-ingress rules: - host: app.domain.com http: From 27aa444b9d9449ee97f1f16d0401e8a6a914ed53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Robles=20Mart=C3=ADn?= Date: Tue, 22 Mar 2022 11:17:32 +0100 Subject: [PATCH 027/117] [AWS] Include DnsEndpoint example with AWS provider specifics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Francisco Robles Martín --- .../crd-source/dnsendpoint-aws-example.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 docs/contributing/crd-source/dnsendpoint-aws-example.yaml diff --git a/docs/contributing/crd-source/dnsendpoint-aws-example.yaml b/docs/contributing/crd-source/dnsendpoint-aws-example.yaml new file mode 100644 index 000000000..e437f0f04 --- /dev/null +++ b/docs/contributing/crd-source/dnsendpoint-aws-example.yaml @@ -0,0 +1,18 @@ +apiVersion: externaldns.k8s.io/v1alpha1 +kind: DNSEndpoint +metadata: + name: examplednsrecord +spec: + endpoints: + - dnsName: subdomain.foo.bar.com + providerSpecific: + - name: "aws/failover" + value: "PRIMARY" + - name: "aws/health-check-id" + value: "asdf1234-as12-as12-as12-asdf12345678" + - name: "aws/evaluate-target-health" + value: "true" + recordType: CNAME + setIdentifier: some-unique-id + targets: + - other-subdomain.foo.bar.com From 2d2d19f5ea3414057732d388e0272ef99e00fa83 Mon Sep 17 00:00:00 2001 From: Christoph Petrausch Date: Mon, 26 Sep 2022 16:43:42 +0200 Subject: [PATCH 028/117] Allow multiple records per A record Fetch labels from existing TXT records to fix https://github.com/kubernetes-sigs/external-dns/issues/603 --- provider/designate/designate.go | 46 ++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/provider/designate/designate.go b/provider/designate/designate.go index 68160f0b8..23338db97 100644 --- a/provider/designate/designate.go +++ b/provider/designate/designate.go @@ -350,7 +350,7 @@ type recordSet struct { } // adds endpoint into recordset aggregation, loading original values from endpoint labels first -func addEndpoint(ep *endpoint.Endpoint, recordSets map[string]*recordSet, delete bool) { +func addEndpoint(ep *endpoint.Endpoint, recordSets map[string]*recordSet, oldEndpoints []*endpoint.Endpoint, delete bool) { key := fmt.Sprintf("%s/%s", ep.DNSName, ep.RecordType) rs := recordSets[key] if rs == nil { @@ -360,6 +360,9 @@ func addEndpoint(ep *endpoint.Endpoint, recordSets map[string]*recordSet, delete names: make(map[string]bool), } } + + addDesignateIDLabelsFromExistingEndpoints(oldEndpoints, ep) + if rs.zoneID == "" { rs.zoneID = ep.Labels[designateZoneID] } @@ -381,24 +384,53 @@ func addEndpoint(ep *endpoint.Endpoint, recordSets map[string]*recordSet, delete recordSets[key] = rs } +// addDesignateIDLabelsFromExistingEndpoints adds the labels identified by the constants designateZoneID and designateRecordSetID +// to an Endpoint. Therefore, it searches all given existing endpoints for an endpoint with the same record type and record +// value. If the given Endpoint already has the labels set, they are left untouched. This fixes an issue with the +// TXTRegistry which generates new TXT entries instead of updating the old ones. +func addDesignateIDLabelsFromExistingEndpoints(existingEndpoints []*endpoint.Endpoint, ep *endpoint.Endpoint) { + _, hasZoneIDLabel := ep.Labels[designateZoneID] + _, hasRecordSetIDLabel := ep.Labels[designateRecordSetID] + if hasZoneIDLabel && hasRecordSetIDLabel { + return + } + for _, oep := range existingEndpoints { + if ep.RecordType == oep.RecordType && ep.DNSName == oep.DNSName { + if !hasZoneIDLabel { + ep.Labels[designateZoneID] = oep.Labels[designateZoneID] + } + if !hasRecordSetIDLabel { + ep.Labels[designateRecordSetID] = oep.Labels[designateRecordSetID] + } + return + } + } +} + // ApplyChanges applies a given set of changes in a given zone. func (p designateProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { managedZones, err := p.getZones() if err != nil { return err } + + endpoints, err := p.Records(ctx) recordSets := map[string]*recordSet{} for _, ep := range changes.Create { - addEndpoint(ep, recordSets, false) - } - for _, ep := range changes.UpdateNew { - addEndpoint(ep, recordSets, false) + addEndpoint(ep, recordSets, endpoints, false) } for _, ep := range changes.UpdateOld { - addEndpoint(ep, recordSets, true) + addEndpoint(ep, recordSets, endpoints, true) + } + for _, ep := range changes.UpdateNew { + addEndpoint(ep, recordSets, endpoints, false) } for _, ep := range changes.Delete { - addEndpoint(ep, recordSets, true) + addEndpoint(ep, recordSets, endpoints, true) + } + + if err != nil { + return fmt.Errorf("failed to fetch active records: %w", err) } for _, rs := range recordSets { if err2 := p.upsertRecordSet(rs, managedZones); err == nil { From b1e6627a3eeeb1c053c5bf0163cd3571c62eb3bb Mon Sep 17 00:00:00 2001 From: Christoph Petrausch Date: Tue, 27 Sep 2022 10:31:41 +0200 Subject: [PATCH 029/117] Only create a single endpoint with all records instead an endpoint per record --- provider/designate/designate.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/provider/designate/designate.go b/provider/designate/designate.go index 23338db97..fa182f8a5 100644 --- a/provider/designate/designate.go +++ b/provider/designate/designate.go @@ -322,13 +322,13 @@ func (p designateProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, e if recordSet.Type != endpoint.RecordTypeA && recordSet.Type != endpoint.RecordTypeTXT && recordSet.Type != endpoint.RecordTypeCNAME { return nil } - for _, record := range recordSet.Records { - ep := endpoint.NewEndpoint(recordSet.Name, recordSet.Type, record) - ep.Labels[designateRecordSetID] = recordSet.ID - ep.Labels[designateZoneID] = recordSet.ZoneID - ep.Labels[designateOriginalRecords] = strings.Join(recordSet.Records, "\000") - result = append(result, ep) - } + + ep := endpoint.NewEndpoint(recordSet.Name, recordSet.Type, recordSet.Records...) + ep.Labels[designateRecordSetID] = recordSet.ID + ep.Labels[designateZoneID] = recordSet.ZoneID + ep.Labels[designateOriginalRecords] = strings.Join(recordSet.Records, "\000") + result = append(result, ep) + return nil }, ) From e52431f9ca725628bcb8838cfc8ea38933d02fe3 Mon Sep 17 00:00:00 2001 From: Christoph Petrausch Date: Wed, 28 Sep 2022 09:44:08 +0200 Subject: [PATCH 030/117] Add unit tests for upserting existing TXT records --- provider/designate/designate.go | 7 ++++--- provider/designate/designate_test.go | 27 +++++++++++++++------------ 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/provider/designate/designate.go b/provider/designate/designate.go index fa182f8a5..ee3db2af3 100644 --- a/provider/designate/designate.go +++ b/provider/designate/designate.go @@ -415,6 +415,10 @@ func (p designateProvider) ApplyChanges(ctx context.Context, changes *plan.Chang } endpoints, err := p.Records(ctx) + if err != nil { + return fmt.Errorf("failed to fetch active records: %w", err) + } + recordSets := map[string]*recordSet{} for _, ep := range changes.Create { addEndpoint(ep, recordSets, endpoints, false) @@ -429,9 +433,6 @@ func (p designateProvider) ApplyChanges(ctx context.Context, changes *plan.Chang addEndpoint(ep, recordSets, endpoints, true) } - if err != nil { - return fmt.Errorf("failed to fetch active records: %w", err) - } for _, rs := range recordSets { if err2 := p.upsertRecordSet(rs, managedZones); err == nil { err = err2 diff --git a/provider/designate/designate_test.go b/provider/designate/designate_test.go index 0afc72fa6..48deecc44 100644 --- a/provider/designate/designate_test.go +++ b/provider/designate/designate_test.go @@ -275,17 +275,7 @@ func TestDesignateRecords(t *testing.T) { { DNSName: "srv.test.net", RecordType: endpoint.RecordTypeA, - Targets: endpoint.Targets{"10.2.1.1"}, - Labels: map[string]string{ - designateRecordSetID: rs21ID, - designateZoneID: zone2ID, - designateOriginalRecords: "10.2.1.1\00010.2.1.2", - }, - }, - { - DNSName: "srv.test.net", - RecordType: endpoint.RecordTypeA, - Targets: endpoint.Targets{"10.2.1.2"}, + Targets: endpoint.Targets{"10.2.1.1", "10.2.1.2"}, Labels: map[string]string{ designateRecordSetID: rs21ID, designateZoneID: zone2ID, @@ -337,6 +327,19 @@ func testDesignateCreateRecords(t *testing.T, client *fakeDesignateClient) []*re Status: "ACTIVE", }) } + + _, err := client.CreateRecordSet("zone-1", recordsets.CreateOpts{ + Name: "www.example.com.", + Description: "", + Records: []string{"foo"}, + TTL: 60, + Type: endpoint.RecordTypeTXT, + }) + + if err != nil { + t.Fatal("failed to prefil records") + } + endpoints := []*endpoint.Endpoint{ { DNSName: "www.example.com", @@ -410,7 +413,7 @@ func testDesignateCreateRecords(t *testing.T, client *fakeDesignateClient) []*re expectedCopy := make([]*recordsets.RecordSet, len(expected)) copy(expectedCopy, expected) - err := client.ToProvider().ApplyChanges(context.Background(), &plan.Changes{Create: endpoints}) + err = client.ToProvider().ApplyChanges(context.Background(), &plan.Changes{Create: endpoints}) if err != nil { t.Fatal(err) } From 07dc39abc7a7ae4e7625e08921922f3d8fa05d9f Mon Sep 17 00:00:00 2001 From: Ole Markus With Date: Sun, 20 Nov 2022 15:40:00 +0100 Subject: [PATCH 031/117] If controller error propagates all the way out, bail execution --- controller/controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/controller.go b/controller/controller.go index 528d870fa..742c6f2c7 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -292,7 +292,7 @@ func (c *Controller) Run(ctx context.Context) { for { if c.ShouldRunOnce(time.Now()) { if err := c.RunOnce(ctx); err != nil { - log.Error(err) + log.Fatal(err) } } select { From 6c0f2eb5911a6366b5343f7ac08a39570be86fcd Mon Sep 17 00:00:00 2001 From: vinhas tommy Date: Thu, 26 Jan 2023 15:42:24 +0100 Subject: [PATCH 032/117] fix(gandi): allow grandi provider to support multiple TXT rsetvalues --- provider/gandi/gandi.go | 15 ++++++++++----- provider/gandi/gandi_test.go | 21 ++++----------------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/provider/gandi/gandi.go b/provider/gandi/gandi.go index 378b166e8..6aa60c47c 100644 --- a/provider/gandi/gandi.go +++ b/provider/gandi/gandi.go @@ -16,7 +16,6 @@ package gandi import ( "context" "errors" - "fmt" "os" "strings" @@ -121,11 +120,17 @@ func (p *GandiProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, erro name = zone } - if len(r.RrsetValues) > 1 { - return nil, fmt.Errorf("can't handle multiple values for rrset %s", name) - } + for _, v := range r.RrsetValues { + log.WithFields(log.Fields{ + "record": r.RrsetName, + "type": r.RrsetType, + "value": v, + "ttl": r.RrsetTTL, + "zone": zone, + }).Info("Rerturning endpoint record") - endpoints = append(endpoints, endpoint.NewEndpoint(name, r.RrsetType, r.RrsetValues[0])) + endpoints = append(endpoints, endpoint.NewEndpoint(name, r.RrsetType, v)) + } } } } diff --git a/provider/gandi/gandi_test.go b/provider/gandi/gandi_test.go index cd9529f05..bd32d5b6c 100644 --- a/provider/gandi/gandi_test.go +++ b/provider/gandi/gandi_test.go @@ -18,7 +18,6 @@ import ( "fmt" "os" "reflect" - "strings" "testing" "github.com/go-gandi/go-gandi/domain" @@ -342,7 +341,7 @@ func TestGandiProvider_RecordsAppliesDomainFilter(t *testing.T) { td.Cmp(t, expectedActions, mockedClient.Actions) } -func TestGandiProvider_RecordsErrorOnMultipleValues(t *testing.T) { +func TestGandiProvider_RecordsWithMultipleValues(t *testing.T) { mockedClient := mockGandiClientNewWithRecords([]livedns.DomainRecord{ { RrsetValues: []string{"foo", "bar"}, @@ -355,23 +354,11 @@ func TestGandiProvider_RecordsErrorOnMultipleValues(t *testing.T) { LiveDNSClient: mockedClient, } - expectedActions := []MockAction{ - { - Name: "ListDomains", - }, - { - Name: "GetDomainRecords", - FQDN: "example.com", - }, - } - endpoints, err := mockedProvider.Records(context.Background()) - if err == nil { - t.Errorf("expected to fail") + if err != nil { + t.Errorf("should not fail, %s", err) } - assert.Equal(t, 0, len(endpoints)) - assert.True(t, strings.HasPrefix(err.Error(), "can't handle multiple values for rrset")) - td.Cmp(t, expectedActions, mockedClient.Actions) + assert.Equal(t, 2, len(endpoints)) } func TestGandiProvider_ApplyChangesEmpty(t *testing.T) { From 176fd2ba320c2674127057fadcf1a8f9678c75ec Mon Sep 17 00:00:00 2001 From: vinhas tommy Date: Mon, 30 Jan 2023 08:09:06 +0100 Subject: [PATCH 033/117] improve log on Records --- provider/gandi/gandi.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/gandi/gandi.go b/provider/gandi/gandi.go index 6aa60c47c..3746469ea 100644 --- a/provider/gandi/gandi.go +++ b/provider/gandi/gandi.go @@ -127,7 +127,7 @@ func (p *GandiProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, erro "value": v, "ttl": r.RrsetTTL, "zone": zone, - }).Info("Rerturning endpoint record") + }).Debug("Returning endpoint record") endpoints = append(endpoints, endpoint.NewEndpoint(name, r.RrsetType, v)) } From 929e618935c6d1c8a751b0d61f7fdb69fe6ef886 Mon Sep 17 00:00:00 2001 From: "Eric R. Rath" Date: Mon, 27 Feb 2023 12:34:16 -0800 Subject: [PATCH 034/117] --events controls CRD informer creation mgruener suggested that the --events flag could be wired to control whether or not the CRD source created and started its informer. This commit makes that change; good idea! --- main.go | 1 + source/store.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index 3ef760edb..4347c546f 100644 --- a/main.go +++ b/main.go @@ -133,6 +133,7 @@ func main() { RequestTimeout: cfg.RequestTimeout, DefaultTargets: cfg.DefaultTargets, OCPRouterName: cfg.OCPRouterName, + UpdateEvents: cfg.UpdateEvents, } // Lookup all the selected sources by names and pass them the desired configuration. diff --git a/source/store.go b/source/store.go index da7ce2a65..9194030b2 100644 --- a/source/store.go +++ b/source/store.go @@ -69,6 +69,7 @@ type Config struct { RequestTimeout time.Duration DefaultTargets []string OCPRouterName string + UpdateEvents bool } // ClientGenerator provides clients @@ -270,8 +271,7 @@ func BuildWithConfig(ctx context.Context, source string, p ClientGenerator, cfg if err != nil { return nil, err } - startInformer := true - return NewCRDSource(crdClient, cfg.Namespace, cfg.CRDSourceKind, cfg.AnnotationFilter, cfg.LabelFilter, scheme, startInformer) + return NewCRDSource(crdClient, cfg.Namespace, cfg.CRDSourceKind, cfg.AnnotationFilter, cfg.LabelFilter, scheme, cfg.UpdateEvents) case "skipper-routegroup": apiServerURL := cfg.APIServerURL tokenPath := "" From 84f9daa57da9097f3c79e88493770fa7ceb13e71 Mon Sep 17 00:00:00 2001 From: Houssem Dellai Date: Wed, 8 Mar 2023 15:20:45 +0100 Subject: [PATCH 035/117] Updated new annotation : ingressClassName: nginx Replaced old annotation: annotations: kubernetes.io/ingress.class: nginx with new one: ingressClassName: nginx --- docs/tutorials/azure.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/tutorials/azure.md b/docs/tutorials/azure.md index 47974a1c1..08a8757c2 100644 --- a/docs/tutorials/azure.md +++ b/docs/tutorials/azure.md @@ -560,9 +560,8 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx - annotations: - kubernetes.io/ingress.class: nginx spec: + ingressClassName: nginx rules: - host: server.example.com http: From 9a76be0202281e7e3e51ca7ce6b32d9def8b32ff Mon Sep 17 00:00:00 2001 From: Mikael Johansson Date: Tue, 25 Oct 2022 14:48:12 +0200 Subject: [PATCH 036/117] Add F5 VirtualServer source --- docs/tutorials/f5-virtualserver.md | 33 ++++ go.mod | 5 +- go.sum | 131 +++++++++++++-- pkg/apis/externaldns/types.go | 2 +- source/f5_virtualserver.go | 240 ++++++++++++++++++++++++++++ source/f5_virtualserver_test.go | 247 +++++++++++++++++++++++++++++ source/store.go | 11 ++ source/store_test.go | 12 +- 8 files changed, 666 insertions(+), 15 deletions(-) create mode 100644 docs/tutorials/f5-virtualserver.md create mode 100644 source/f5_virtualserver.go create mode 100644 source/f5_virtualserver_test.go diff --git a/docs/tutorials/f5-virtualserver.md b/docs/tutorials/f5-virtualserver.md new file mode 100644 index 000000000..f91acabec --- /dev/null +++ b/docs/tutorials/f5-virtualserver.md @@ -0,0 +1,33 @@ +# Configuring ExternalDNS to use the F5 Networks VirtualServer Source +This tutorial describes how to configure ExternalDNS to use the F5 Networks VirtualServer Source. It is meant to supplement the other provider-specific setup tutorials. + +The F5 Networks VirtualServer CRD is part of [this](https://github.com/F5Networks/k8s-bigip-ctlr) project. See more in-depth info regarding the VirtualServer CRD [here](https://github.com/F5Networks/k8s-bigip-ctlr/blob/master/docs/config_examples/customResource/CustomResource.md#virtualserver). + +## Start with ExternalDNS with the F5 Networks VirtualServer source + +1. Make sure that you have the `k8s-bigip-ctlr` installed in your cluster. The needed CRDs are bundled within the controller. + +2. In your Helm `values.yaml` add: +``` +sources: + - ... + - f5-virtualserver + - ... +``` +or add it in your `Deployment` if you aren't installing `external-dns` via Helm: +``` +args: +- --source=f5-virtualserver +``` + +Note that, in case you're not installing via Helm, you'll need the following in the `ClusterRole` bound to the service account of `external-dns`: +``` +- apiGroups: + - cis.f5.com + resources: + - virtualservers + verbs: + - get + - list + - watch +``` \ No newline at end of file diff --git a/go.mod b/go.mod index 2d037fffb..de66742d3 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/Azure/go-autorest/autorest v0.11.28 github.com/Azure/go-autorest/autorest/adal v0.9.21 github.com/Azure/go-autorest/autorest/to v0.4.0 + github.com/F5Networks/k8s-bigip-ctlr/v2 v2.11.1 github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.0.0 github.com/IBM/go-sdk-core/v5 v5.8.0 github.com/IBM/networking-go-sdk v0.36.0 @@ -39,8 +40,8 @@ require ( github.com/nesv/go-dynect v0.6.0 github.com/nic-at/rc0go v1.1.1 github.com/onsi/ginkgo v1.16.5 - github.com/openshift/api v0.0.0-20200605231317-fb2a6ca106ae - github.com/openshift/client-go v0.0.0-20200608144219-584632b8fc73 + github.com/openshift/api v0.0.0-20210315202829-4b79815405ec + github.com/openshift/client-go v0.0.0-20210112165513-ebc401615f47 github.com/oracle/oci-go-sdk v24.3.0+incompatible github.com/ovh/go-ovh v1.1.0 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index c712e9acb..bb032b1b7 100644 --- a/go.sum +++ b/go.sum @@ -28,6 +28,8 @@ cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2Aawl cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -49,9 +51,13 @@ github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= +github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM= github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk= github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U= @@ -60,12 +66,14 @@ github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8K github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= @@ -74,6 +82,9 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/F5Networks/f5-ipam-controller v0.1.6-0.20211217134627-c2be8b459270/go.mod h1:XBOjYUVRKG8q8atIpNmil/XF6RAGVekqfbeNQludcV4= +github.com/F5Networks/k8s-bigip-ctlr/v2 v2.11.1 h1:/ZCtF9JC9VyTN2bdwtHnlpCoJOWxZON5Sh7QDtFlB1E= +github.com/F5Networks/k8s-bigip-ctlr/v2 v2.11.1/go.mod h1:vdlVHq8oUD8W8yUr53RSZ9hKkOcboFcgvhS6nMKH+qc= github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.0.0 h1:2gzVSELk4I4ncZNrsaKI6fvZ3to60iYnig+lTFcGCEM= github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.0.0/go.mod h1:P9YNyJaJazc49fLNFG4uQ61VZVptykWqNU2vWLWcxu0= github.com/IBM/go-sdk-core/v5 v5.8.0 h1:Bn9BxTaKYKWpd+BDpVsL6XOOJl4QDgxux4gSdWi31vE= @@ -94,6 +105,7 @@ github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHS github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -173,8 +185,10 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bodgit/tsig v1.2.0 h1:wNfc7yTk2OuWh/s7nEFa9h+SkIfTn7e4xlFtf1Sgvr4= github.com/bodgit/tsig v1.2.0/go.mod h1:bsN2ntwGE/s3EeoawjAoKUcAfO4Fr0nGKC72vNF/cqM= @@ -224,6 +238,7 @@ github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDG github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -242,6 +257,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/datawire/ambassador v1.6.0 h1:4KduhY/wqtv0jK8sMVQNtENHy9fmoXugsuFp/UrM0Ts= github.com/datawire/ambassador v1.6.0/go.mod h1:mV5EhoG/NnHBsffmLnjrq+x4ZNkYDWFZXW9R+AueUiE= @@ -306,11 +322,14 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/envoyproxy/protoc-gen-validate v0.3.0-java.0.20200609174644-bd816e4522c1/go.mod h1:bjmEhrMDubXDd0uKxnWwRmgSsiEv2CkJliIHnj6ETm8= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exoscale/egoscale v0.97.0 h1:9DRSdFxepQrm/BOX/tvMXmfeN7d1row9N/+D9wrFp8E= github.com/exoscale/egoscale v0.97.0/go.mod h1:BAb9p4rmyU+Wl400CJZO5270H2sXtdsZjLcm5xMKkz4= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/f5devcentral/go-bigip/f5teem v0.0.0-20210918163638-28fdd0579913/go.mod h1:r7o5I22EvO+fps2u10bz4ZUlTlNHopQSWzVcW19hK3U= +github.com/f5devcentral/mockhttpclient v0.0.0-20210630101009-cc12e8b81051/go.mod h1:g2/ykgb7Fzf6ag/pYv0LfcwSH8z46TnjFOF3rWyh01I= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= @@ -318,6 +337,7 @@ github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99 h1:jmwW6QWvUO2OPe22YfgFvBaaZlSr8Rlrac5lZvG6IdM= github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99/go.mod h1:4mP9w9+vYGw2jUx2+2v03IA+phyQQjNRR4AL3uxlNrs= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -349,6 +369,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -392,6 +414,7 @@ github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsd github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= @@ -571,6 +594,8 @@ github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57Q github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= +github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/gookit/color v1.2.3/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gophercloud/gophercloud v0.25.0 h1:C3Oae7y0fUVQGSsBrb3zliAjdX+riCSEh4lNMejFNI4= @@ -592,6 +617,7 @@ github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/z github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -602,7 +628,9 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -633,6 +661,7 @@ github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -654,6 +683,7 @@ github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI= github.com/infobloxopen/infoblox-go-client/v2 v2.1.2-0.20220407114022-6f4c71443168 h1:EXKtVoP/44ckXpw3v2/vrtMEdKx/PA+YBl+REoV27XQ= github.com/infobloxopen/infoblox-go-client/v2 v2.1.2-0.20220407114022-6f4c71443168/go.mod h1:+lznx4ASBSUZ2i6qwlgyn0v3eKDxBHNU5aRJzghAFbw= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= @@ -758,6 +788,7 @@ github.com/lyft/protoc-gen-star v0.4.10/go.mod h1:mE8fbna26u7aEA2QCVvvfBU/ZrPgoc github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -790,7 +821,9 @@ github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= @@ -798,6 +831,7 @@ github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwU github.com/mholt/archiver/v3 v3.3.0/go.mod h1:YnQtqsp+94Rwd0D/rk5cnLrxusUBUXg+08Ebtr1Mqao= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.6/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.42/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.51 h1:0+Xg7vObnhrz/4ZCZcZh7zPXlmU0aveS2HDBd0m0qSo= github.com/miekg/dns v1.1.51/go.mod h1:2Z9d3CP1LQWihRZUf29mQ19yDThaI4DAYzte2CaQW5c= @@ -820,6 +854,9 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mitchellh/protoc-gen-go-json v0.0.0-20190813154521-ece073100ced/go.mod h1:VhkV06JZBMrulb3fNiUvM5Lmz3yc7ei3omzocl9UtCw= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= +github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -868,6 +905,7 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg= +github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= @@ -880,6 +918,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= +github.com/onsi/gomega v1.12.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= +github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= @@ -894,11 +934,12 @@ github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5X github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/openshift/api v0.0.0-20200605231317-fb2a6ca106ae h1:cRqNH6AtRQwEqCpymMkaR2ePp08FBIYLkU7YusJeZJ8= -github.com/openshift/api v0.0.0-20200605231317-fb2a6ca106ae/go.mod h1:l6TGeqJ92DrZBuWMNKcot1iZUHfbYSJyBWHGgg6Dn6s= -github.com/openshift/build-machinery-go v0.0.0-20200424080330-082bf86082cc/go.mod h1:1CkcsT3aVebzRBzVTSbiKSkJMsC/CASqxesfqEMfJEc= -github.com/openshift/client-go v0.0.0-20200608144219-584632b8fc73 h1:JePLt9EpNLF/30KsSsArrzxGWPaUIvYUt8Fwnw9wlgM= -github.com/openshift/client-go v0.0.0-20200608144219-584632b8fc73/go.mod h1:+66gk3dEqw9e+WoiXjJFzWlS1KGhj9ZRHi/RI/YG/ZM= +github.com/openshift/api v0.0.0-20210105115604-44119421ec6b/go.mod h1:aqU5Cq+kqKKPbDMqxo9FojgDeSpNJI7iuskjXjtojDg= +github.com/openshift/api v0.0.0-20210315202829-4b79815405ec h1:Uoyk1PiylPs99uuDpUiYyqjbk6BkZRj/oCJeQ2f3WSE= +github.com/openshift/api v0.0.0-20210315202829-4b79815405ec/go.mod h1:aqU5Cq+kqKKPbDMqxo9FojgDeSpNJI7iuskjXjtojDg= +github.com/openshift/build-machinery-go v0.0.0-20200917070002-f171684f77ab/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE= +github.com/openshift/client-go v0.0.0-20210112165513-ebc401615f47 h1:+TEY29DK0XhqB7HFC9OfV8qf3wffSyi7MWv3AP28DGQ= +github.com/openshift/client-go v0.0.0-20210112165513-ebc401615f47/go.mod h1:u7NRAjtYVAKokiI9LouzTv4mhds8P4S1TwdVAfbjKSk= github.com/openshift/gssapi v0.0.0-20161010215902-5fb4217df13b h1:it0YPE/evO6/m8t8wxis9KFI2F/aleOKsI6d9uz0cEk= github.com/openshift/gssapi v0.0.0-20161010215902-5fb4217df13b/go.mod h1:tNrEB5k8SI+g5kOlsCmL2ELASfpqEofI0+FLBgBdN08= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= @@ -990,6 +1031,7 @@ github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= @@ -1030,6 +1072,7 @@ github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= @@ -1057,6 +1100,7 @@ github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKv github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -1066,6 +1110,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -1087,6 +1132,7 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.599 h1:9rMFA8++HynZHYz32gAluJ2ONtz7NjhlBaiomVHWwdw= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.599/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= @@ -1130,7 +1176,9 @@ github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49 github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xeipuuv/gojsonpointer v0.0.0-20151027082146-e0fe6f683076/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20150808065054-e02fc20de94c/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= @@ -1152,7 +1200,9 @@ github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.etcd.io/etcd/api/v3 v3.5.5 h1:BX4JIbQ7hl7+jL+g+2j5UAr0o1bctCm6/Ct+ArBGkf0= go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= go.etcd.io/etcd/client/pkg/v3 v3.5.5 h1:9S0JUVvmrVl7wCF39iTQthdaaNIiAaQbmK75ogO6GU8= @@ -1215,8 +1265,10 @@ golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -1257,6 +1309,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -1307,9 +1361,14 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -1405,7 +1464,10 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1413,6 +1475,7 @@ golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1434,6 +1497,7 @@ golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -1459,6 +1523,11 @@ golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs= +golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1482,6 +1551,7 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1490,6 +1560,7 @@ golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1498,7 +1569,6 @@ golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200115044656-831fdb1e1868/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1511,9 +1581,11 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -1595,6 +1667,18 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc h1:ijGwO+0vL2hJt5gaygqP2j6PfflOBrRot0IczKbmtio= google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= @@ -1662,6 +1746,7 @@ gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdOD gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -1695,6 +1780,8 @@ gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= helm.sh/helm/v3 v3.2.4/go.mod h1:ZaXz/vzktgwjyGGFbUWtIQkscfE7WYoRGP2szqAFHR0= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1714,47 +1801,66 @@ istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a/go.mod h1:OzpAts7jljZc k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8= k8s.io/api v0.18.1/go.mod h1:3My4jorQWzSs5a+l7Ge6JBbIxChLnY8HnuT58ZWolss= k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= -k8s.io/api v0.18.3/go.mod h1:UOaMwERbqJMfeeeHc8XJKawj4P9TgDRnViIqqBeH2QA= k8s.io/api v0.18.4/go.mod h1:lOIQAKYgai1+vz9J7YcDZwC26Z0zQewYOGWdyIPUUQ4= +k8s.io/api v0.20.0/go.mod h1:HyLC5l5eoS/ygQYl1BXBgFzWNlkHiAuyNAbevIn+FKg= +k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= +k8s.io/api v0.21.2/go.mod h1:Lv6UGJZ1rlMI1qusN8ruAp9PUBFyBwpEHAdG24vIsiU= k8s.io/api v0.26.0 h1:IpPlZnxBpV1xl7TGk/X6lFtpgjgntCg8PJ+qrPHAC7I= k8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg= k8s.io/apiextensions-apiserver v0.18.0/go.mod h1:18Cwn1Xws4xnWQNC00FLq1E350b9lUF+aOdIWDOZxgo= k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= k8s.io/apiextensions-apiserver v0.18.4/go.mod h1:NYeyeYq4SIpFlPxSAB6jHPIdvu3hL0pc36wuRChybio= +k8s.io/apiextensions-apiserver v0.20.4/go.mod h1:Hzebis/9c6Io5yzHp24Vg4XOkTp1ViMwKP/6gmpsfA4= +k8s.io/apiextensions-apiserver v0.21.2/go.mod h1:+Axoz5/l3AYpGLlhJDfcVQzCerVYq3K3CvDMvw6X1RA= k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= k8s.io/apimachinery v0.18.1/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= -k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= k8s.io/apimachinery v0.18.4/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= +k8s.io/apimachinery v0.20.0/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.21.2/go.mod h1:CdTY8fU/BlvAbJ2z/8kBwimGki5Zp8/fbVuLY8gJumM= k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg= k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw= k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw= k8s.io/apiserver v0.18.4/go.mod h1:q+zoFct5ABNnYkGIaGQ3bcbUNdmPyOCoEBcg51LChY8= +k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= +k8s.io/apiserver v0.21.2/go.mod h1:lN4yBoGyiNT7SC1dmNk0ue6a5Wi6O3SWOIw91TsucQw= k8s.io/cli-runtime v0.18.0/go.mod h1:1eXfmBsIJosjn9LjEBUd2WVPoPAY9XGTqTFcPMIBsUQ= k8s.io/cli-runtime v0.18.4/go.mod h1:9/hS/Cuf7NVzWR5F/5tyS6xsnclxoPLVtwhnkJG1Y4g= k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8= k8s.io/client-go v0.18.1/go.mod h1:iCikYRiXOj/yRRFE/aWqrpPtDt4P2JVWhtHkmESTcfY= k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= -k8s.io/client-go v0.18.3/go.mod h1:4a/dpQEvzAhT1BbuWW09qvIaGw6Gbu1gZYiQZIi1DMw= k8s.io/client-go v0.18.4/go.mod h1:f5sXwL4yAZRkAtzOxRWUhA/N8XzGCb+nPZI8PfobZ9g= +k8s.io/client-go v0.20.0/go.mod h1:4KWh/g+Ocd8KkCwKF8vUNnmqgv+EVnQDK4MBF4oB5tY= +k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= +k8s.io/client-go v0.21.2/go.mod h1:HdJ9iknWpbl3vMGtib6T2PyI/VYxiZfq936WNVHBRrA= k8s.io/client-go v0.26.0 h1:lT1D3OfO+wIi9UFolCrifbjUUgu7CpLca0AD8ghRLI8= k8s.io/client-go v0.26.0/go.mod h1:I2Sh57A79EQsDmn7F7ASpmru1cceh3ocVT9KlX2jEZg= k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= -k8s.io/code-generator v0.18.3/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= k8s.io/code-generator v0.18.4/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= +k8s.io/code-generator v0.20.0/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg= +k8s.io/code-generator v0.20.4/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg= +k8s.io/code-generator v0.21.2/go.mod h1:8mXJDCB7HcRo1xiEQstcguZkbxZaqeUOrO9SsicWs3U= k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1v2c= k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM= k8s.io/component-base v0.18.4/go.mod h1:7jr/Ef5PGmKwQhyAz/pjByxJbC58mhKAhiaDu0vXfPk= +k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= +k8s.io/component-base v0.21.2/go.mod h1:9lvmIThzdlrJj5Hp8Z/TOgIkdfsNARQ1pT+3PByuiuc= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/helm v2.16.9+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= k8s.io/kubectl v0.18.0/go.mod h1:LOkWx9Z5DXMEg5KtOjHhRiC1fqJPLyCr3KtQgEolCkU= @@ -1762,6 +1868,7 @@ k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/metrics v0.18.0/go.mod h1:8aYTW18koXqjLVKL7Ds05RPMX9ipJZI3mywYvBOxXd4= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20200603063816-c1c6865ac451/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs= k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8= @@ -1771,6 +1878,8 @@ rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.19/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/controller-runtime v0.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gEORz0efEja7A= sigs.k8s.io/controller-runtime v0.12.1 h1:4BJY01xe9zKQti8oRjj/NeHKRXthf1YkYJAgLONFFoI= sigs.k8s.io/controller-runtime v0.12.1/go.mod h1:BKhxlA4l7FPK4AQcsuL4X6vZeWnKDXez/vp1Y8dxTU0= @@ -1782,6 +1891,8 @@ sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h6 sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 230610e7f..12759ab32 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -404,7 +404,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, gateway-grpcroute, gateway-tlsroute, gateway-tcproute, gateway-udproute, 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-grpcroute", "gateway-tlsroute", "gateway-tcproute", "gateway-udproute", "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-grpcroute, gateway-tlsroute, gateway-tcproute, gateway-udproute, istio-gateway, istio-virtualservice, cloudfoundry, contour-ingressroute, contour-httpproxy, gloo-proxy, crd, empty, skipper-routegroup, openshift-route, ambassador-host, kong-tcpingress, f5-virtualserver)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "pod", "gateway-httproute", "gateway-grpcroute", "gateway-tlsroute", "gateway-tcproute", "gateway-udproute", "istio-gateway", "istio-virtualservice", "cloudfoundry", "contour-ingressroute", "contour-httpproxy", "gloo-proxy", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route", "ambassador-host", "kong-tcpingress", "f5-virtualserver") 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/f5_virtualserver.go b/source/f5_virtualserver.go new file mode 100644 index 000000000..016cf260d --- /dev/null +++ b/source/f5_virtualserver.go @@ -0,0 +1,240 @@ +/* +Copyright 2022 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" + "fmt" + "sort" + + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" + 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" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/dynamic/dynamicinformer" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/cache" + + f5 "github.com/F5Networks/k8s-bigip-ctlr/v2/config/apis/cis/v1" + + "sigs.k8s.io/external-dns/endpoint" +) + +var f5VirtualServerGVR = schema.GroupVersionResource{ + Group: "cis.f5.com", + Version: "v1", + Resource: "virtualservers", +} + +// virtualServerSource is an implementation of Source for F5 VirtualServer objects. +type f5VirtualServerSource struct { + dynamicKubeClient dynamic.Interface + virtualServerInformer informers.GenericInformer + kubeClient kubernetes.Interface + annotationFilter string + namespace string + unstructuredConverter *unstructuredConverter +} + +func NewF5VirtualServerSource( + ctx context.Context, + dynamicKubeClient dynamic.Interface, + kubeClient kubernetes.Interface, + namespace string, + annotationFilter string, +) (Source, error) { + informerFactory := dynamicinformer.NewFilteredDynamicSharedInformerFactory(dynamicKubeClient, 0, namespace, nil) + virtualServerInformer := informerFactory.ForResource(f5VirtualServerGVR) + + virtualServerInformer.Informer().AddEventHandler( + cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + }, + }, + ) + + informerFactory.Start(ctx.Done()) + + // wait for the local cache to be populated. + if err := waitForDynamicCacheSync(context.Background(), informerFactory); err != nil { + return nil, err + } + + uc, err := newVSUnstructuredConverter() + if err != nil { + return nil, errors.Wrapf(err, "failed to setup unstructured converter") + } + + return &f5VirtualServerSource{ + dynamicKubeClient: dynamicKubeClient, + virtualServerInformer: virtualServerInformer, + kubeClient: kubeClient, + namespace: namespace, + annotationFilter: annotationFilter, + unstructuredConverter: uc, + }, nil +} + +// Endpoints returns endpoint objects for each host-target combination that should be processed. +// Retrieves all VirtualServers in the source's namespace(s). +func (vs *f5VirtualServerSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) { + virtualServerObjects, err := vs.virtualServerInformer.Lister().ByNamespace(vs.namespace).List(labels.Everything()) + if err != nil { + return nil, err + } + + var virtualServers []*f5.VirtualServer + for _, vsObj := range virtualServerObjects { + unstructuredHost, ok := vsObj.(*unstructured.Unstructured) + if !ok { + return nil, errors.New("could not convert") + } + + virtualServer := &f5.VirtualServer{} + err := vs.unstructuredConverter.scheme.Convert(unstructuredHost, virtualServer, nil) + if err != nil { + return nil, err + } + virtualServers = append(virtualServers, virtualServer) + } + + virtualServers, err = vs.filterByAnnotations(virtualServers) + if err != nil { + return nil, errors.Wrap(err, "failed to filter VirtualServers") + } + + endpoints, err := vs.endpointsFromVirtualServers(virtualServers) + if err != nil { + return nil, err + } + + // Sort endpoints + for _, ep := range endpoints { + sort.Sort(ep.Targets) + } + + return endpoints, nil +} + +func (vs *f5VirtualServerSource) AddEventHandler(ctx context.Context, handler func()) { + log.Debug("Adding event handler for VirtualServer") + + vs.virtualServerInformer.Informer().AddEventHandler(eventHandlerFunc(handler)) +} + +// endpointsFromVirtualServers extracts the endpoints from a slice of VirtualServers +func (vs *f5VirtualServerSource) endpointsFromVirtualServers(virtualServers []*f5.VirtualServer) ([]*endpoint.Endpoint, error) { + var endpoints []*endpoint.Endpoint + + for _, virtualServer := range virtualServers { + ttl, err := getTTLFromAnnotations(virtualServer.Annotations) + if err != nil { + return nil, err + } + + if virtualServer.Spec.VirtualServerAddress != "" { + ep := &endpoint.Endpoint{ + Targets: endpoint.Targets{ + virtualServer.Spec.VirtualServerAddress, + }, + RecordType: "A", + DNSName: virtualServer.Spec.Host, + Labels: endpoint.NewLabels(), + RecordTTL: ttl, + } + + vs.setResourceLabel(virtualServer, ep) + endpoints = append(endpoints, ep) + continue + } + + if virtualServer.Status.VSAddress != "" { + ep := &endpoint.Endpoint{ + Targets: endpoint.Targets{ + virtualServer.Status.VSAddress, + }, + RecordType: "A", + DNSName: virtualServer.Spec.Host, + Labels: endpoint.NewLabels(), + RecordTTL: ttl, + } + + vs.setResourceLabel(virtualServer, ep) + endpoints = append(endpoints, ep) + continue + } + } + + return endpoints, nil +} + +// newUnstructuredConverter returns a new unstructuredConverter initialized +func newVSUnstructuredConverter() (*unstructuredConverter, error) { + uc := &unstructuredConverter{ + scheme: runtime.NewScheme(), + } + + // Add the core types we need + uc.scheme.AddKnownTypes(f5VirtualServerGVR.GroupVersion(), &f5.VirtualServer{}, &f5.VirtualServerList{}) + if err := scheme.AddToScheme(uc.scheme); err != nil { + return nil, err + } + + return uc, nil +} + +// filterByAnnotations filters a list of VirtualServers by a given annotation selector. +func (vs *f5VirtualServerSource) filterByAnnotations(virtualServers []*f5.VirtualServer) ([]*f5.VirtualServer, error) { + labelSelector, err := metav1.ParseToLabelSelector(vs.annotationFilter) + if err != nil { + return nil, err + } + + selector, err := metav1.LabelSelectorAsSelector(labelSelector) + if err != nil { + return nil, err + } + + // empty filter returns original list + if selector.Empty() { + return virtualServers, nil + } + + filteredList := []*f5.VirtualServer{} + + for _, vs := range virtualServers { + // convert the VirtualServer's annotations to an equivalent label selector + annotations := labels.Set(vs.Annotations) + + // include VirtualServer if its annotations match the selector + if selector.Matches(annotations) { + filteredList = append(filteredList, vs) + } + } + + return filteredList, nil +} + +func (vs *f5VirtualServerSource) setResourceLabel(virtualServer *f5.VirtualServer, ep *endpoint.Endpoint) { + ep.Labels[endpoint.ResourceLabelKey] = fmt.Sprintf("f5-virtualserver/%s/%s", virtualServer.Namespace, virtualServer.Name) +} diff --git a/source/f5_virtualserver_test.go b/source/f5_virtualserver_test.go new file mode 100644 index 000000000..c08ba3457 --- /dev/null +++ b/source/f5_virtualserver_test.go @@ -0,0 +1,247 @@ +/* +Copyright 2022 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" + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + fakeDynamic "k8s.io/client-go/dynamic/fake" + fakeKube "k8s.io/client-go/kubernetes/fake" + "sigs.k8s.io/external-dns/endpoint" + + f5 "github.com/F5Networks/k8s-bigip-ctlr/v2/config/apis/cis/v1" +) + +const defaultF5VirtualServerNamespace = "virtualserver" + +func TestF5VirtualServerEndpoints(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + annotationFilter string + virtualServer f5.VirtualServer + expected []*endpoint.Endpoint + }{ + { + name: "F5 VirtualServer with host and virtualServerAddress set", + annotationFilter: "", + virtualServer: f5.VirtualServer{ + TypeMeta: metav1.TypeMeta{ + APIVersion: f5VirtualServerGVR.GroupVersion().String(), + Kind: "VirtualServer", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-vs", + Namespace: defaultF5VirtualServerNamespace, + }, + Spec: f5.VirtualServerSpec{ + Host: "www.example.com", + VirtualServerAddress: "192.168.1.100", + }, + }, + expected: []*endpoint.Endpoint{ + { + DNSName: "www.example.com", + Targets: []string{"192.168.1.100"}, + RecordType: endpoint.RecordTypeA, + RecordTTL: 0, + Labels: endpoint.Labels{ + "resource": "f5-virtualserver/virtualserver/test-vs", + }, + }, + }, + }, + { + name: "F5 VirtualServer with host set and IP address from the status field", + annotationFilter: "", + virtualServer: f5.VirtualServer{ + TypeMeta: metav1.TypeMeta{ + APIVersion: f5VirtualServerGVR.GroupVersion().String(), + Kind: "VirtualServer", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-vs", + Namespace: defaultF5VirtualServerNamespace, + }, + Spec: f5.VirtualServerSpec{ + Host: "www.example.com", + }, + Status: f5.VirtualServerStatus{ + VSAddress: "192.168.1.100", + }, + }, + expected: []*endpoint.Endpoint{ + { + DNSName: "www.example.com", + Targets: []string{"192.168.1.100"}, + RecordType: endpoint.RecordTypeA, + RecordTTL: 0, + Labels: endpoint.Labels{ + "resource": "f5-virtualserver/virtualserver/test-vs", + }, + }, + }, + }, + { + name: "F5 VirtualServer with no IP address set", + annotationFilter: "", + virtualServer: f5.VirtualServer{ + TypeMeta: metav1.TypeMeta{ + APIVersion: f5VirtualServerGVR.GroupVersion().String(), + Kind: "VirtualServer", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-vs", + Namespace: defaultF5VirtualServerNamespace, + }, + Spec: f5.VirtualServerSpec{ + Host: "www.example.com", + }, + Status: f5.VirtualServerStatus{ + VSAddress: "", + }, + }, + expected: nil, + }, + { + name: "F5 VirtualServer with matching annotation filter", + annotationFilter: "foo=bar", + virtualServer: f5.VirtualServer{ + TypeMeta: metav1.TypeMeta{ + APIVersion: f5VirtualServerGVR.GroupVersion().String(), + Kind: "VirtualServer", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-vs", + Namespace: defaultF5VirtualServerNamespace, + Annotations: map[string]string{ + "foo": "bar", + }, + }, + Spec: f5.VirtualServerSpec{ + Host: "www.example.com", + VirtualServerAddress: "192.168.1.100", + }, + }, + expected: []*endpoint.Endpoint{ + { + DNSName: "www.example.com", + Targets: []string{"192.168.1.100"}, + RecordType: endpoint.RecordTypeA, + RecordTTL: 0, + Labels: endpoint.Labels{ + "resource": "f5-virtualserver/virtualserver/test-vs", + }, + }, + }, + }, + { + name: "F5 VirtualServer with non-matching annotation filter", + annotationFilter: "foo=bar", + virtualServer: f5.VirtualServer{ + TypeMeta: metav1.TypeMeta{ + APIVersion: f5VirtualServerGVR.GroupVersion().String(), + Kind: "VirtualServer", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-vs", + Namespace: defaultF5VirtualServerNamespace, + Annotations: map[string]string{ + "bar": "foo", + }, + }, + Spec: f5.VirtualServerSpec{ + Host: "www.example.com", + VirtualServerAddress: "192.168.1.100", + }, + }, + expected: nil, + }, + { + name: "F5 VirtualServer TTL annotation", + virtualServer: f5.VirtualServer{ + TypeMeta: metav1.TypeMeta{ + APIVersion: f5VirtualServerGVR.GroupVersion().String(), + Kind: "VirtualServer", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-vs", + Namespace: defaultF5VirtualServerNamespace, + Annotations: map[string]string{ + "external-dns.alpha.kubernetes.io/ttl": "600", + }, + }, + Spec: f5.VirtualServerSpec{ + Host: "www.example.com", + VirtualServerAddress: "192.168.1.100", + }, + }, + expected: []*endpoint.Endpoint{ + { + DNSName: "www.example.com", + Targets: []string{"192.168.1.100"}, + RecordType: endpoint.RecordTypeA, + RecordTTL: 600, + Labels: endpoint.Labels{ + "resource": "f5-virtualserver/virtualserver/test-vs", + }, + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + fakeKubernetesClient := fakeKube.NewSimpleClientset() + scheme := runtime.NewScheme() + scheme.AddKnownTypes(f5VirtualServerGVR.GroupVersion(), &f5.VirtualServer{}, &f5.VirtualServerList{}) + fakeDynamicClient := fakeDynamic.NewSimpleDynamicClient(scheme) + + virtualServer := unstructured.Unstructured{} + + virtualServerJSON, err := json.Marshal(tc.virtualServer) + require.NoError(t, err) + assert.NoError(t, virtualServer.UnmarshalJSON(virtualServerJSON)) + + // Create VirtualServer resources + _, err = fakeDynamicClient.Resource(f5VirtualServerGVR).Namespace(defaultF5VirtualServerNamespace).Create(context.Background(), &virtualServer, metav1.CreateOptions{}) + assert.NoError(t, err) + + source, err := NewF5VirtualServerSource(context.TODO(), fakeDynamicClient, fakeKubernetesClient, defaultF5VirtualServerNamespace, tc.annotationFilter) + require.NoError(t, err) + assert.NotNil(t, source) + + count := &unstructured.UnstructuredList{} + for len(count.Items) < 1 { + count, _ = fakeDynamicClient.Resource(f5VirtualServerGVR).Namespace(defaultF5VirtualServerNamespace).List(context.Background(), metav1.ListOptions{}) + } + + endpoints, err := source.Endpoints(context.Background()) + require.NoError(t, err) + assert.Len(t, endpoints, len(tc.expected)) + assert.Equal(t, endpoints, tc.expected) + }) + } +} diff --git a/source/store.go b/source/store.go index 87ba403a2..9f5d24b00 100644 --- a/source/store.go +++ b/source/store.go @@ -330,7 +330,18 @@ func BuildWithConfig(ctx context.Context, source string, p ClientGenerator, cfg return nil, err } return NewKongTCPIngressSource(ctx, dynamicClient, kubernetesClient, cfg.Namespace, cfg.AnnotationFilter) + case "f5-virtualserver": + kubernetesClient, err := p.KubeClient() + if err != nil { + return nil, err + } + dynamicClient, err := p.DynamicKubernetesClient() + if err != nil { + return nil, err + } + return NewF5VirtualServerSource(ctx, dynamicClient, kubernetesClient, cfg.Namespace, cfg.AnnotationFilter) } + return nil, ErrSourceNotFound } diff --git a/source/store_test.go b/source/store_test.go index 30275d8c4..e2d15f3f2 100644 --- a/source/store_test.go +++ b/source/store_test.go @@ -125,11 +125,16 @@ func (suite *ByNamesTestSuite) TestAllInitialized() { Version: "v1beta1", Resource: "tcpingresses", }: "TCPIngressesList", + { + Group: "cis.f5.com", + Version: "v1", + Resource: "virtualservers", + }: "VirtualServersList", }), nil) - sources, err := ByNames(context.TODO(), mockClientGenerator, []string{"service", "ingress", "istio-gateway", "contour-httpproxy", "kong-tcpingress", "fake"}, minimalConfig) + sources, err := ByNames(context.TODO(), mockClientGenerator, []string{"service", "ingress", "istio-gateway", "contour-httpproxy", "kong-tcpingress", "f5-virtualserver", "fake"}, minimalConfig) suite.NoError(err, "should not generate errors") - suite.Len(sources, 6, "should generate all six sources") + suite.Len(sources, 7, "should generate all seven sources") } func (suite *ByNamesTestSuite) TestOnlyFake() { @@ -166,6 +171,9 @@ func (suite *ByNamesTestSuite) TestKubeClientFails() { _, err = ByNames(context.TODO(), mockClientGenerator, []string{"kong-tcpingress"}, minimalConfig) suite.Error(err, "should return an error if kubernetes client cannot be created") + + _, err = ByNames(context.TODO(), mockClientGenerator, []string{"f5-virtualserver"}, minimalConfig) + suite.Error(err, "should return an error if kubernetes client cannot be created") } func (suite *ByNamesTestSuite) TestIstioClientFails() { From 6a7e846dd75683444eb6b9e8ff0b4d9e4582b216 Mon Sep 17 00:00:00 2001 From: Raffaele Di Fazio Date: Sat, 18 Mar 2023 11:25:16 +0100 Subject: [PATCH 037/117] docker buildx and tester (#3472) * docker buildx and tester Signed-off-by: Raffaele Di Fazio * fix Signed-off-by: Raffaele Di Fazio * run new build for PRs temporarily Signed-off-by: Raffaele Di Fazio * test to see if images do build Signed-off-by: Raffaele Di Fazio * speed up builds Signed-off-by: Raffaele Di Fazio * makefile changes Signed-off-by: Raffaele Di Fazio * fixes Signed-off-by: Raffaele Di Fazio * omg docker Signed-off-by: Raffaele Di Fazio * omg docker Signed-off-by: Raffaele Di Fazio * makefile Signed-off-by: Raffaele Di Fazio * maybe not neeeded Signed-off-by: Raffaele Di Fazio * try this Signed-off-by: Raffaele Di Fazio * output type docker Signed-off-by: Raffaele Di Fazio * changes Signed-off-by: Raffaele Di Fazio * try to run the right job Signed-off-by: Raffaele Di Fazio * add test target Signed-off-by: Raffaele Di Fazio * tr Signed-off-by: Raffaele Di Fazio --------- Signed-off-by: Raffaele Di Fazio --- .github/workflows/staging-image-tester.yml | 37 +++++++++++ Dockerfile | 4 +- Makefile | 71 ++++++++++++++++------ 3 files changed, 90 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/staging-image-tester.yml diff --git a/.github/workflows/staging-image-tester.yml b/.github/workflows/staging-image-tester.yml new file mode 100644 index 000000000..6d98258ec --- /dev/null +++ b/.github/workflows/staging-image-tester.yml @@ -0,0 +1,37 @@ +name: Build all images + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + + build: + permissions: + contents: read # to fetch code (actions/checkout) + checks: write # to create a new check based on the results (shogo82148/actions-goveralls) + + name: Build + runs-on: ubuntu-latest + steps: + + - name: Set up Go 1.x + uses: actions/setup-go@v3 + with: + go-version: 1.19 + id: go + + - name: Check out code into the Go module directory + uses: actions/checkout@v3 + + - name: Install CI + run: | + go get -v -t -d ./... + + - name: Test + run: make build.image/multiarch diff --git a/Dockerfile b/Dockerfile index 12af3f2ab..312fbfa62 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,10 +24,8 @@ COPY go.sum . RUN go mod download COPY . . -RUN make test build.$ARCH -# final image -FROM $ARCH/alpine:3.17 +FROM alpine:3.17 RUN apk update && apk add "libcrypto3>=3.0.8-r0" "libssl3>=3.0.8-r0" && rm -rf /var/cache/apt/* diff --git a/Makefile b/Makefile index 1e00099d8..5939e50c8 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ cover: go get github.com/wadey/gocovmerge $(eval PKGS := $(shell go list ./... | grep -v /vendor/)) - $(eval PKGS_DELIM := $(shell echo $(PKGS) | sed -e 's/ /,/g')) + $(eval PKGS_DELIM := $(shell echo $(PKGS) | tr / -')) go list -f '{{if or (len .TestGoFiles) (len .XTestGoFiles)}}go test -test.v -test.timeout=120s -covermode=count -coverprofile={{.Name}}_{{len .Imports}}_{{len .Deps}}.coverprofile -coverpkg $(PKGS_DELIM) {{.ImportPath}}{{end}}' $(PKGS) | xargs -0 sh -c gocovmerge `ls *.coverprofile` > cover.out rm *.coverprofile @@ -90,8 +90,11 @@ IMAGE ?= us.gcr.io/k8s-artifacts-prod/external-dns/$(BINARY) VERSION ?= $(shell git describe --tags --always --dirty) BUILD_FLAGS ?= -v LDFLAGS ?= -X sigs.k8s.io/external-dns/pkg/apis/externaldns.Version=$(VERSION) -w -s -ARCHS = amd64 arm64v8 arm32v7 -SHELL = /bin/bash +ARCHS = amd64 arm64 arm/v7 +ARCH ?= amd64 +DEFAULT_ARCH = amd64 +SHELL = /bin/bash +OUTPUT_TYPE ?= docker build: build/$(BINARY) @@ -99,37 +102,67 @@ build: build/$(BINARY) build/$(BINARY): $(SOURCES) CGO_ENABLED=0 go build -o build/$(BINARY) $(BUILD_FLAGS) -ldflags "$(LDFLAGS)" . -build.push/multiarch: +build.push/multiarch: $(addprefix build.push-,$(ARCHS)) arch_specific_tags=() for arch in $(ARCHS); do \ - image="$(IMAGE):$(VERSION)-$${arch}" ;\ - # pre-pull due to https://github.com/kubernetes-sigs/cluster-addons/pull/84/files ;\ - docker pull $${arch}/alpine:3.17 ;\ - docker pull golang:1.19 ;\ - DOCKER_BUILDKIT=1 docker build --rm --tag $${image} --build-arg VERSION="$(VERSION)" --build-arg ARCH="$${arch}" . ;\ - docker push $${image} ;\ + image="$(IMAGE):$(VERSION)-$$(echo $$arch | tr / -)" ;\ arch_specific_tags+=( "--amend $${image}" ) ;\ done ;\ DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create "$(IMAGE):$(VERSION)" $${arch_specific_tags[@]} ;\ for arch in $(ARCHS); do \ - DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate --arch $${arch} "$(IMAGE):$(VERSION)" "$(IMAGE):$(VERSION)-$${arch}" ;\ + image="$(IMAGE):$(VERSION)-$$(echo $$arch | tr / -)" ;\ + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate --arch $${arch} "$(IMAGE):$(VERSION)" "$${image}" ;\ done;\ DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(IMAGE):$(VERSION)" \ -build.push: build.docker - docker push "$(IMAGE):$(VERSION)" +build.image/multiarch: $(addprefix build.image-,$(ARCHS)) -build.arm64v8: +build.image: + $(MAKE) ARCH=$(ARCH) OUTPUT_TYPE=docker build.docker + +build.image-amd64: + $(MAKE) ARCH=amd64 build.image + +build.image-arm64: + $(MAKE) ARCH=arm64 build.image + +build.image-arm/v7: + $(MAKE) ARCH=arm/v7 build.image + +build.push: + $(MAKE) ARCH=$(ARCH) OUTPUT_TYPE=registry build.docker + +build.push-amd64: + $(MAKE) ARCH=amd64 build.push + +build.push-arm64: + $(MAKE) ARCH=arm64 build.push + +build.push-arm/v7: + $(MAKE) ARCH=arm/v7 build.push + +build.arm64: CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o build/$(BINARY) $(BUILD_FLAGS) -ldflags "$(LDFLAGS)" . build.amd64: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o build/$(BINARY) $(BUILD_FLAGS) -ldflags "$(LDFLAGS)" . -build.arm32v7: +build.arm/v7: CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -o build/$(BINARY) $(BUILD_FLAGS) -ldflags "$(LDFLAGS)" . -build.docker: +build.setup: + docker buildx inspect img-builder > /dev/null || docker buildx create --name img-builder --use + +build.docker: build.setup build.$(ARCH) docker build --rm --tag "$(IMAGE):$(VERSION)" --build-arg VERSION="$(VERSION)" --build-arg ARCH="amd64" . + image="$(IMAGE):$(VERSION)-$(subst /,-,$(ARCH))"; \ + docker buildx build \ + --pull \ + --output=type=$(OUTPUT_TYPE) \ + --platform linux/$(ARCH) \ + --build-arg ARCH="$(ARCH)" \ + --build-arg VERSION="$(VERSION)" \ + --tag $${image} . build.mini: docker build --rm --tag "$(IMAGE):$(VERSION)-mini" --build-arg VERSION="$(VERSION)" -f Dockerfile.mini . @@ -140,8 +173,8 @@ clean: # Builds and push container images to the staging bucket. .PHONY: release.staging -release.staging: +release.staging: test IMAGE=$(IMAGE_STAGING) $(MAKE) build.push/multiarch -release.prod: - $(MAKE) build.push/multiarch +release.prod: test + $(MAKE) build.push/multiarch \ No newline at end of file From c42fd9c60ad740f98967aefc41f6672bc95886d2 Mon Sep 17 00:00:00 2001 From: Raffaele Di Fazio Date: Sat, 18 Mar 2023 18:47:58 +0100 Subject: [PATCH 038/117] fix prow ci build Signed-off-by: Raffaele Di Fazio --- cloudbuild.yaml | 2 +- go.sum | 31 ++++++------------------------- 2 files changed, 7 insertions(+), 26 deletions(-) diff --git a/cloudbuild.yaml b/cloudbuild.yaml index 52576cc5b..449207fd7 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -3,7 +3,7 @@ timeout: 5000s options: substitution_option: ALLOW_LOOSE steps: - - name: "gcr.io/k8s-staging-test-infra/gcb-docker-gcloud:v20211118-2f2d816b90" + - name: "gcr.io/k8s-staging-test-infra/gcb-docker-gcloud:latest" entrypoint: make env: - DOCKER_CLI_EXPERIMENTAL=enabled diff --git a/go.sum b/go.sum index bb032b1b7..8786ee822 100644 --- a/go.sum +++ b/go.sum @@ -27,9 +27,8 @@ cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGB cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -256,8 +255,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/datawire/ambassador v1.6.0 h1:4KduhY/wqtv0jK8sMVQNtENHy9fmoXugsuFp/UrM0Ts= github.com/datawire/ambassador v1.6.0/go.mod h1:mV5EhoG/NnHBsffmLnjrq+x4ZNkYDWFZXW9R+AueUiE= @@ -595,7 +594,6 @@ github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsC github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/gookit/color v1.2.3/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gophercloud/gophercloud v0.25.0 h1:C3Oae7y0fUVQGSsBrb3zliAjdX+riCSEh4lNMejFNI4= @@ -1310,7 +1308,6 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -1361,13 +1358,10 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -1465,11 +1459,11 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1509,6 +1503,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -1519,15 +1514,11 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs= -golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1595,6 +1586,7 @@ golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4X golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= @@ -1667,18 +1659,7 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc h1:ijGwO+0vL2hJt5gaygqP2j6PfflOBrRot0IczKbmtio= google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= From 23f5615e8e0e648be83e862a385863aa0c12cb4c Mon Sep 17 00:00:00 2001 From: Raffaele Di Fazio Date: Sat, 18 Mar 2023 19:27:53 +0100 Subject: [PATCH 039/117] add deps and pin image Signed-off-by: Raffaele Di Fazio --- Makefile | 2 ++ cloudbuild.yaml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5939e50c8..58b091570 100644 --- a/Makefile +++ b/Makefile @@ -174,7 +174,9 @@ clean: .PHONY: release.staging release.staging: test + apk add musl-dev gcc IMAGE=$(IMAGE_STAGING) $(MAKE) build.push/multiarch release.prod: test + apk add musl-dev gcc $(MAKE) build.push/multiarch \ No newline at end of file diff --git a/cloudbuild.yaml b/cloudbuild.yaml index 449207fd7..1062c582d 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -3,7 +3,7 @@ timeout: 5000s options: substitution_option: ALLOW_LOOSE steps: - - name: "gcr.io/k8s-staging-test-infra/gcb-docker-gcloud:latest" + - name: "gcr.io/k8s-staging-test-infra/gcb-docker-gcloud:v20230206-8160eea68e" entrypoint: make env: - DOCKER_CLI_EXPERIMENTAL=enabled From 64d6bbbbc2aac9e233b07afc6a95ecb4666da023 Mon Sep 17 00:00:00 2001 From: Raffaele Di Fazio Date: Sat, 18 Mar 2023 23:07:24 +0100 Subject: [PATCH 040/117] fix cloudbuild Signed-off-by: Raffaele Di Fazio --- Makefile | 2 -- cloudbuild.yaml | 7 +++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 58b091570..5939e50c8 100644 --- a/Makefile +++ b/Makefile @@ -174,9 +174,7 @@ clean: .PHONY: release.staging release.staging: test - apk add musl-dev gcc IMAGE=$(IMAGE_STAGING) $(MAKE) build.push/multiarch release.prod: test - apk add musl-dev gcc $(MAKE) build.push/multiarch \ No newline at end of file diff --git a/cloudbuild.yaml b/cloudbuild.yaml index 1062c582d..856807017 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -4,13 +4,16 @@ options: substitution_option: ALLOW_LOOSE steps: - name: "gcr.io/k8s-staging-test-infra/gcb-docker-gcloud:v20230206-8160eea68e" - entrypoint: make + entrypoint: bash env: - DOCKER_CLI_EXPERIMENTAL=enabled - VERSION=$_GIT_TAG - PULL_BASE_REF=$_PULL_BASE_REF args: - - release.staging + - -c + - | + apk add musl-dev gcc + make release.staging substitutions: # _GIT_TAG will be filled with a git-based tag for the image, of the form vYYYYMMDD-hash, and # can be used as a substitution From 5ea5d04900434359e487894e7fbec526c42daf91 Mon Sep 17 00:00:00 2001 From: Raffaele Di Fazio Date: Sun, 19 Mar 2023 18:32:51 +0100 Subject: [PATCH 041/117] try to force use of buildx Signed-off-by: Raffaele Di Fazio --- cloudbuild.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cloudbuild.yaml b/cloudbuild.yaml index 856807017..2e99eab0a 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -9,9 +9,11 @@ steps: - DOCKER_CLI_EXPERIMENTAL=enabled - VERSION=$_GIT_TAG - PULL_BASE_REF=$_PULL_BASE_REF + - HOME=/root args: - -c - | + /buildx-entrypoint version apk add musl-dev gcc make release.staging substitutions: From e27d8ffa3e919eb8538048d72c5eb53152ff2e31 Mon Sep 17 00:00:00 2001 From: Kundan Kumar Date: Thu, 23 Mar 2023 14:16:15 +0530 Subject: [PATCH 042/117] updated icon image path --- charts/external-dns/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/external-dns/Chart.yaml b/charts/external-dns/Chart.yaml index 9dbbda380..29a31c935 100644 --- a/charts/external-dns/Chart.yaml +++ b/charts/external-dns/Chart.yaml @@ -12,7 +12,7 @@ keywords: - service - ingress home: https://github.com/kubernetes-sigs/external-dns/ -icon: https://github.com/kubernetes-sigs/external-dns/raw/master/img/external-dns.png +icon: https://github.com/kubernetes-sigs/external-dns/raw/master/docs/img/external-dns.png sources: - https://github.com/kubernetes-sigs/external-dns/ maintainers: From e2fa389c81dc3744d8c583e3cbc0ad96ee89580a Mon Sep 17 00:00:00 2001 From: Raffaele Di Fazio Date: Fri, 24 Mar 2023 21:45:45 +0100 Subject: [PATCH 043/117] explicit auth Signed-off-by: Raffaele Di Fazio --- cloudbuild.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/cloudbuild.yaml b/cloudbuild.yaml index 2e99eab0a..cb3b918f4 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -13,6 +13,7 @@ steps: args: - -c - | + gcloud auth configure-docker /buildx-entrypoint version apk add musl-dev gcc make release.staging From 987e5c200ad42889d13134109899232291fa5cfd Mon Sep 17 00:00:00 2001 From: Raffaele Di Fazio Date: Sat, 25 Mar 2023 14:07:14 +0100 Subject: [PATCH 044/117] fix docker image build Signed-off-by: Raffaele Di Fazio --- Makefile | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 5939e50c8..5af815efa 100644 --- a/Makefile +++ b/Makefile @@ -106,14 +106,10 @@ build.push/multiarch: $(addprefix build.push-,$(ARCHS)) arch_specific_tags=() for arch in $(ARCHS); do \ image="$(IMAGE):$(VERSION)-$$(echo $$arch | tr / -)" ;\ - arch_specific_tags+=( "--amend $${image}" ) ;\ + arch_specific_tags+=( " $${image}" ) ;\ done ;\ - DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create "$(IMAGE):$(VERSION)" $${arch_specific_tags[@]} ;\ - for arch in $(ARCHS); do \ - image="$(IMAGE):$(VERSION)-$$(echo $$arch | tr / -)" ;\ - DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate --arch $${arch} "$(IMAGE):$(VERSION)" "$${image}" ;\ - done;\ - DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(IMAGE):$(VERSION)" \ + echo $${arch_specific_tags[@]} ;\ + DOCKER_CLI_EXPERIMENTAL=enabled docker buildx imagetools create --tag "$(IMAGE):$(VERSION)" $${arch_specific_tags[@]} ;\ build.image/multiarch: $(addprefix build.image-,$(ARCHS)) @@ -154,7 +150,7 @@ build.setup: docker buildx inspect img-builder > /dev/null || docker buildx create --name img-builder --use build.docker: build.setup build.$(ARCH) - docker build --rm --tag "$(IMAGE):$(VERSION)" --build-arg VERSION="$(VERSION)" --build-arg ARCH="amd64" . + docker build --rm --tag "$(IMAGE):$(VERSION)" --build-arg VERSION="$(VERSION)" --build-arg ARCH="$(ARCH)" . image="$(IMAGE):$(VERSION)-$(subst /,-,$(ARCH))"; \ docker buildx build \ --pull \ From bfdec8bafc63ed2f0db58fef72cf1a6fe8359a41 Mon Sep 17 00:00:00 2001 From: Raffaele Di Fazio Date: Sat, 25 Mar 2023 15:13:16 +0100 Subject: [PATCH 045/117] switch to different matchine type Signed-off-by: Raffaele Di Fazio --- cloudbuild.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/cloudbuild.yaml b/cloudbuild.yaml index cb3b918f4..8d2f51943 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -2,6 +2,7 @@ timeout: 5000s options: substitution_option: ALLOW_LOOSE + machineType: 'N1_HIGHCPU_8' steps: - name: "gcr.io/k8s-staging-test-infra/gcb-docker-gcloud:v20230206-8160eea68e" entrypoint: bash From 54041124537f79964ff6c8edd961457cf754bb01 Mon Sep 17 00:00:00 2001 From: Raffaele Di Fazio Date: Sat, 25 Mar 2023 15:50:54 +0100 Subject: [PATCH 046/117] removes provenance and sbom to push to gcr Signed-off-by: Raffaele Di Fazio --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5af815efa..500fea06a 100644 --- a/Makefile +++ b/Makefile @@ -154,6 +154,8 @@ build.docker: build.setup build.$(ARCH) image="$(IMAGE):$(VERSION)-$(subst /,-,$(ARCH))"; \ docker buildx build \ --pull \ + --provenance=false \ + --sbom=false \ --output=type=$(OUTPUT_TYPE) \ --platform linux/$(ARCH) \ --build-arg ARCH="$(ARCH)" \ @@ -173,4 +175,4 @@ release.staging: test IMAGE=$(IMAGE_STAGING) $(MAKE) build.push/multiarch release.prod: test - $(MAKE) build.push/multiarch \ No newline at end of file + $(MAKE) build.push/multiarch From 7f967f63adb8ffe0c2fd227699982da476da343b Mon Sep 17 00:00:00 2001 From: Raffaele Di Fazio Date: Sun, 26 Mar 2023 11:19:52 +0200 Subject: [PATCH 047/117] bumps libcrypto and libssl Signed-off-by: Raffaele Di Fazio --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 312fbfa62..55b72749d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,7 +27,7 @@ COPY . . FROM alpine:3.17 -RUN apk update && apk add "libcrypto3>=3.0.8-r0" "libssl3>=3.0.8-r0" && rm -rf /var/cache/apt/* +RUN apk update && apk add "libcrypto3>=3.0.8-r1" "libssl3>=3.0.8-r1" && rm -rf /var/cache/apt/* COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt COPY --from=builder /sigs.k8s.io/external-dns/build/external-dns /bin/external-dns From f129e9224664e9adc8e59b0e69cfa26a26fcb721 Mon Sep 17 00:00:00 2001 From: grantbirki Date: Sun, 26 Mar 2023 16:30:51 +0100 Subject: [PATCH 048/117] update some actions versions --- .github/workflows/ci.yml | 2 +- .github/workflows/docs.yml | 4 ++-- .github/workflows/lint.yaml | 2 +- .github/workflows/staging-image-tester.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 21b20e7f5..f3d36d5d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Set up Go 1.x - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: 1.19 id: go diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 4682abf01..112048477 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -18,13 +18,13 @@ jobs: with: fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 with: python-version: "3.10" cache: "pip" cache-dependency-path: "./docs/scripts/requirements.txt" - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v4 with: go-version: ^1.19 diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index b4dd91739..41b9880fa 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -21,7 +21,7 @@ jobs: steps: - name: Set up Go 1.x - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: 1.19 id: go diff --git a/.github/workflows/staging-image-tester.yml b/.github/workflows/staging-image-tester.yml index 6d98258ec..30c5eda9f 100644 --- a/.github/workflows/staging-image-tester.yml +++ b/.github/workflows/staging-image-tester.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Set up Go 1.x - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: 1.19 id: go From 693e1dfe38a49a3d63030cae2fcde37102f9f018 Mon Sep 17 00:00:00 2001 From: grantbirki Date: Sun, 26 Mar 2023 16:32:55 +0100 Subject: [PATCH 049/117] add workflow to validate yaml and json files --- .github/workflows/json-yaml-validate.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/json-yaml-validate.yml diff --git a/.github/workflows/json-yaml-validate.yml b/.github/workflows/json-yaml-validate.yml new file mode 100644 index 000000000..f6edaa2ee --- /dev/null +++ b/.github/workflows/json-yaml-validate.yml @@ -0,0 +1,22 @@ +name: json-yaml-validate +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +permissions: + contents: read + pull-requests: write # enable write permissions for pull requests + +jobs: + json-yaml-validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: json-yaml-validate + uses: GrantBirki/json-yaml-validate@v1.2.0 + with: + comment: "true" # enable comment mode From 99bbadcc2d8132b9e3f7c07c481dc39a47bd4b67 Mon Sep 17 00:00:00 2001 From: grantbirki Date: Sun, 26 Mar 2023 16:33:56 +0100 Subject: [PATCH 050/117] fix branches in action --- .github/workflows/json-yaml-validate.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/json-yaml-validate.yml b/.github/workflows/json-yaml-validate.yml index f6edaa2ee..fc27e10be 100644 --- a/.github/workflows/json-yaml-validate.yml +++ b/.github/workflows/json-yaml-validate.yml @@ -1,9 +1,9 @@ name: json-yaml-validate on: push: - branches: - - main + branches: [ master ] pull_request: + branches: [ master ] workflow_dispatch: permissions: From a150ab0e3f08ce5feccbac0f93a79ed9c7aae144 Mon Sep 17 00:00:00 2001 From: grantbirki Date: Sun, 26 Mar 2023 16:39:43 +0100 Subject: [PATCH 051/117] exclude templates dir --- .github/workflows/json-yaml-validate.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/json-yaml-validate.yml b/.github/workflows/json-yaml-validate.yml index fc27e10be..13f2b1961 100644 --- a/.github/workflows/json-yaml-validate.yml +++ b/.github/workflows/json-yaml-validate.yml @@ -20,3 +20,4 @@ jobs: uses: GrantBirki/json-yaml-validate@v1.2.0 with: comment: "true" # enable comment mode + yaml_exclude_regex: "charts/external-dns/templates/.*" From b0625058dbb0e63ffdc6898e15b05a16fde1488f Mon Sep 17 00:00:00 2001 From: grantbirki Date: Sun, 26 Mar 2023 16:44:27 +0100 Subject: [PATCH 052/117] update yaml exclude regex --- .github/workflows/json-yaml-validate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/json-yaml-validate.yml b/.github/workflows/json-yaml-validate.yml index 13f2b1961..cb6c1ac03 100644 --- a/.github/workflows/json-yaml-validate.yml +++ b/.github/workflows/json-yaml-validate.yml @@ -20,4 +20,4 @@ jobs: uses: GrantBirki/json-yaml-validate@v1.2.0 with: comment: "true" # enable comment mode - yaml_exclude_regex: "charts/external-dns/templates/.*" + yaml_exclude_regex: "(charts\/external-dns\/templates\/.*|mkdocs\\.yml)" From eabccf3e082aa04caf3e3f374aedee19fa1f4164 Mon Sep 17 00:00:00 2001 From: grantbirki Date: Sun, 26 Mar 2023 16:45:30 +0100 Subject: [PATCH 053/117] update regex --- .github/workflows/json-yaml-validate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/json-yaml-validate.yml b/.github/workflows/json-yaml-validate.yml index cb6c1ac03..1eb833f63 100644 --- a/.github/workflows/json-yaml-validate.yml +++ b/.github/workflows/json-yaml-validate.yml @@ -20,4 +20,4 @@ jobs: uses: GrantBirki/json-yaml-validate@v1.2.0 with: comment: "true" # enable comment mode - yaml_exclude_regex: "(charts\/external-dns\/templates\/.*|mkdocs\\.yml)" + yaml_exclude_regex: "(charts/external-dns/templates.*|mkdocs.yml)" From d00387c92999e0c1530b144713a318bd1a5b6a17 Mon Sep 17 00:00:00 2001 From: Raffaele Di Fazio Date: Mon, 27 Mar 2023 23:06:10 +0200 Subject: [PATCH 054/117] updates kustomize with newly released version Signed-off-by: Raffaele Di Fazio --- kustomize/kustomization.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kustomize/kustomization.yaml b/kustomize/kustomization.yaml index 03d30833d..f0c516a64 100644 --- a/kustomize/kustomization.yaml +++ b/kustomize/kustomization.yaml @@ -3,7 +3,7 @@ kind: Kustomization images: - name: registry.k8s.io/external-dns/external-dns - newTag: v0.13.2 + newTag: v0.13.4 resources: - ./external-dns-deployment.yaml From 00f919d20f5f6833bc3722edffb7a05736162c4b Mon Sep 17 00:00:00 2001 From: Raffaele Di Fazio Date: Mon, 27 Mar 2023 23:07:49 +0200 Subject: [PATCH 055/117] all the v0.13.4 updates Signed-off-by: Raffaele Di Fazio --- docs/faq.md | 2 +- docs/release.md | 2 +- docs/tutorials/ANS_Group_SafeDNS.md | 4 ++-- docs/tutorials/akamai-edgedns.md | 4 ++-- docs/tutorials/alibabacloud.md | 4 ++-- docs/tutorials/aws-sd.md | 4 ++-- docs/tutorials/aws.md | 6 +++--- docs/tutorials/azure-private-dns.md | 6 +++--- docs/tutorials/azure.md | 6 +++--- docs/tutorials/bluecat.md | 4 ++-- docs/tutorials/civo.md | 4 ++-- docs/tutorials/cloudflare.md | 4 ++-- docs/tutorials/contour.md | 4 ++-- docs/tutorials/coredns.md | 4 ++-- docs/tutorials/designate.md | 4 ++-- docs/tutorials/digitalocean.md | 4 ++-- docs/tutorials/dnsimple.md | 4 ++-- docs/tutorials/dyn.md | 2 +- docs/tutorials/exoscale.md | 2 +- docs/tutorials/externalname.md | 2 +- docs/tutorials/gandi.md | 4 ++-- docs/tutorials/gateway-api.md | 2 +- docs/tutorials/gke.md | 2 +- docs/tutorials/gloo-proxy.md | 4 ++-- docs/tutorials/godaddy.md | 4 ++-- docs/tutorials/hostport.md | 4 ++-- docs/tutorials/ibmcloud.md | 4 ++-- docs/tutorials/infoblox.md | 4 ++-- docs/tutorials/istio.md | 4 ++-- docs/tutorials/kong.md | 4 ++-- docs/tutorials/linode.md | 4 ++-- docs/tutorials/nginx-ingress.md | 4 ++-- docs/tutorials/nodes.md | 4 ++-- docs/tutorials/ns1.md | 4 ++-- docs/tutorials/openshift.md | 4 ++-- docs/tutorials/oracle.md | 2 +- docs/tutorials/ovh.md | 4 ++-- docs/tutorials/pdns.md | 2 +- docs/tutorials/pihole.md | 2 +- docs/tutorials/plural.md | 4 ++-- docs/tutorials/public-private-route53.md | 4 ++-- docs/tutorials/rcodezero.md | 4 ++-- docs/tutorials/rdns.md | 4 ++-- docs/tutorials/rfc2136.md | 4 ++-- docs/tutorials/scaleway.md | 4 ++-- docs/tutorials/security-context.md | 2 +- docs/tutorials/tencentcloud.md | 2 +- docs/tutorials/transip.md | 4 ++-- docs/tutorials/ultradns.md | 4 ++-- docs/tutorials/vinyldns.md | 4 ++-- docs/tutorials/vultr.md | 4 ++-- 51 files changed, 93 insertions(+), 93 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 217951aaa..28b111dd8 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -205,7 +205,7 @@ $ docker run \ -e EXTERNAL_DNS_SOURCE=$'service\ningress' \ -e EXTERNAL_DNS_PROVIDER=google \ -e EXTERNAL_DNS_DOMAIN_FILTER=$'foo.com\nbar.com' \ - registry.k8s.io/external-dns/external-dns:v0.13.2 + registry.k8s.io/external-dns/external-dns:v0.13.4 time="2017-08-08T14:10:26Z" level=info msg="config: &{APIServerURL: KubeConfig: Sources:[service ingress] Namespace: ... ``` diff --git a/docs/release.md b/docs/release.md index de37dce59..4761dd602 100644 --- a/docs/release.md +++ b/docs/release.md @@ -31,7 +31,7 @@ You must be an official maintainer of the project to be able to do a release. - Branch out from the default branch and run `scripts/kustomize-version-updater.sh` to update the image tag used in the kustomization.yaml. - Create an issue to release the corresponding Helm chart via the chart release process (below) assigned to a chart maintainer - Create a PR with the kustomize change. -- Create a PR to replace all versions for docker images in the tutorials. A possible script to use is `sd registry.k8s.io/external-dns/external-dns:.* registry.k8s.io/external-dns/external-dns:v0.13.2 $(fd --type file)` which uses the `fd` and `sd` utilities. +- Create a PR to replace all versions for docker images in the tutorials. A possible script to use is `sd registry.k8s.io/external-dns/external-dns:v0.13.4 - Once the PR is merged, all is done :-) ## How to release a new chart version diff --git a/docs/tutorials/ANS_Group_SafeDNS.md b/docs/tutorials/ANS_Group_SafeDNS.md index 9f636b310..c60e65517 100644 --- a/docs/tutorials/ANS_Group_SafeDNS.md +++ b/docs/tutorials/ANS_Group_SafeDNS.md @@ -48,7 +48,7 @@ spec: - name: external-dns # You will need to check what the latest version is yourself: # https://github.com/kubernetes-sigs/external-dns/releases - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible # (optional) limit to only example.com domains; change to match the @@ -114,7 +114,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible # (optional) limit to only example.com domains; change to match the diff --git a/docs/tutorials/akamai-edgedns.md b/docs/tutorials/akamai-edgedns.md index beda1096a..dd065daed 100644 --- a/docs/tutorials/akamai-edgedns.md +++ b/docs/tutorials/akamai-edgedns.md @@ -57,7 +57,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # or ingress or both - --provider=akamai @@ -143,7 +143,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # or ingress or both - --provider=akamai diff --git a/docs/tutorials/alibabacloud.md b/docs/tutorials/alibabacloud.md index 3f24f71fc..ee0477ede 100644 --- a/docs/tutorials/alibabacloud.md +++ b/docs/tutorials/alibabacloud.md @@ -113,7 +113,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --source=ingress @@ -187,7 +187,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --source=ingress diff --git a/docs/tutorials/aws-sd.md b/docs/tutorials/aws-sd.md index 321928a73..970af7940 100644 --- a/docs/tutorials/aws-sd.md +++ b/docs/tutorials/aws-sd.md @@ -81,7 +81,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 env: - name: AWS_REGION value: us-east-1 # put your CloudMap NameSpace region @@ -148,7 +148,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 env: - name: AWS_REGION value: us-east-1 # put your CloudMap NameSpace region diff --git a/docs/tutorials/aws.md b/docs/tutorials/aws.md index 6b073b574..d5e7ad92b 100644 --- a/docs/tutorials/aws.md +++ b/docs/tutorials/aws.md @@ -413,7 +413,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --source=ingress @@ -508,7 +508,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --source=ingress @@ -962,7 +962,7 @@ A simple way to implement randomised startup is with an init container: spec: initContainers: - name: init-jitter - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 command: - /bin/sh - -c diff --git a/docs/tutorials/azure-private-dns.md b/docs/tutorials/azure-private-dns.md index 2e8dab0e4..640036e46 100644 --- a/docs/tutorials/azure-private-dns.md +++ b/docs/tutorials/azure-private-dns.md @@ -130,7 +130,7 @@ spec: spec: containers: - name: externaldns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --source=ingress @@ -201,7 +201,7 @@ spec: serviceAccountName: externaldns containers: - name: externaldns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --source=ingress @@ -272,7 +272,7 @@ spec: serviceAccountName: externaldns containers: - name: externaldns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --source=ingress diff --git a/docs/tutorials/azure.md b/docs/tutorials/azure.md index 47974a1c1..78b054a83 100644 --- a/docs/tutorials/azure.md +++ b/docs/tutorials/azure.md @@ -356,7 +356,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --source=ingress @@ -424,7 +424,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --source=ingress @@ -495,7 +495,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --source=ingress diff --git a/docs/tutorials/bluecat.md b/docs/tutorials/bluecat.md index f0cd4295e..4ef9c1b39 100644 --- a/docs/tutorials/bluecat.md +++ b/docs/tutorials/bluecat.md @@ -46,7 +46,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --log-level=debug - --source=service @@ -136,7 +136,7 @@ spec: secretName: bluecatconfig containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 volumeMounts: - name: bluecatconfig mountPath: "/etc/external-dns/" diff --git a/docs/tutorials/civo.md b/docs/tutorials/civo.md index 52cd8dfe7..efa5b6fc4 100644 --- a/docs/tutorials/civo.md +++ b/docs/tutorials/civo.md @@ -41,7 +41,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. @@ -105,7 +105,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. diff --git a/docs/tutorials/cloudflare.md b/docs/tutorials/cloudflare.md index e8483d94c..211dbf72c 100644 --- a/docs/tutorials/cloudflare.md +++ b/docs/tutorials/cloudflare.md @@ -54,7 +54,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. @@ -123,7 +123,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. diff --git a/docs/tutorials/contour.md b/docs/tutorials/contour.md index 649fddd09..05e392b6f 100644 --- a/docs/tutorials/contour.md +++ b/docs/tutorials/contour.md @@ -26,7 +26,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --source=ingress @@ -102,7 +102,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --source=ingress diff --git a/docs/tutorials/coredns.md b/docs/tutorials/coredns.md index 0f6e2d3ea..f2c11b8c2 100644 --- a/docs/tutorials/coredns.md +++ b/docs/tutorials/coredns.md @@ -108,7 +108,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=ingress - --provider=coredns @@ -175,7 +175,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=ingress - --provider=coredns diff --git a/docs/tutorials/designate.md b/docs/tutorials/designate.md index c2317e720..e2c79bfbb 100644 --- a/docs/tutorials/designate.md +++ b/docs/tutorials/designate.md @@ -59,7 +59,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. @@ -136,7 +136,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. diff --git a/docs/tutorials/digitalocean.md b/docs/tutorials/digitalocean.md index 83431b7aa..cbad0cfe6 100644 --- a/docs/tutorials/digitalocean.md +++ b/docs/tutorials/digitalocean.md @@ -43,7 +43,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. @@ -107,7 +107,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. diff --git a/docs/tutorials/dnsimple.md b/docs/tutorials/dnsimple.md index 1363f05fa..5af82f6d9 100644 --- a/docs/tutorials/dnsimple.md +++ b/docs/tutorials/dnsimple.md @@ -35,7 +35,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone you create in DNSimple. @@ -100,7 +100,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone you create in DNSimple. diff --git a/docs/tutorials/dyn.md b/docs/tutorials/dyn.md index 5285dbe93..573837bb5 100644 --- a/docs/tutorials/dyn.md +++ b/docs/tutorials/dyn.md @@ -43,7 +43,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=ingress - --txt-prefix=_d diff --git a/docs/tutorials/exoscale.md b/docs/tutorials/exoscale.md index 0347cbf78..d1e93cb0f 100644 --- a/docs/tutorials/exoscale.md +++ b/docs/tutorials/exoscale.md @@ -41,7 +41,7 @@ spec: # serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=ingress # or service or both - --provider=exoscale diff --git a/docs/tutorials/externalname.md b/docs/tutorials/externalname.md index 0bf6aaff2..c5a560039 100644 --- a/docs/tutorials/externalname.md +++ b/docs/tutorials/externalname.md @@ -27,7 +27,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --log-level=debug - --source=service diff --git a/docs/tutorials/gandi.md b/docs/tutorials/gandi.md index 52132f047..7f4f4036b 100644 --- a/docs/tutorials/gandi.md +++ b/docs/tutorials/gandi.md @@ -39,7 +39,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. @@ -103,7 +103,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. diff --git a/docs/tutorials/gateway-api.md b/docs/tutorials/gateway-api.md index 617258560..26ebae312 100644 --- a/docs/tutorials/gateway-api.md +++ b/docs/tutorials/gateway-api.md @@ -72,7 +72,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: # Add desired Gateway API Route sources. - --source=gateway-httproute diff --git a/docs/tutorials/gke.md b/docs/tutorials/gke.md index 4f3246cf7..d4d9e8d29 100644 --- a/docs/tutorials/gke.md +++ b/docs/tutorials/gke.md @@ -319,7 +319,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --source=ingress diff --git a/docs/tutorials/gloo-proxy.md b/docs/tutorials/gloo-proxy.md index 0ec63da80..be5de4a60 100644 --- a/docs/tutorials/gloo-proxy.md +++ b/docs/tutorials/gloo-proxy.md @@ -22,7 +22,7 @@ spec: containers: - name: external-dns # update this to the desired external-dns version - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=gloo-proxy - --gloo-namespace=custom-gloo-system # gloo system namespace. Omit to use the default (gloo-system) @@ -90,7 +90,7 @@ spec: containers: - name: external-dns # update this to the desired external-dns version - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=gloo-proxy - --gloo-namespace=custom-gloo-system # gloo system namespace. Omit to use the default (gloo-system) diff --git a/docs/tutorials/godaddy.md b/docs/tutorials/godaddy.md index 02e22a14f..cd6ba1528 100644 --- a/docs/tutorials/godaddy.md +++ b/docs/tutorials/godaddy.md @@ -44,7 +44,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. @@ -115,7 +115,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. diff --git a/docs/tutorials/hostport.md b/docs/tutorials/hostport.md index 34f94a310..2a0d321dc 100644 --- a/docs/tutorials/hostport.md +++ b/docs/tutorials/hostport.md @@ -31,7 +31,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --log-level=debug - --source=service @@ -96,7 +96,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --log-level=debug - --source=service diff --git a/docs/tutorials/ibmcloud.md b/docs/tutorials/ibmcloud.md index 93784c29c..6f243bd5e 100644 --- a/docs/tutorials/ibmcloud.md +++ b/docs/tutorials/ibmcloud.md @@ -69,7 +69,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. @@ -142,7 +142,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. diff --git a/docs/tutorials/infoblox.md b/docs/tutorials/infoblox.md index 7e713e036..3b25aa523 100644 --- a/docs/tutorials/infoblox.md +++ b/docs/tutorials/infoblox.md @@ -69,7 +69,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --domain-filter=example.com # (optional) limit to only example.com domains. @@ -150,7 +150,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --domain-filter=example.com # (optional) limit to only example.com domains. diff --git a/docs/tutorials/istio.md b/docs/tutorials/istio.md index a4e597657..a63d1d815 100644 --- a/docs/tutorials/istio.md +++ b/docs/tutorials/istio.md @@ -28,7 +28,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --source=ingress @@ -98,7 +98,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --source=ingress diff --git a/docs/tutorials/kong.md b/docs/tutorials/kong.md index 49a456f69..fc4fbf20f 100644 --- a/docs/tutorials/kong.md +++ b/docs/tutorials/kong.md @@ -22,7 +22,7 @@ spec: containers: - name: external-dns # update this to the desired external-dns version - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=kong-tcpingress - --provider=aws @@ -86,7 +86,7 @@ spec: containers: - name: external-dns # update this to the desired external-dns version - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=kong-tcpingress - --provider=aws diff --git a/docs/tutorials/linode.md b/docs/tutorials/linode.md index 9505a6eea..5884c162e 100644 --- a/docs/tutorials/linode.md +++ b/docs/tutorials/linode.md @@ -41,7 +41,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. @@ -105,7 +105,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. diff --git a/docs/tutorials/nginx-ingress.md b/docs/tutorials/nginx-ingress.md index bf491ea51..f6c170b4c 100644 --- a/docs/tutorials/nginx-ingress.md +++ b/docs/tutorials/nginx-ingress.md @@ -273,7 +273,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=ingress - --domain-filter=external-dns-test.gcp.zalan.do @@ -570,7 +570,7 @@ spec: - --google-project=zalando-external-dns-test - --registry=txt - --txt-owner-id=my-identifier - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 name: external-dns securityContext: fsGroup: 65534 diff --git a/docs/tutorials/nodes.md b/docs/tutorials/nodes.md index a4a1e1f15..46f21da5d 100644 --- a/docs/tutorials/nodes.md +++ b/docs/tutorials/nodes.md @@ -28,7 +28,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=node # will use nodes as source - --provider=aws @@ -99,7 +99,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=node # will use nodes as source - --provider=aws diff --git a/docs/tutorials/ns1.md b/docs/tutorials/ns1.md index d286497f8..b96ca85a8 100644 --- a/docs/tutorials/ns1.md +++ b/docs/tutorials/ns1.md @@ -61,7 +61,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. @@ -125,7 +125,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. diff --git a/docs/tutorials/openshift.md b/docs/tutorials/openshift.md index 6cdda8d3b..e5a995b42 100644 --- a/docs/tutorials/openshift.md +++ b/docs/tutorials/openshift.md @@ -66,7 +66,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=openshift-route - --domain-filter=external-dns-test.my-org.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones @@ -133,7 +133,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=openshift-route - --domain-filter=external-dns-test.my-org.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones diff --git a/docs/tutorials/oracle.md b/docs/tutorials/oracle.md index 745947972..f5b0a168c 100644 --- a/docs/tutorials/oracle.md +++ b/docs/tutorials/oracle.md @@ -93,7 +93,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --source=ingress diff --git a/docs/tutorials/ovh.md b/docs/tutorials/ovh.md index bdf531520..0f05457f4 100644 --- a/docs/tutorials/ovh.md +++ b/docs/tutorials/ovh.md @@ -86,7 +86,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. @@ -160,7 +160,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. diff --git a/docs/tutorials/pdns.md b/docs/tutorials/pdns.md index 5cd492e3b..ff0a9874a 100644 --- a/docs/tutorials/pdns.md +++ b/docs/tutorials/pdns.md @@ -42,7 +42,7 @@ spec: # serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # or ingress or both - --provider=pdns diff --git a/docs/tutorials/pihole.md b/docs/tutorials/pihole.md index a55c8589b..dfb3137c9 100644 --- a/docs/tutorials/pihole.md +++ b/docs/tutorials/pihole.md @@ -78,7 +78,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 # If authentication is disabled and/or you didn't create # a secret, you can remove this block. envFrom: diff --git a/docs/tutorials/plural.md b/docs/tutorials/plural.md index c6c140424..956087e93 100644 --- a/docs/tutorials/plural.md +++ b/docs/tutorials/plural.md @@ -35,7 +35,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. @@ -105,7 +105,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. diff --git a/docs/tutorials/public-private-route53.md b/docs/tutorials/public-private-route53.md index 284da28a4..9a4f7be6f 100644 --- a/docs/tutorials/public-private-route53.md +++ b/docs/tutorials/public-private-route53.md @@ -243,7 +243,7 @@ spec: - --txt-owner-id=external-dns - --annotation-filter=kubernetes.io/ingress.class in (external-ingress) - --aws-zone-type=public - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 name: external-dns-public ``` @@ -281,7 +281,7 @@ spec: - --txt-owner-id=dev.k8s.nexus - --annotation-filter=kubernetes.io/ingress.class in (internal-ingress) - --aws-zone-type=private - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 name: external-dns-private ``` diff --git a/docs/tutorials/rcodezero.md b/docs/tutorials/rcodezero.md index 95a6115e2..7890b8fc9 100644 --- a/docs/tutorials/rcodezero.md +++ b/docs/tutorials/rcodezero.md @@ -53,7 +53,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. @@ -120,7 +120,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. diff --git a/docs/tutorials/rdns.md b/docs/tutorials/rdns.md index b6f9a3b89..684c7c64d 100644 --- a/docs/tutorials/rdns.md +++ b/docs/tutorials/rdns.md @@ -54,7 +54,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=ingress - --provider=rdns @@ -123,7 +123,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=ingress - --provider=rdns diff --git a/docs/tutorials/rfc2136.md b/docs/tutorials/rfc2136.md index 980df16af..63e515826 100644 --- a/docs/tutorials/rfc2136.md +++ b/docs/tutorials/rfc2136.md @@ -218,7 +218,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --registry=txt - --txt-prefix=external-dns- @@ -260,7 +260,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --registry=txt - --txt-prefix=external-dns- diff --git a/docs/tutorials/scaleway.md b/docs/tutorials/scaleway.md index ae903c6cb..b6aa2d098 100644 --- a/docs/tutorials/scaleway.md +++ b/docs/tutorials/scaleway.md @@ -53,7 +53,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. @@ -121,7 +121,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. diff --git a/docs/tutorials/security-context.md b/docs/tutorials/security-context.md index 785f38b0e..0b5bf3929 100644 --- a/docs/tutorials/security-context.md +++ b/docs/tutorials/security-context.md @@ -20,7 +20,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - ... # your arguments here securityContext: diff --git a/docs/tutorials/tencentcloud.md b/docs/tutorials/tencentcloud.md index 902ae87be..e5a94311d 100644 --- a/docs/tutorials/tencentcloud.md +++ b/docs/tutorials/tencentcloud.md @@ -129,7 +129,7 @@ spec: - --policy=sync # set `upsert-only` would prevent ExternalDNS from deleting any records - --tencent-cloud-zone-type=private # only look at private hosted zones. set `public` to use the public dns service. - --tencent-cloud-config-file=/etc/kubernetes/tencent-cloud.json - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 imagePullPolicy: Always name: external-dns resources: {} diff --git a/docs/tutorials/transip.md b/docs/tutorials/transip.md index e9b445e7f..83dd6ec50 100644 --- a/docs/tutorials/transip.md +++ b/docs/tutorials/transip.md @@ -36,7 +36,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains @@ -107,7 +107,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains diff --git a/docs/tutorials/ultradns.md b/docs/tutorials/ultradns.md index c112185ab..af684bebb 100644 --- a/docs/tutorials/ultradns.md +++ b/docs/tutorials/ultradns.md @@ -44,7 +44,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --source=ingress # ingress is also possible @@ -116,7 +116,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service - --source=ingress diff --git a/docs/tutorials/vinyldns.md b/docs/tutorials/vinyldns.md index bf15795cc..b7166c777 100644 --- a/docs/tutorials/vinyldns.md +++ b/docs/tutorials/vinyldns.md @@ -66,7 +66,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --provider=vinyldns - --source=service @@ -137,7 +137,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --provider=vinyldns - --source=service diff --git a/docs/tutorials/vultr.md b/docs/tutorials/vultr.md index 81fa6060a..7eef4a580 100644 --- a/docs/tutorials/vultr.md +++ b/docs/tutorials/vultr.md @@ -42,7 +42,7 @@ spec: spec: containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. @@ -106,7 +106,7 @@ spec: serviceAccountName: external-dns containers: - name: external-dns - image: registry.k8s.io/external-dns/external-dns:v0.13.2 + image: registry.k8s.io/external-dns/external-dns:v0.13.4 args: - --source=service # ingress is also possible - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. From 120ba1c9389a20d4333ebdf111c003c5bb5fa004 Mon Sep 17 00:00:00 2001 From: Raffaele Di Fazio Date: Mon, 27 Mar 2023 23:11:05 +0200 Subject: [PATCH 056/117] do not replace this file Signed-off-by: Raffaele Di Fazio --- docs/release.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/release.md b/docs/release.md index 4761dd602..de37dce59 100644 --- a/docs/release.md +++ b/docs/release.md @@ -31,7 +31,7 @@ You must be an official maintainer of the project to be able to do a release. - Branch out from the default branch and run `scripts/kustomize-version-updater.sh` to update the image tag used in the kustomization.yaml. - Create an issue to release the corresponding Helm chart via the chart release process (below) assigned to a chart maintainer - Create a PR with the kustomize change. -- Create a PR to replace all versions for docker images in the tutorials. A possible script to use is `sd registry.k8s.io/external-dns/external-dns:v0.13.4 +- Create a PR to replace all versions for docker images in the tutorials. A possible script to use is `sd registry.k8s.io/external-dns/external-dns:.* registry.k8s.io/external-dns/external-dns:v0.13.2 $(fd --type file)` which uses the `fd` and `sd` utilities. - Once the PR is merged, all is done :-) ## How to release a new chart version From 352acc5cc35dbd97d44f36453fe6992ca1c14aa5 Mon Sep 17 00:00:00 2001 From: Kundan Kumar Date: Tue, 28 Mar 2023 18:24:12 +0530 Subject: [PATCH 057/117] updated relative link for external-dns logo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a5041afbe..2426c9a36 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ hide: ---

- ExternalDNS + ExternalDNS

# ExternalDNS From 815ff2a6d97dc42a01fd788c53b0a68e4854507b Mon Sep 17 00:00:00 2001 From: Sewci0 Date: Wed, 29 Mar 2023 14:00:24 +0100 Subject: [PATCH 058/117] Fix cli docs --- pkg/apis/externaldns/types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 12759ab32..1eb8570bf 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -424,7 +424,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("crd-source-apiversion", "API version of the CRD for crd source, e.g. `externaldns.k8s.io/v1alpha1`, valid only when using crd source").Default(defaultConfig.CRDSourceAPIVersion).StringVar(&cfg.CRDSourceAPIVersion) app.Flag("crd-source-kind", "Kind of the CRD for the crd source in API group and version specified by crd-source-apiversion").Default(defaultConfig.CRDSourceKind).StringVar(&cfg.CRDSourceKind) app.Flag("service-type-filter", "The service types to take care about (default: all, expected: ClusterIP, NodePort, LoadBalancer or ExternalName)").StringsVar(&cfg.ServiceTypeFilter) - app.Flag("managed-record-types", "Comma separated list of record types to manage (default: A, CNAME) (supported records: CNAME, A, NS").Default("A", "CNAME").StringsVar(&cfg.ManagedDNSRecordTypes) + app.Flag("managed-record-types", "Record types to manage; specify multiple times to include many; (default: A, CNAME) (supported records: CNAME, A, NS").Default("A", "CNAME").StringsVar(&cfg.ManagedDNSRecordTypes) app.Flag("default-targets", "Set globally default IP address that will apply as a target instead of source addresses. Specify multiple times for multiple targets (optional)").StringsVar(&cfg.DefaultTargets) app.Flag("target-net-filter", "Limit possible targets by a net filter; specify multiple times for multiple possible nets (optional)").StringsVar(&cfg.TargetNetFilter) app.Flag("exclude-target-net", "Exclude target nets (optional)").StringsVar(&cfg.ExcludeTargetNets) From 32fa5048088e3e2a283995aedc0ceac58e68af1e Mon Sep 17 00:00:00 2001 From: MichaelVL Date: Sun, 26 Mar 2023 08:22:18 +0200 Subject: [PATCH 059/117] Add gateway-api RBAC to helm chart clusterrole --- charts/external-dns/CHANGELOG.md | 9 ++---- .../external-dns/templates/clusterrole.yaml | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/charts/external-dns/CHANGELOG.md b/charts/external-dns/CHANGELOG.md index 8e30bd0e2..7b064094a 100644 --- a/charts/external-dns/CHANGELOG.md +++ b/charts/external-dns/CHANGELOG.md @@ -7,15 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- - +- Added RBAC for Gateway-API resources to clusterrole [#3499](https://github.com/kubernetes-sigs/external-dns/pull/3499). [@michaelvl](https://github.com/MichaelVL) ## [v1.12.1] - 2023-02-06 diff --git a/charts/external-dns/templates/clusterrole.yaml b/charts/external-dns/templates/clusterrole.yaml index 8fcc15dce..6a7db92e9 100644 --- a/charts/external-dns/templates/clusterrole.yaml +++ b/charts/external-dns/templates/clusterrole.yaml @@ -60,6 +60,36 @@ rules: resources: ["dnsendpoints/status"] verbs: ["*"] {{- end }} +{{- if or (has "gateway-httproute" .Values.sources) (has "gateway-grpcroute" .Values.sources) (has "gateway-tlsroute" .Values.sources) (has "gateway-tcproute" .Values.sources) (has "gateway-udproute" .Values.sources) }} + - apiGroups: ["gateway.networking.k8s.io"] + resources: ["gateways"] + verbs: ["get","watch","list"] +{{- end }} +{{- if has "gateway-httproute" .Values.sources }} + - apiGroups: ["gateway.networking.k8s.io"] + resources: ["httproutes"] + verbs: ["get","watch","list"] +{{- end }} +{{- if has "gateway-grpcroute" .Values.sources }} + - apiGroups: ["gateway.networking.k8s.io"] + resources: ["grpcroutes"] + verbs: ["get","watch","list"] +{{- end }} +{{- if has "gateway-tlsroute" .Values.sources }} + - apiGroups: ["gateway.networking.k8s.io"] + resources: ["tlsroutes"] + verbs: ["get","watch","list"] +{{- end }} +{{- if has "gateway-tcproute" .Values.sources }} + - apiGroups: ["gateway.networking.k8s.io"] + resources: ["tcproutes"] + verbs: ["get","watch","list"] +{{- end }} +{{- if has "gateway-udproute" .Values.sources }} + - apiGroups: ["gateway.networking.k8s.io"] + resources: ["udproutes"] + verbs: ["get","watch","list"] +{{- end }} {{- if has "gloo-proxy" .Values.sources }} - apiGroups: ["gloo.solo.io","gateway.solo.io"] resources: ["proxies","virtualservices"] From d2932e33853ffec7abeaf9790f7df3980b0ff62b Mon Sep 17 00:00:00 2001 From: Mikael Johansson Date: Mon, 27 Mar 2023 12:40:16 +0200 Subject: [PATCH 060/117] Add RBAC to support F5 VirtualServer source Signed-off-by: Mikael Johansson --- charts/external-dns/CHANGELOG.md | 6 ++++++ charts/external-dns/templates/clusterrole.yaml | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/charts/external-dns/CHANGELOG.md b/charts/external-dns/CHANGELOG.md index 8e30bd0e2..170a49fee 100644 --- a/charts/external-dns/CHANGELOG.md +++ b/charts/external-dns/CHANGELOG.md @@ -17,6 +17,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Deprecated - Removed --> +## [v1.12.2] - UNRELEASED + +### All Changes + +- Add RBAC to be able to support the F5 VirtualServer `Source` ([#3503](https://github.com/kubernetes-sigs/external-dns/pull/3503)) [@mikejoh](https://github.com/mikejoh) + ## [v1.12.1] - 2023-02-06 ### All Changes diff --git a/charts/external-dns/templates/clusterrole.yaml b/charts/external-dns/templates/clusterrole.yaml index 8fcc15dce..330b919b3 100644 --- a/charts/external-dns/templates/clusterrole.yaml +++ b/charts/external-dns/templates/clusterrole.yaml @@ -83,6 +83,11 @@ rules: resources: ["routegroups/status"] verbs: ["patch","update"] {{- end }} +{{- if has "f5-virtualserver" .Values.sources }} + - apiGroups: ["cis.f5.com"] + resources: ["virtualservers"] + verbs: ["get","watch","list"] +{{- end }} {{- with .Values.rbac.additionalPermissions }} {{- toYaml . | nindent 2 }} {{- end }} From e4792aec22426266693bac66a62aaedcb49f66b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Thu, 30 Mar 2023 10:37:49 +0200 Subject: [PATCH 061/117] [helm] Add support for namespaced scope (#3403) * [helm] Add support for namespaced scope * Add documentation about namespaced scope * Add documentation about namespaced scope --- charts/external-dns/README.md | 34 +++++++++++++++++++ .../external-dns/templates/clusterrole.yaml | 4 +-- .../templates/clusterrolebinding.yaml | 4 +-- charts/external-dns/templates/deployment.yaml | 3 ++ charts/external-dns/values.yaml | 2 ++ 5 files changed, 43 insertions(+), 4 deletions(-) diff --git a/charts/external-dns/README.md b/charts/external-dns/README.md index 67ab1f5df..bfdb56deb 100644 --- a/charts/external-dns/README.md +++ b/charts/external-dns/README.md @@ -69,6 +69,7 @@ The following table lists the configurable parameters of the _ExternalDNS_ chart | `logFormat` | Formats of the logs, available values are: `text`, `json`. | `text` | | `interval` | The interval for DNS updates. | `1m` | | `triggerLoopOnEvent` | When enabled, triggers run loop on create/update/delete events in addition of regular interval. | `false` | +| `namespaced` | When enabled, external-dns runs on namespace scope. Additionally, Role and Rolebinding will be namespaced, too. | `false` | | `sources` | K8s resources type to be observed for new DNS entries. | See _values.yaml_ | | `policy` | How DNS records are synchronized between sources and providers, available values are: `sync`, `upsert-only`. | `upsert-only` | | `registry` | Registry Type, available types are: `txt`, `noop`. | `txt` | @@ -82,3 +83,36 @@ The following table lists the configurable parameters of the _ExternalDNS_ chart | `secretConfiguration.mountPath` | Mount path of secret configuration secret (this can be templated). | `""` | | `secretConfiguration.data` | Secret configuration secret data. Could be used to store DNS provider credentials. | `{}` | | `secretConfiguration.subPath` | Sub-path of secret configuration secret (this can be templated). | `""` | + +## Namespaced scoped installation + +external-dns supports running on a namespaced only scope, too. +If `namespaced=true` is defined, the helm chart will setup `Roles` and `RoleBindings` instead `ClusterRoles` and `ClusterRoleBindings`. + +### Limited supported +Not all sources are supported in namespaced scope, since some sources depends on cluster-wide resources. +For example: Source `node` isn't supported, since `kind: Node` has scope `Cluster`. +Sources like `istio-virtualservice` only work, if all resources like `Gateway` and `VirtualService` are present in the same +namespaces as `external-dns`. + +The annotation `external-dns.alpha.kubernetes.io/endpoints-type: NodeExternalIP` is not supported. + +If `namespaced` is set to `true`, please ensure that `sources` my only contains supported sources (Default: `service,ingress`. + +### Support matrix + +| Source | Supported | Infos | +|------------------------|-----------|------------------------| +| `ingress` | ✅ | | +| `istio-gateway` | ✅ | | +| `istio-virtualservice` | ✅ | | +| `contour-ingressroute` | ✅ | | +| `crd` | ✅ | | +| `kong-tcpingress` | ✅ | | +| `openshift-route` | ✅ | | +| `skipper-routegroup` | ✅ | | +| `gloo-proxy` | ✅ | | +| `contour-httpproxy` | ✅ | | +| `service` | ⚠️️ | NodePort not supported | +| `node` | ❌ | | +| `pod` | ❌ | | diff --git a/charts/external-dns/templates/clusterrole.yaml b/charts/external-dns/templates/clusterrole.yaml index 6a7db92e9..9cab6a6eb 100644 --- a/charts/external-dns/templates/clusterrole.yaml +++ b/charts/external-dns/templates/clusterrole.yaml @@ -1,12 +1,12 @@ {{- if .Values.rbac.create -}} apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole +kind: {{ .Values.namespaced | ternary "Role" "ClusterRole" }} metadata: name: {{ template "external-dns.fullname" . }} labels: {{- include "external-dns.labels" . | nindent 4 }} rules: -{{- if or (has "node" .Values.sources) (has "pod" .Values.sources) (has "service" .Values.sources) (has "contour-httpproxy" .Values.sources) (has "gloo-proxy" .Values.sources) (has "openshift-route" .Values.sources) (has "skipper-routegroup" .Values.sources) }} +{{- if and (not .Values.namespaced) (or (has "node" .Values.sources) (has "pod" .Values.sources) (has "service" .Values.sources) (has "contour-httpproxy" .Values.sources) (has "gloo-proxy" .Values.sources) (has "openshift-route" .Values.sources) (has "skipper-routegroup" .Values.sources)) }} - apiGroups: [""] resources: ["nodes"] verbs: ["list","watch"] diff --git a/charts/external-dns/templates/clusterrolebinding.yaml b/charts/external-dns/templates/clusterrolebinding.yaml index 9028c6f96..74a51476f 100644 --- a/charts/external-dns/templates/clusterrolebinding.yaml +++ b/charts/external-dns/templates/clusterrolebinding.yaml @@ -1,13 +1,13 @@ {{- if .Values.rbac.create -}} apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding +kind: {{ .Values.namespaced | ternary "RoleBinding" "ClusterRoleBinding" }} metadata: name: {{ printf "%s-viewer" (include "external-dns.fullname" .) }} labels: {{- include "external-dns.labels" . | nindent 4 }} roleRef: apiGroup: rbac.authorization.k8s.io - kind: ClusterRole + kind: {{ .Values.namespaced | ternary "Role" "ClusterRole" }} name: {{ template "external-dns.fullname" . }} subjects: - kind: ServiceAccount diff --git a/charts/external-dns/templates/deployment.yaml b/charts/external-dns/templates/deployment.yaml index 70952675d..5c3e1128f 100644 --- a/charts/external-dns/templates/deployment.yaml +++ b/charts/external-dns/templates/deployment.yaml @@ -89,6 +89,9 @@ spec: - --txt-suffix={{ .Values.txtSuffix }} {{- end }} {{- end }} + {{- if .Values.namespaced }} + - --namespace={{ .Release.Namespace }} + {{- end }} {{- range .Values.domainFilters }} - --domain-filter={{ . }} {{- end }} diff --git a/charts/external-dns/values.yaml b/charts/external-dns/values.yaml index 5b30a9c0e..6e0f80265 100644 --- a/charts/external-dns/values.yaml +++ b/charts/external-dns/values.yaml @@ -151,6 +151,8 @@ logFormat: text interval: 1m triggerLoopOnEvent: false +namespaced: false + sources: - service - ingress From 37ab60f78726791adb6b3db69e84fb72aa30fee3 Mon Sep 17 00:00:00 2001 From: Steve Hipwell Date: Thu, 30 Mar 2023 09:57:26 +0100 Subject: [PATCH 062/117] feat(chart): Updated chart to ExternalDNS v0.13.4 Signed-off-by: Steve Hipwell --- charts/external-dns/CHANGELOG.md | 12 ++++++++---- charts/external-dns/Chart.yaml | 18 ++++++++++++------ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/charts/external-dns/CHANGELOG.md b/charts/external-dns/CHANGELOG.md index 289be70d1..5c2d88be3 100644 --- a/charts/external-dns/CHANGELOG.md +++ b/charts/external-dns/CHANGELOG.md @@ -7,16 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- -## [v1.12.2] - UNRELEASED -### Highlights +## [UNRELEASED] + ### All Changes -- Added RBAC for Gateway-API resources to clusterrole [#3499](https://github.com/kubernetes-sigs/external-dns/pull/3499). [@michaelvl](https://github.com/MichaelVL) ## [v1.12.2] - UNRELEASED ### All Changes -- Add RBAC to be able to support the F5 VirtualServer `Source` ([#3503](https://github.com/kubernetes-sigs/external-dns/pull/3503)) [@mikejoh](https://github.com/mikejoh) +- Added support for ServiceMonitor relabelling. ([#3366](https://github.com/kubernetes-sigs/external-dns/pull/3366)) [@jkroepke](https://github.com/jkroepke) +- Updated chart icon path. ([#3492](https://github.com/kubernetes-sigs/external-dns/pull/3494)) [kundan2707](https://github.com/kundan2707) +- Added RBAC for Gateway-API resources to ClusterRole. ([#3499](https://github.com/kubernetes-sigs/external-dns/pull/3499)) [@michaelvl](https://github.com/MichaelVL) +- Added RBAC for F5 VirtualServer to ClusterRole. ([#3503](https://github.com/kubernetes-sigs/external-dns/pull/3503)) [@mikejoh](https://github.com/mikejoh) +- Added support for running ExternalDNS with namespaced scope. ([#3403](https://github.com/kubernetes-sigs/external-dns/pull/3403)) [@jkroepke](https://github.com/jkroepke) +- Updated _ExternalDNS_ version to [v0.13.4](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.13.4). ([#3516](https://github.com/kubernetes-sigs/external-dns/pull/3516)) [@stevehipwell](https://github.com/stevehipwell) ## [v1.12.1] - 2023-02-06 diff --git a/charts/external-dns/Chart.yaml b/charts/external-dns/Chart.yaml index 29a31c935..0e4e2cd9b 100644 --- a/charts/external-dns/Chart.yaml +++ b/charts/external-dns/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: external-dns description: ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with DNS providers. type: application -version: 1.12.1 -appVersion: 0.13.2 +version: 1.12.2 +appVersion: 0.13.4 keywords: - kubernetes - externaldns @@ -20,9 +20,15 @@ maintainers: email: steve.hipwell@gmail.com annotations: artifacthub.io/changes: | - - kind: changed - description: "Updated ExternalDNS version to v0.13.2." - kind: added - description: "Added secretConfiguration.subPath to mount specific files from secret as a sub-path." + description: "Added support for ServiceMonitor relabelling." - kind: changed - description: "Changed to use registry.k8s.io instead of k8s.gcr.io." + description: "Updated chart icon path." + - kind: added + description: "Added RBAC for Gateway-API resources to ClusterRole." + - kind: added + description: "Added RBAC for F5 VirtualServer to ClusterRole." + - kind: added + description: "Added support for running ExternalDNS with namespaced scope." + - kind: changed + description: "Updated _ExternalDNS_ version to [v0.13.4](https://github.com/kubernetes-sigs/external-dns/releases/tag/v0.13.4)." From 205e8c0334fe07065de0fedb1f92fa85a43450b9 Mon Sep 17 00:00:00 2001 From: Seweryn Chlewicki Date: Thu, 30 Mar 2023 19:58:18 +0100 Subject: [PATCH 063/117] Add support for MX records in Azure, GCP and AWS --- endpoint/endpoint.go | 2 + provider/aws/aws.go | 11 +++- provider/azure/azure.go | 36 ++++++++++++- provider/azure/azure_private_dns.go | 25 +++++++++ provider/azure/azure_privatedns_test.go | 25 +++++++++ provider/azure/azure_test.go | 27 ++++++++++ provider/azure/common.go | 33 ++++++++++++ provider/azure/common_test.go | 71 +++++++++++++++++++++++++ provider/google/google.go | 18 ++++++- provider/google/google_test.go | 2 + 10 files changed, 247 insertions(+), 3 deletions(-) create mode 100644 provider/azure/common.go create mode 100644 provider/azure/common_test.go diff --git a/endpoint/endpoint.go b/endpoint/endpoint.go index 428a7961b..dba536dcd 100644 --- a/endpoint/endpoint.go +++ b/endpoint/endpoint.go @@ -40,6 +40,8 @@ const ( RecordTypeNS = "NS" // RecordTypePTR is a RecordType enum value RecordTypePTR = "PTR" + // RecordTypeMX is a RecordType enum value + RecordTypeMX = "MX" ) // TTL is a structure defining the TTL of a DNS record diff --git a/provider/aws/aws.go b/provider/aws/aws.go index 5f7457420..4a21e39dd 100644 --- a/provider/aws/aws.go +++ b/provider/aws/aws.go @@ -373,7 +373,7 @@ func (p *AWSProvider) records(ctx context.Context, zones map[string]*route53.Hos for _, r := range resp.ResourceRecordSets { newEndpoints := make([]*endpoint.Endpoint, 0) - if !provider.SupportedRecordType(aws.StringValue(r.Type)) { + if !p.SupportedRecordType(aws.StringValue(r.Type)) { continue } @@ -1059,3 +1059,12 @@ func canonicalHostedZone(hostname string) string { func cleanZoneID(id string) string { return strings.TrimPrefix(id, "/hostedzone/") } + +func (p *AWSProvider) SupportedRecordType(recordType string) bool { + switch recordType { + case "MX": + return true + default: + return provider.SupportedRecordType(recordType) + } +} diff --git a/provider/azure/azure.go b/provider/azure/azure.go index dfab96eff..24a0e0ecb 100644 --- a/provider/azure/azure.go +++ b/provider/azure/azure.go @@ -109,7 +109,7 @@ func (p *AzureProvider) Records(ctx context.Context) (endpoints []*endpoint.Endp return true } recordType := strings.TrimPrefix(*recordSet.Type, "Microsoft.Network/dnszones/") - if !provider.SupportedRecordType(recordType) { + if !p.SupportedRecordType(recordType) { return true } name := formatAzureDNSName(*recordSet.Name, *zone.Name) @@ -190,6 +190,15 @@ func (p *AzureProvider) zones(ctx context.Context) ([]dns.Zone, error) { return zones, nil } +func (p *AzureProvider) SupportedRecordType(recordType string) bool { + switch recordType { + case "MX": + return true + default: + return provider.SupportedRecordType(recordType) + } +} + func (p *AzureProvider) iterateRecords(ctx context.Context, zoneName string, callback func(dns.RecordSet) bool) error { log.Debugf("Retrieving Azure DNS records for zone '%s'.", zoneName) @@ -377,6 +386,21 @@ func (p *AzureProvider) newRecordSet(endpoint *endpoint.Endpoint) (dns.RecordSet }, }, }, nil + case dns.MX: + mxRecords := make([]dns.MxRecord, len(endpoint.Targets)) + for i, target := range endpoint.Targets { + mxRecord, err := parseMxTarget[dns.MxRecord](target) + if err != nil { + return dns.RecordSet{}, err + } + mxRecords[i] = mxRecord + } + return dns.RecordSet{ + RecordSetProperties: &dns.RecordSetProperties{ + TTL: to.Int64Ptr(ttl), + MxRecords: &mxRecords, + }, + }, nil case dns.TXT: return dns.RecordSet{ RecordSetProperties: &dns.RecordSetProperties{ @@ -425,6 +449,16 @@ func extractAzureTargets(recordSet *dns.RecordSet) []string { return []string{*cnameRecord.Cname} } + // Check for MX records + mxRecords := properties.MxRecords + if mxRecords != nil && len(*mxRecords) > 0 && (*mxRecords)[0].Exchange != nil { + targets := make([]string, len(*mxRecords)) + for i, mxRecord := range *mxRecords { + targets[i] = fmt.Sprintf("%d %s", *mxRecord.Preference, *mxRecord.Exchange) + } + return targets + } + // Check for TXT records txtRecords := properties.TxtRecords if txtRecords != nil && len(*txtRecords) > 0 && (*txtRecords)[0].Value != nil { diff --git a/provider/azure/azure_private_dns.go b/provider/azure/azure_private_dns.go index 374b5e5d6..fd5733bfe 100644 --- a/provider/azure/azure_private_dns.go +++ b/provider/azure/azure_private_dns.go @@ -367,6 +367,21 @@ func (p *AzurePrivateDNSProvider) newRecordSet(endpoint *endpoint.Endpoint) (pri }, }, }, nil + case privatedns.MX: + mxRecords := make([]privatedns.MxRecord, len(endpoint.Targets)) + for i, target := range endpoint.Targets { + mxRecord, err := parseMxTarget[privatedns.MxRecord](target) + if err != nil { + return privatedns.RecordSet{}, err + } + mxRecords[i] = mxRecord + } + return privatedns.RecordSet{ + RecordSetProperties: &privatedns.RecordSetProperties{ + TTL: to.Int64Ptr(ttl), + MxRecords: &mxRecords, + }, + }, nil case privatedns.TXT: return privatedns.RecordSet{ RecordSetProperties: &privatedns.RecordSetProperties{ @@ -407,6 +422,16 @@ func extractAzurePrivateDNSTargets(recordSet *privatedns.RecordSet) []string { return []string{*cnameRecord.Cname} } + // Check for MX records + mxRecords := properties.MxRecords + if mxRecords != nil && len(*mxRecords) > 0 && (*mxRecords)[0].Exchange != nil { + targets := make([]string, len(*mxRecords)) + for i, mxRecord := range *mxRecords { + targets[i] = fmt.Sprintf("%d %s", *mxRecord.Preference, *mxRecord.Exchange) + } + return targets + } + // Check for TXT records txtRecords := properties.TxtRecords if txtRecords != nil && len(*txtRecords) > 0 && (*txtRecords)[0].Value != nil { diff --git a/provider/azure/azure_privatedns_test.go b/provider/azure/azure_privatedns_test.go index f35720154..e728659c4 100644 --- a/provider/azure/azure_privatedns_test.go +++ b/provider/azure/azure_privatedns_test.go @@ -123,6 +123,17 @@ func privateCNameRecordSetPropertiesGetter(values []string, ttl int64) *privated } } +func privateMXRecordSetPropertiesGetter(values []string, ttl int64) *privatedns.RecordSetProperties { + mxRecords := make([]privatedns.MxRecord, len(values)) + for i, target := range values { + mxRecords[i], _ = parseMxTarget[privatedns.MxRecord](target) + } + return &privatedns.RecordSetProperties{ + TTL: to.Int64Ptr(ttl), + MxRecords: &mxRecords, + } +} + func privateTxtRecordSetPropertiesGetter(values []string, ttl int64) *privatedns.RecordSetProperties { return &privatedns.RecordSetProperties{ TTL: to.Int64Ptr(ttl), @@ -156,6 +167,8 @@ func createPrivateMockRecordSetMultiWithTTL(name, recordType string, ttl int64, getterFunc = privateARecordSetPropertiesGetter case endpoint.RecordTypeCNAME: getterFunc = privateCNameRecordSetPropertiesGetter + case endpoint.RecordTypeMX: + getterFunc = privateMXRecordSetPropertiesGetter case endpoint.RecordTypeTXT: getterFunc = privateTxtRecordSetPropertiesGetter default: @@ -266,6 +279,7 @@ func TestAzurePrivateDNSRecord(t *testing.T) { createPrivateMockRecordSetWithTTL("nginx", endpoint.RecordTypeA, "123.123.123.123", 3600), createPrivateMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL), createPrivateMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10), + createPrivateMockRecordSetWithTTL("mail", endpoint.RecordTypeMX, "10 example.com", 4000), }) if err != nil { t.Fatal(err) @@ -281,6 +295,7 @@ func TestAzurePrivateDNSRecord(t *testing.T) { endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123"), endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"), endpoint.NewEndpointWithTTL("hack.example.com", endpoint.RecordTypeCNAME, 10, "hack.azurewebsites.net"), + endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeMX, 4000, "10 example.com"), } validateAzureEndpoints(t, actual, expected) @@ -299,6 +314,7 @@ func TestAzurePrivateDNSMultiRecord(t *testing.T) { createPrivateMockRecordSetMultiWithTTL("nginx", endpoint.RecordTypeA, 3600, "123.123.123.123", "234.234.234.234"), createPrivateMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL), createPrivateMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10), + createPrivateMockRecordSetMultiWithTTL("mail", endpoint.RecordTypeMX, 4000, "10 example.com", "20 backup.example.com"), }) if err != nil { t.Fatal(err) @@ -314,6 +330,7 @@ func TestAzurePrivateDNSMultiRecord(t *testing.T) { endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123", "234.234.234.234"), endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"), endpoint.NewEndpointWithTTL("hack.example.com", endpoint.RecordTypeCNAME, 10, "hack.azurewebsites.net"), + endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeMX, 4000, "10 example.com", "20 backup.example.com"), } validateAzureEndpoints(t, actual, expected) @@ -329,6 +346,7 @@ func TestAzurePrivateDNSApplyChanges(t *testing.T) { endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, ""), endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, ""), endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, ""), + endpoint.NewEndpoint("oldmail.example.com", endpoint.RecordTypeMX, ""), }) validateAzureEndpoints(t, recordsClient.updatedEndpoints, []*endpoint.Endpoint{ @@ -342,6 +360,9 @@ func TestAzurePrivateDNSApplyChanges(t *testing.T) { endpoint.NewEndpointWithTTL("other.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"), endpoint.NewEndpointWithTTL("new.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"), endpoint.NewEndpointWithTTL("newcname.example.com", endpoint.RecordTypeCNAME, 10, "other.com"), + endpoint.NewEndpointWithTTL("newmail.example.com", endpoint.RecordTypeMX, 7200, "40 bar.other.com"), + endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 other.com"), + endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"), }) } @@ -401,17 +422,21 @@ func testAzurePrivateDNSApplyChangesInternal(t *testing.T, dryRun bool, client P endpoint.NewEndpoint("other.com", endpoint.RecordTypeTXT, "tag"), endpoint.NewEndpoint("nope.com", endpoint.RecordTypeA, "4.4.4.4"), endpoint.NewEndpoint("nope.com", endpoint.RecordTypeTXT, "tag"), + endpoint.NewEndpoint("mail.example.com", endpoint.RecordTypeMX, "10 other.com"), + endpoint.NewEndpoint("mail.example.com", endpoint.RecordTypeTXT, "tag"), } currentRecords := []*endpoint.Endpoint{ endpoint.NewEndpoint("old.example.com", endpoint.RecordTypeA, "121.212.121.212"), endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, "other.com"), endpoint.NewEndpoint("old.nope.com", endpoint.RecordTypeA, "121.212.121.212"), + endpoint.NewEndpoint("oldmail.example.com", endpoint.RecordTypeMX, "20 foo.other.com"), } updatedRecords := []*endpoint.Endpoint{ endpoint.NewEndpointWithTTL("new.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"), endpoint.NewEndpointWithTTL("newcname.example.com", endpoint.RecordTypeCNAME, 10, "other.com"), endpoint.NewEndpoint("new.nope.com", endpoint.RecordTypeA, "222.111.222.111"), + endpoint.NewEndpointWithTTL("newmail.example.com", endpoint.RecordTypeMX, 7200, "40 bar.other.com"), } deleteRecords := []*endpoint.Endpoint{ diff --git a/provider/azure/azure_test.go b/provider/azure/azure_test.go index 0598dd4f3..050ab3d92 100644 --- a/provider/azure/azure_test.go +++ b/provider/azure/azure_test.go @@ -122,6 +122,17 @@ func cNameRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetPr } } +func mxRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetProperties { + mxRecords := make([]dns.MxRecord, len(values)) + for i, target := range values { + mxRecords[i], _ = parseMxTarget[dns.MxRecord](target) + } + return &dns.RecordSetProperties{ + TTL: to.Int64Ptr(ttl), + MxRecords: &mxRecords, + } +} + func txtRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetProperties { return &dns.RecordSetProperties{ TTL: to.Int64Ptr(ttl), @@ -155,6 +166,8 @@ func createMockRecordSetMultiWithTTL(name, recordType string, ttl int64, values getterFunc = aRecordSetPropertiesGetter case endpoint.RecordTypeCNAME: getterFunc = cNameRecordSetPropertiesGetter + case endpoint.RecordTypeMX: + getterFunc = mxRecordSetPropertiesGetter case endpoint.RecordTypeTXT: getterFunc = txtRecordSetPropertiesGetter default: @@ -271,6 +284,7 @@ func TestAzureRecord(t *testing.T) { createMockRecordSetWithTTL("nginx", endpoint.RecordTypeA, "123.123.123.123", 3600), createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL), createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10), + createMockRecordSetMultiWithTTL("mail", endpoint.RecordTypeMX, 4000, "10 example.com"), }) if err != nil { t.Fatal(err) @@ -287,6 +301,7 @@ func TestAzureRecord(t *testing.T) { endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123"), endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"), endpoint.NewEndpointWithTTL("hack.example.com", endpoint.RecordTypeCNAME, 10, "hack.azurewebsites.net"), + endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeMX, 4000, "10 example.com"), } validateAzureEndpoints(t, actual, expected) @@ -305,6 +320,7 @@ func TestAzureMultiRecord(t *testing.T) { createMockRecordSetMultiWithTTL("nginx", endpoint.RecordTypeA, 3600, "123.123.123.123", "234.234.234.234"), createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL), createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10), + createMockRecordSetMultiWithTTL("mail", endpoint.RecordTypeMX, 4000, "10 example.com", "20 backup.example.com"), }) if err != nil { t.Fatal(err) @@ -321,6 +337,7 @@ func TestAzureMultiRecord(t *testing.T) { endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123", "234.234.234.234"), endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"), endpoint.NewEndpointWithTTL("hack.example.com", endpoint.RecordTypeCNAME, 10, "hack.azurewebsites.net"), + endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeMX, 4000, "10 example.com", "20 backup.example.com"), } validateAzureEndpoints(t, actual, expected) @@ -336,6 +353,7 @@ func TestAzureApplyChanges(t *testing.T) { endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, ""), endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, ""), endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, ""), + endpoint.NewEndpoint("oldmail.example.com", endpoint.RecordTypeMX, ""), }) validateAzureEndpoints(t, recordsClient.updatedEndpoints, []*endpoint.Endpoint{ @@ -349,6 +367,9 @@ func TestAzureApplyChanges(t *testing.T) { endpoint.NewEndpointWithTTL("other.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"), endpoint.NewEndpointWithTTL("new.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"), endpoint.NewEndpointWithTTL("newcname.example.com", endpoint.RecordTypeCNAME, 10, "other.com"), + endpoint.NewEndpointWithTTL("newmail.example.com", endpoint.RecordTypeMX, 7200, "40 bar.other.com"), + endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 other.com"), + endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"), }) } @@ -410,17 +431,21 @@ func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordSetsC endpoint.NewEndpoint("other.com", endpoint.RecordTypeTXT, "tag"), endpoint.NewEndpoint("nope.com", endpoint.RecordTypeA, "4.4.4.4"), endpoint.NewEndpoint("nope.com", endpoint.RecordTypeTXT, "tag"), + endpoint.NewEndpoint("mail.example.com", endpoint.RecordTypeMX, "10 other.com"), + endpoint.NewEndpoint("mail.example.com", endpoint.RecordTypeTXT, "tag"), } currentRecords := []*endpoint.Endpoint{ endpoint.NewEndpoint("old.example.com", endpoint.RecordTypeA, "121.212.121.212"), endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, "other.com"), endpoint.NewEndpoint("old.nope.com", endpoint.RecordTypeA, "121.212.121.212"), + endpoint.NewEndpoint("oldmail.example.com", endpoint.RecordTypeMX, "20 foo.other.com"), } updatedRecords := []*endpoint.Endpoint{ endpoint.NewEndpointWithTTL("new.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"), endpoint.NewEndpointWithTTL("newcname.example.com", endpoint.RecordTypeCNAME, 10, "other.com"), endpoint.NewEndpoint("new.nope.com", endpoint.RecordTypeA, "222.111.222.111"), + endpoint.NewEndpointWithTTL("newmail.example.com", endpoint.RecordTypeMX, 7200, "40 bar.other.com"), } deleteRecords := []*endpoint.Endpoint{ @@ -455,6 +480,7 @@ func TestAzureNameFilter(t *testing.T) { createMockRecordSetWithTTL("test.nginx", endpoint.RecordTypeA, "123.123.123.123", 3600), createMockRecordSetWithTTL("nginx", endpoint.RecordTypeA, "123.123.123.123", 3600), createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL), + createMockRecordSetWithTTL("mail.nginx", endpoint.RecordTypeMX, "20 example.com", recordTTL), createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10), }) if err != nil { @@ -470,6 +496,7 @@ func TestAzureNameFilter(t *testing.T) { endpoint.NewEndpointWithTTL("test.nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123"), endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123"), endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"), + endpoint.NewEndpointWithTTL("mail.nginx.example.com", endpoint.RecordTypeMX, recordTTL, "20 example.com"), } validateAzureEndpoints(t, actual, expected) diff --git a/provider/azure/common.go b/provider/azure/common.go new file mode 100644 index 000000000..15ecf64cf --- /dev/null +++ b/provider/azure/common.go @@ -0,0 +1,33 @@ +package azure + +import ( + "fmt" + "strconv" + "strings" + + "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" + "github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns" + "github.com/Azure/go-autorest/autorest/to" +) + +// Helper function (shared with test code) +func parseMxTarget[T dns.MxRecord | privatedns.MxRecord](mxTarget string) (T, error) { + targetParts := strings.SplitN(mxTarget, " ", 2) + + if len(targetParts) != 2 { + return T{}, fmt.Errorf("mx target needs to be of form '10 example.com'") + } + + preferenceRaw, exchange := targetParts[0], targetParts[1] + preference, err := strconv.ParseInt(preferenceRaw, 10, 32) + + if err != nil { + return T{}, fmt.Errorf("invalid preference specified") + } + res := T{ + Preference: to.Int32Ptr(int32(preference)), + Exchange: to.StringPtr(exchange), + } + + return res, nil +} diff --git a/provider/azure/common_test.go b/provider/azure/common_test.go new file mode 100644 index 000000000..b7f4d8dfd --- /dev/null +++ b/provider/azure/common_test.go @@ -0,0 +1,71 @@ +package azure + +import ( + "fmt" + "testing" + + "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" + "github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns" + "github.com/Azure/go-autorest/autorest/to" + + "github.com/stretchr/testify/assert" +) + +func Test_parseMxTarget(t *testing.T) { + type testCase[T interface { + dns.MxRecord | privatedns.MxRecord + }] struct { + name string + args string + want T + wantErr assert.ErrorAssertionFunc + } + + tests := []testCase[dns.MxRecord]{ + { + name: "valid mx target", + args: "10 example.com", + want: dns.MxRecord{ + Preference: to.Int32Ptr(int32(10)), + Exchange: to.StringPtr("example.com"), + }, + wantErr: assert.NoError, + }, + { + name: "valid mx target with a subdomain", + args: "99 foo-bar.example.com", + want: dns.MxRecord{ + Preference: to.Int32Ptr(int32(99)), + Exchange: to.StringPtr("foo-bar.example.com"), + }, + wantErr: assert.NoError, + }, + { + name: "invalid mx target with misplaced preference and exchange", + args: "example.com 10", + want: dns.MxRecord{}, + wantErr: assert.Error, + }, + { + name: "invalid mx target without preference", + args: "example.com", + want: dns.MxRecord{}, + wantErr: assert.Error, + }, + { + name: "invalid mx target with non numeric preference", + args: "aa example.com", + want: dns.MxRecord{}, + wantErr: assert.Error, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseMxTarget[dns.MxRecord](tt.args) + if !tt.wantErr(t, err, fmt.Sprintf("parseMxTarget(%v)", tt.args)) { + return + } + assert.Equalf(t, tt.want, got, "parseMxTarget(%v)", tt.args) + }) + } +} diff --git a/provider/google/google.go b/provider/google/google.go index d6c53ef6a..5fce653f5 100644 --- a/provider/google/google.go +++ b/provider/google/google.go @@ -213,7 +213,7 @@ func (p *GoogleProvider) Records(ctx context.Context) (endpoints []*endpoint.End f := func(resp *dns.ResourceRecordSetsListResponse) error { for _, r := range resp.Rrsets { - if !provider.SupportedRecordType(r.Type) { + if !p.SupportedRecordType(r.Type) { continue } endpoints = append(endpoints, endpoint.NewEndpointWithTTL(r.Name, r.Type, endpoint.TTL(r.Ttl), r.Rrdatas...)) @@ -273,6 +273,16 @@ func (p *GoogleProvider) ApplyChanges(ctx context.Context, changes *plan.Changes return p.submitChange(ctx, change) } +// SupportedRecordType returns true if the record type is supported by the provider +func (p *GoogleProvider) SupportedRecordType(recordType string) bool { + switch recordType { + case "MX": + return true + default: + return provider.SupportedRecordType(recordType) + } +} + // newFilteredRecords returns a collection of RecordSets based on the given endpoints and domainFilter. func (p *GoogleProvider) newFilteredRecords(endpoints []*endpoint.Endpoint) []*dns.ResourceRecordSet { records := []*dns.ResourceRecordSet{} @@ -447,6 +457,12 @@ func newRecord(ep *endpoint.Endpoint) *dns.ResourceRecordSet { targets[0] = provider.EnsureTrailingDot(targets[0]) } + if ep.RecordType == endpoint.RecordTypeMX { + for i, mxRecord := range ep.Targets { + targets[i] = provider.EnsureTrailingDot(mxRecord) + } + } + // no annotation results in a Ttl of 0, default to 300 for backwards-compatibility var ttl int64 = googleRecordTTL if ep.RecordTTL.IsConfigured() { diff --git a/provider/google/google_test.go b/provider/google/google_test.go index 3fe610091..bc321200b 100644 --- a/provider/google/google_test.go +++ b/provider/google/google_test.go @@ -512,6 +512,7 @@ func TestNewFilteredRecords(t *testing.T) { endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, 4000, "bar.elb.amazonaws.com"), // test fallback to Ttl:300 when Ttl==0 : endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, 0, "8.8.8.8"), + endpoint.NewEndpointWithTTL("update-test-mx.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeMX, 6000, "10 mail.elb.amazonaws.com"), endpoint.NewEndpoint("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "8.8.8.8"), endpoint.NewEndpoint("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, "qux.elb.amazonaws.com"), }) @@ -521,6 +522,7 @@ func TestNewFilteredRecords(t *testing.T) { {Name: "delete-test.zone-2.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"8.8.4.4"}, Type: "A", Ttl: 120}, {Name: "update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"bar.elb.amazonaws.com."}, Type: "CNAME", Ttl: 4000}, {Name: "update-test.zone-1.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"8.8.8.8"}, Type: "A", Ttl: 300}, + {Name: "update-test-mx.zone-1.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"10 mail.elb.amazonaws.com."}, Type: "MX", Ttl: 6000}, {Name: "delete-test.zone-1.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"8.8.8.8"}, Type: "A", Ttl: 300}, {Name: "delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"qux.elb.amazonaws.com."}, Type: "CNAME", Ttl: 300}, }) From 06227c1fbf4d5938cdb20c5b0e650e363f168837 Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Sun, 28 Nov 2021 11:37:40 -0800 Subject: [PATCH 064/117] Make unit tests more strict about record type --- source/contour_httpproxy_test.go | 136 ++++++++++++-------- source/ingress_test.go | 146 ++++++++++++--------- source/istio_gateway_test.go | 156 +++++++++++++---------- source/istio_virtualservice_test.go | 134 ++++++++++++-------- source/openshift_route_test.go | 22 ++-- source/service_test.go | 188 ++++++++++++++-------------- source/shared_test.go | 2 +- source/skipper_routegroup_test.go | 129 +++++++++++-------- 8 files changed, 528 insertions(+), 385 deletions(-) diff --git a/source/contour_httpproxy_test.go b/source/contour_httpproxy_test.go index 1905d0eee..e90bd059a 100644 --- a/source/contour_httpproxy_test.go +++ b/source/contour_httpproxy_test.go @@ -220,8 +220,9 @@ func testEndpointsFromHTTPProxy(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -235,8 +236,9 @@ func testEndpointsFromHTTPProxy(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, }, }, @@ -251,12 +253,14 @@ func testEndpointsFromHTTPProxy(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"8.8.8.8", "127.0.0.1"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8", "127.0.0.1"}, }, { - DNSName: "foo.bar", - Targets: endpoint.Targets{"elb.com", "alb.com"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"elb.com", "alb.com"}, }, }, }, @@ -342,20 +346,24 @@ func testHTTPProxyEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "example.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -380,20 +388,24 @@ func testHTTPProxyEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "example.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -418,12 +430,14 @@ func testHTTPProxyEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "example.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -446,8 +460,9 @@ func testHTTPProxyEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, }, }, @@ -509,8 +524,9 @@ func testHTTPProxyEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, }, }, @@ -551,8 +567,9 @@ func testHTTPProxyEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, }, }, @@ -593,12 +610,14 @@ func testHTTPProxyEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "fake1.ext-dns.test.com", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "fake1.ext-dns.test.com", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "fake1.ext-dns.test.com", - Targets: endpoint.Targets{"elb.com"}, + DNSName: "fake1.ext-dns.test.com", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"elb.com"}, }, }, fqdnTemplate: "{{.Name}}.ext-dns.test.com", @@ -882,19 +901,22 @@ func testHTTPProxyEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"httpproxy-target.com"}, - RecordTTL: endpoint.TTL(6), + DNSName: "example.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"httpproxy-target.com"}, + RecordTTL: endpoint.TTL(6), }, { - DNSName: "example2.org", - Targets: endpoint.Targets{"httpproxy-target.com"}, - RecordTTL: endpoint.TTL(1), + DNSName: "example2.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"httpproxy-target.com"}, + RecordTTL: endpoint.TTL(1), }, { - DNSName: "example3.org", - Targets: endpoint.Targets{"httpproxy-target.com"}, - RecordTTL: endpoint.TTL(10), + DNSName: "example3.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"httpproxy-target.com"}, + RecordTTL: endpoint.TTL(10), }, }, }, @@ -997,20 +1019,24 @@ func testHTTPProxyEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "example.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, ignoreHostnameAnnotation: true, diff --git a/source/ingress_test.go b/source/ingress_test.go index 6c345d09b..ae3dd3b7c 100644 --- a/source/ingress_test.go +++ b/source/ingress_test.go @@ -177,8 +177,9 @@ func testEndpointsFromIngress(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -190,8 +191,9 @@ func testEndpointsFromIngress(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, }, }, @@ -204,12 +206,14 @@ func testEndpointsFromIngress(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"8.8.8.8", "127.0.0.1"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8", "127.0.0.1"}, }, { - DNSName: "foo.bar", - Targets: endpoint.Targets{"elb.com", "alb.com"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"elb.com", "alb.com"}, }, }, }, @@ -270,12 +274,14 @@ func testEndpointsFromIngressHostnameSourceAnnotation(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, { - DNSName: "foo.baz", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "foo.baz", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -287,8 +293,9 @@ func testEndpointsFromIngressHostnameSourceAnnotation(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -301,12 +308,14 @@ func testEndpointsFromIngressHostnameSourceAnnotation(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, { - DNSName: "foo.baz", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "foo.baz", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -319,8 +328,9 @@ func testEndpointsFromIngressHostnameSourceAnnotation(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -333,8 +343,9 @@ func testEndpointsFromIngressHostnameSourceAnnotation(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.baz", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "foo.baz", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -387,12 +398,14 @@ func testIngressEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -435,12 +448,14 @@ func testIngressEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -463,8 +478,9 @@ func testIngressEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, }, }, @@ -485,8 +501,9 @@ func testIngressEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, }, }, @@ -542,8 +559,9 @@ func testIngressEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, }, }, @@ -580,8 +598,9 @@ func testIngressEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, }, }, @@ -618,12 +637,14 @@ func testIngressEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "fake1.ext-dns.test.com", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "fake1.ext-dns.test.com", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "fake1.ext-dns.test.com", - Targets: endpoint.Targets{"elb.com"}, + DNSName: "fake1.ext-dns.test.com", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"elb.com"}, }, }, fqdnTemplate: "{{.Name}}.ext-dns.test.com", @@ -973,19 +994,22 @@ func testIngressEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"ingress-target.com"}, - RecordTTL: endpoint.TTL(6), + DNSName: "example.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"ingress-target.com"}, + RecordTTL: endpoint.TTL(6), }, { - DNSName: "example2.org", - Targets: endpoint.Targets{"ingress-target.com"}, - RecordTTL: endpoint.TTL(1), + DNSName: "example2.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"ingress-target.com"}, + RecordTTL: endpoint.TTL(1), }, { - DNSName: "example3.org", - Targets: endpoint.Targets{"ingress-target.com"}, - RecordTTL: endpoint.TTL(10), + DNSName: "example3.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"ingress-target.com"}, + RecordTTL: endpoint.TTL(10), }, }, }, @@ -1132,12 +1156,14 @@ func testIngressEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -1169,8 +1195,9 @@ func testIngressEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"1.2.3.4"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"1.2.3.4"}, }, }, }, @@ -1189,8 +1216,9 @@ func testIngressEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, }, }, diff --git a/source/istio_gateway_test.go b/source/istio_gateway_test.go index fefa46c53..535972a34 100644 --- a/source/istio_gateway_test.go +++ b/source/istio_gateway_test.go @@ -184,8 +184,9 @@ func testEndpointsFromGatewayConfig(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -203,8 +204,9 @@ func testEndpointsFromGatewayConfig(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -222,8 +224,9 @@ func testEndpointsFromGatewayConfig(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, }, }, @@ -242,12 +245,14 @@ func testEndpointsFromGatewayConfig(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"8.8.8.8", "127.0.0.1"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8", "127.0.0.1"}, }, { - DNSName: "foo.bar", - Targets: endpoint.Targets{"elb.com", "alb.com"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"elb.com", "alb.com"}, }, }, }, @@ -310,8 +315,9 @@ func testEndpointsFromGatewayConfig(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"lb.com", "lb2.com"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com", "lb2.com"}, }, }, }, @@ -376,20 +382,24 @@ func testGatewayEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "example.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -416,20 +426,24 @@ func testGatewayEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "example.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -452,12 +466,14 @@ func testGatewayEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "example.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -482,8 +498,9 @@ func testGatewayEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, }, }, @@ -551,8 +568,9 @@ func testGatewayEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, }, }, @@ -597,8 +615,9 @@ func testGatewayEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, }, }, @@ -643,12 +662,14 @@ func testGatewayEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "fake1.ext-dns.test.com", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "fake1.ext-dns.test.com", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "fake1.ext-dns.test.com", - Targets: endpoint.Targets{"elb.com"}, + DNSName: "fake1.ext-dns.test.com", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"elb.com"}, }, }, fqdnTemplate: "{{.Name}}.ext-dns.test.com", @@ -948,19 +969,22 @@ func testGatewayEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"gateway-target.com"}, - RecordTTL: endpoint.TTL(6), + DNSName: "example.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"gateway-target.com"}, + RecordTTL: endpoint.TTL(6), }, { - DNSName: "example2.org", - Targets: endpoint.Targets{"gateway-target.com"}, - RecordTTL: endpoint.TTL(1), + DNSName: "example2.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"gateway-target.com"}, + RecordTTL: endpoint.TTL(1), }, { - DNSName: "example3.org", - Targets: endpoint.Targets{"gateway-target.com"}, - RecordTTL: endpoint.TTL(10), + DNSName: "example3.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"gateway-target.com"}, + RecordTTL: endpoint.TTL(10), }, }, }, @@ -1069,20 +1093,24 @@ func testGatewayEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "example.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, ignoreHostnameAnnotation: true, @@ -1137,12 +1165,14 @@ func testGatewayEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "fake1.dns-through-hostname.com", - Targets: endpoint.Targets{"1.2.3.4"}, + DNSName: "fake1.dns-through-hostname.com", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"1.2.3.4"}, }, { - DNSName: "fake2.dns-through-hostname.com", - Targets: endpoint.Targets{"1.2.3.4"}, + DNSName: "fake2.dns-through-hostname.com", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"1.2.3.4"}, }, }, }, diff --git a/source/istio_virtualservice_test.go b/source/istio_virtualservice_test.go index 8f1aa6d81..a510094e7 100644 --- a/source/istio_virtualservice_test.go +++ b/source/istio_virtualservice_test.go @@ -394,8 +394,9 @@ func testEndpointsFromVirtualServiceConfig(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -416,8 +417,9 @@ func testEndpointsFromVirtualServiceConfig(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, }, }, @@ -439,12 +441,14 @@ func testEndpointsFromVirtualServiceConfig(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"8.8.8.8", "127.0.0.1"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8", "127.0.0.1"}, }, { - DNSName: "foo.bar", - Targets: endpoint.Targets{"elb.com", "alb.com"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"elb.com", "alb.com"}, }, }, }, @@ -543,8 +547,9 @@ func testEndpointsFromVirtualServiceConfig(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "foo.bar", - Targets: endpoint.Targets{"elb.com", "alb.com"}, + DNSName: "foo.bar", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"elb.com", "alb.com"}, }, }, }, @@ -618,20 +623,24 @@ func testVirtualServiceEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "example.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -665,8 +674,9 @@ func testVirtualServiceEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, }, }, @@ -702,20 +712,24 @@ func testVirtualServiceEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "example.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -752,12 +766,14 @@ func testVirtualServiceEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "example.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, }, @@ -790,8 +806,9 @@ func testVirtualServiceEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, }, }, @@ -858,8 +875,9 @@ func testVirtualServiceEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, }, }, @@ -917,12 +935,14 @@ func testVirtualServiceEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "vs1.ext-dns.test.com", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "vs1.ext-dns.test.com", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "vs1.ext-dns.test.com", - Targets: endpoint.Targets{"elb.com"}, + DNSName: "vs1.ext-dns.test.com", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"elb.com"}, }, }, fqdnTemplate: "{{.Name}}.ext-dns.test.com", @@ -1206,14 +1226,16 @@ func testVirtualServiceEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, - RecordTTL: endpoint.TTL(6), + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, + RecordTTL: endpoint.TTL(6), }, { - DNSName: "example2.org", - Targets: endpoint.Targets{"8.8.8.8"}, - RecordTTL: endpoint.TTL(1), + DNSName: "example2.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, + RecordTTL: endpoint.TTL(1), }, }, }, @@ -1322,20 +1344,24 @@ func testVirtualServiceEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "example.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"8.8.8.8"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"8.8.8.8"}, }, { - DNSName: "new.org", - Targets: endpoint.Targets{"lb.com"}, + DNSName: "new.org", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets{"lb.com"}, }, }, ignoreHostnameAnnotation: true, diff --git a/source/openshift_route_test.go b/source/openshift_route_test.go index af6d9fc57..f9f7d31d7 100644 --- a/source/openshift_route_test.go +++ b/source/openshift_route_test.go @@ -197,7 +197,8 @@ func testOcpRouteSourceEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "my-domain.com", + DNSName: "my-domain.com", + RecordType: endpoint.RecordTypeCNAME, Targets: []string{ "apps.my-domain.com", }, @@ -230,7 +231,8 @@ func testOcpRouteSourceEndpoints(t *testing.T) { ocpRouterName: "default", expected: []*endpoint.Endpoint{ { - DNSName: "my-domain.com", + DNSName: "my-domain.com", + RecordType: endpoint.RecordTypeCNAME, Targets: []string{ "router-default.my-domain.com", }, @@ -274,7 +276,8 @@ func testOcpRouteSourceEndpoints(t *testing.T) { ocpRouterName: "default", expected: []*endpoint.Endpoint{ { - DNSName: "my-domain.com", + DNSName: "my-domain.com", + RecordType: endpoint.RecordTypeCNAME, Targets: []string{ "router-default.my-domain.com", }, @@ -393,8 +396,11 @@ func testOcpRouteSourceEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "my-domain.com", - Targets: []string{"router-test.my-domain.com"}, + DNSName: "my-domain.com", + RecordType: endpoint.RecordTypeCNAME, + Targets: []string{ + "router-test.my-domain.com", + }, }, }, }, @@ -439,7 +445,8 @@ func testOcpRouteSourceEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "my-annotation-domain.com", + DNSName: "my-annotation-domain.com", + RecordType: endpoint.RecordTypeCNAME, Targets: []string{ "my.site.foo.com", }, @@ -479,7 +486,8 @@ func testOcpRouteSourceEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "my-annotation-domain.com", + DNSName: "my-annotation-domain.com", + RecordType: endpoint.RecordTypeCNAME, Targets: []string{ "my.site.foo.com", }, diff --git a/source/service_test.go b/source/service_test.go index a16994b2b..b0ce34c97 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -232,7 +232,7 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -277,8 +277,8 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.fqdn.org", Targets: endpoint.Targets{"1.2.3.4"}}, - {DNSName: "foo.fqdn.com", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.fqdn.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.fqdn.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -294,8 +294,8 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.fqdn.org", Targets: endpoint.Targets{"1.2.3.4"}}, - {DNSName: "foo.fqdn.com", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.fqdn.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.fqdn.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -313,10 +313,10 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, - {DNSName: "bar.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, - {DNSName: "foo.fqdn.org", Targets: endpoint.Targets{"1.2.3.4"}}, - {DNSName: "foo.fqdn.com", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.fqdn.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.fqdn.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -335,8 +335,8 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.fqdn.org", Targets: endpoint.Targets{"1.2.3.4"}}, - {DNSName: "foo.fqdn.com", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.fqdn.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.fqdn.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -352,8 +352,8 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, - {DNSName: "bar.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -369,8 +369,8 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, - {DNSName: "bar.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -386,7 +386,7 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"lb.example.com"}, // Kubernetes omits the trailing dot serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"lb.example.com"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"lb.example.com"}}, }, }, { @@ -402,8 +402,8 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4", "lb.example.com"}, // Kubernetes omits the trailing dot serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, - {DNSName: "foo.example.org", Targets: endpoint.Targets{"lb.example.com"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"lb.example.com"}}, }, }, { @@ -420,7 +420,7 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -453,7 +453,7 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -484,7 +484,7 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -502,7 +502,7 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -553,7 +553,7 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -599,7 +599,7 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"10.2.3.4", "11.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.2.3.4", "11.2.3.4"}}, }, }, { @@ -615,7 +615,7 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4", "8.8.8.8"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4", "8.8.8.8"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4", "8.8.8.8"}}, }, }, { @@ -646,7 +646,7 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -665,8 +665,8 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, - {DNSName: "bar.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -683,8 +683,8 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4", "lb.example.com"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "internal.foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, - {DNSName: "internal.foo.example.org", Targets: endpoint.Targets{"lb.example.com"}}, + {DNSName: "internal.foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "internal.foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"lb.example.com"}}, }, }, { @@ -702,10 +702,10 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, - {DNSName: "bar.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, - {DNSName: "internal.foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, - {DNSName: "internal.bar.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "internal.foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "internal.bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -720,8 +720,8 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4", "elb.com"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.bar.example.com", Targets: endpoint.Targets{"1.2.3.4"}}, - {DNSName: "foo.bar.example.com", Targets: endpoint.Targets{"elb.com"}}, + {DNSName: "foo.bar.example.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.bar.example.com", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"elb.com"}}, }, }, { @@ -738,8 +738,8 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4", "elb.com"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, - {DNSName: "foo.example.org", Targets: endpoint.Targets{"elb.com"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"elb.com"}}, }, }, { @@ -757,7 +757,7 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "mate.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "mate.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -787,7 +787,7 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(0)}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(0)}, }, }, { @@ -804,7 +804,7 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(0)}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(0)}, }, }, { @@ -821,7 +821,7 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(10)}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(10)}, }, }, { @@ -838,7 +838,7 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(60)}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(60)}, }, }, { @@ -855,7 +855,7 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(0)}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(0)}, }, }, { @@ -871,7 +871,7 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{string(v1.ServiceTypeLoadBalancer)}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -902,7 +902,7 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.internal.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, + {DNSName: "foo.internal.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}}, }, }, { @@ -920,8 +920,8 @@ func testServiceSourceEndpoints(t *testing.T) { lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, expected: []*endpoint.Endpoint{ - {DNSName: "foo.internal.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.internal.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -939,7 +939,7 @@ func testServiceSourceEndpoints(t *testing.T) { serviceLabelSelector: "app=web-external", fqdnTemplate: "{{.Name}}.bar.example.com", expected: []*endpoint.Endpoint{ - {DNSName: "fqdn.bar.example.com", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "fqdn.bar.example.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -957,7 +957,7 @@ func testServiceSourceEndpoints(t *testing.T) { serviceLabelSelector: "app=web-external", annotations: map[string]string{hostnameAnnotationKey: "annotation.bar.example.com"}, expected: []*endpoint.Endpoint{ - {DNSName: "annotation.bar.example.com", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "annotation.bar.example.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -1112,7 +1112,7 @@ func testMultipleServicesEndpoints(t *testing.T) { }, []string{}, []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foo1.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foo1.2.3.4"}}, }, false, }, @@ -1136,7 +1136,7 @@ func testMultipleServicesEndpoints(t *testing.T) { }, []string{}, []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4", "1.2.3.5", "1.2.3.6"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foo1.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4", "1.2.3.5", "1.2.3.6"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foo1.2.3.4"}}, }, false, }, @@ -1164,9 +1164,9 @@ func testMultipleServicesEndpoints(t *testing.T) { }, []string{}, []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4", "1.2.3.5", "1.2.3.6"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foo1.2.3.4"}}, - {DNSName: "bar.example.org", Targets: endpoint.Targets{"10.1.1.1", "10.1.1.2", "10.1.1.3"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foo10.1.1.1"}}, - {DNSName: "foobar.example.org", Targets: endpoint.Targets{"20.1.1.1"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foo20.1.1.1"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4", "1.2.3.5", "1.2.3.6"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foo1.2.3.4"}}, + {DNSName: "bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.1.1.1", "10.1.1.2", "10.1.1.3"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foo10.1.1.1"}}, + {DNSName: "foobar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"20.1.1.1"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foo20.1.1.1"}}, }, false, }, @@ -1189,8 +1189,8 @@ func testMultipleServicesEndpoints(t *testing.T) { }, []string{}, []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"a.elb.com"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/fooa.elb.com"}, SetIdentifier: "a"}, - {DNSName: "foo.example.org", Targets: endpoint.Targets{"b.elb.com"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foob.elb.com"}, SetIdentifier: "b"}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"a.elb.com"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/fooa.elb.com"}, SetIdentifier: "a"}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"b.elb.com"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foob.elb.com"}, SetIdentifier: "b"}, }, false, }, @@ -1304,7 +1304,7 @@ func TestClusterIpServices(t *testing.T) { }, clusterIP: "1.2.3.4", expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, { @@ -1327,7 +1327,7 @@ func TestClusterIpServices(t *testing.T) { fqdnTemplate: "{{.Name}}.bar.example.com", clusterIP: "4.5.6.7", expected: []*endpoint.Endpoint{ - {DNSName: "foo.bar.example.com", Targets: endpoint.Targets{"4.5.6.7"}}, + {DNSName: "foo.bar.example.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"4.5.6.7"}}, }, }, { @@ -1347,7 +1347,7 @@ func TestClusterIpServices(t *testing.T) { labels: map[string]string{"app": "web-internal"}, clusterIP: "4.5.6.7", expected: []*endpoint.Endpoint{ - {DNSName: "foo.bar.example.com", Targets: endpoint.Targets{"4.5.6.7"}}, + {DNSName: "foo.bar.example.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"4.5.6.7"}}, }, labelSelector: "app=web-internal", }, @@ -1748,8 +1748,8 @@ func TestServiceSourceNodePortServices(t *testing.T) { kopsDNSControllerInternalHostnameAnnotationKey: "internal.foo.example.org., internal.bar.example.org", }, expected: []*endpoint.Endpoint{ - {DNSName: "internal.foo.example.org", Targets: endpoint.Targets{"10.0.1.1"}}, - {DNSName: "internal.bar.example.org", Targets: endpoint.Targets{"10.0.1.1"}}, + {DNSName: "internal.foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.0.1.1"}}, + {DNSName: "internal.bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.0.1.1"}}, }, nodes: []*v1.Node{{ ObjectMeta: metav1.ObjectMeta{ @@ -1790,8 +1790,8 @@ func TestServiceSourceNodePortServices(t *testing.T) { kopsDNSControllerInternalHostnameAnnotationKey: "internal.foo.example.org., internal.bar.example.org", }, expected: []*endpoint.Endpoint{ - {DNSName: "internal.foo.example.org", Targets: endpoint.Targets{"10.0.1.1", "10.0.1.2"}}, - {DNSName: "internal.bar.example.org", Targets: endpoint.Targets{"10.0.1.1", "10.0.1.2"}}, + {DNSName: "internal.foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.0.1.1", "10.0.1.2"}}, + {DNSName: "internal.bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.0.1.1", "10.0.1.2"}}, }, nodes: []*v1.Node{{ ObjectMeta: metav1.ObjectMeta{ @@ -1832,8 +1832,8 @@ func TestServiceSourceNodePortServices(t *testing.T) { kopsDNSControllerHostnameAnnotationKey: "foo.example.org., bar.example.org", }, expected: []*endpoint.Endpoint{ - {DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}}, - {DNSName: "bar.example.org", Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}}, + {DNSName: "bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}}, }, nodes: []*v1.Node{{ ObjectMeta: metav1.ObjectMeta{ @@ -2051,9 +2051,9 @@ func TestHeadlessServices(t *testing.T) { false, []v1.Node{}, []*endpoint.Endpoint{ - {DNSName: "foo-0.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, - {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.2"}}, - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}}, + {DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}}, + {DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.2"}}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}}, }, false, }, @@ -2114,9 +2114,9 @@ func TestHeadlessServices(t *testing.T) { false, []v1.Node{}, []*endpoint.Endpoint{ - {DNSName: "foo-0.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}, RecordTTL: endpoint.TTL(1)}, - {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.2"}, RecordTTL: endpoint.TTL(1)}, - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}, RecordTTL: endpoint.TTL(1)}, + {DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}, RecordTTL: endpoint.TTL(1)}, + {DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.2"}, RecordTTL: endpoint.TTL(1)}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}, RecordTTL: endpoint.TTL(1)}, }, false, }, @@ -2147,8 +2147,8 @@ func TestHeadlessServices(t *testing.T) { false, []v1.Node{}, []*endpoint.Endpoint{ - {DNSName: "foo-0.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, + {DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}}, }, false, }, @@ -2179,9 +2179,9 @@ func TestHeadlessServices(t *testing.T) { true, []v1.Node{}, []*endpoint.Endpoint{ - {DNSName: "foo-0.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, - {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.2"}}, - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}}, + {DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}}, + {DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.2"}}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}}, }, false, }, @@ -2212,7 +2212,7 @@ func TestHeadlessServices(t *testing.T) { false, []v1.Node{}, []*endpoint.Endpoint{ - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}}, }, false, }, @@ -2243,7 +2243,7 @@ func TestHeadlessServices(t *testing.T) { false, []v1.Node{}, []*endpoint.Endpoint{ - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}}, }, false, }, @@ -2276,7 +2276,7 @@ func TestHeadlessServices(t *testing.T) { false, []v1.Node{}, []*endpoint.Endpoint{ - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, false, }, @@ -2319,7 +2319,7 @@ func TestHeadlessServices(t *testing.T) { }, }, []*endpoint.Endpoint{ - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, false, }, @@ -2351,7 +2351,7 @@ func TestHeadlessServices(t *testing.T) { false, []v1.Node{}, []*endpoint.Endpoint{ - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, false, }, @@ -2523,9 +2523,9 @@ func TestHeadlessServicesHostIP(t *testing.T) { }, false, []*endpoint.Endpoint{ - {DNSName: "foo-0.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, - {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.2"}}, - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}}, + {DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}}, + {DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.2"}}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}}, }, false, }, @@ -2588,9 +2588,9 @@ func TestHeadlessServicesHostIP(t *testing.T) { }, false, []*endpoint.Endpoint{ - {DNSName: "foo-0.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}, RecordTTL: endpoint.TTL(1)}, - {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.2"}, RecordTTL: endpoint.TTL(1)}, - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}, RecordTTL: endpoint.TTL(1)}, + {DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}, RecordTTL: endpoint.TTL(1)}, + {DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.2"}, RecordTTL: endpoint.TTL(1)}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}, RecordTTL: endpoint.TTL(1)}, }, false, }, @@ -2622,8 +2622,8 @@ func TestHeadlessServicesHostIP(t *testing.T) { }, false, []*endpoint.Endpoint{ - {DNSName: "foo-0.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, + {DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}}, }, false, }, @@ -2655,9 +2655,9 @@ func TestHeadlessServicesHostIP(t *testing.T) { }, true, []*endpoint.Endpoint{ - {DNSName: "foo-0.service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, - {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.2"}}, - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}}, + {DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}}, + {DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.2"}}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}}, }, false, }, @@ -2689,7 +2689,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { }, false, []*endpoint.Endpoint{ - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}}, }, false, }, diff --git a/source/shared_test.go b/source/shared_test.go index 7114b0dd7..9cdc58d22 100644 --- a/source/shared_test.go +++ b/source/shared_test.go @@ -79,7 +79,7 @@ func validateEndpoint(t *testing.T, endpoint, expected *endpoint.Endpoint) { } // if non-empty record type is expected, check that it matches. - if expected.RecordType != "" && endpoint.RecordType != expected.RecordType { + if endpoint.RecordType != expected.RecordType { t.Errorf("RecordType expected %q, got %q", expected.RecordType, endpoint.RecordType) } diff --git a/source/skipper_routegroup_test.go b/source/skipper_routegroup_test.go index 40cf65c1b..9b4c325a5 100644 --- a/source/skipper_routegroup_test.go +++ b/source/skipper_routegroup_test.go @@ -90,8 +90,9 @@ func TestEndpointsFromRouteGroups(t *testing.T) { }), want: []*endpoint.Endpoint{ { - DNSName: "rg1.k8s.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "rg1.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, }, }, @@ -113,12 +114,14 @@ func TestEndpointsFromRouteGroups(t *testing.T) { ), want: []*endpoint.Endpoint{ { - DNSName: "rg1.k8s.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "rg1.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, { - DNSName: "my.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "my.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, }, }, @@ -140,8 +143,9 @@ func TestEndpointsFromRouteGroups(t *testing.T) { ), want: []*endpoint.Endpoint{ { - DNSName: "rg1.k8s.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "rg1.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, }, }, @@ -163,9 +167,10 @@ func TestEndpointsFromRouteGroups(t *testing.T) { ), want: []*endpoint.Endpoint{ { - DNSName: "rg1.k8s.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), - RecordTTL: endpoint.TTL(2189), + DNSName: "rg1.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), + RecordTTL: endpoint.TTL(2189), }, }, }, @@ -185,8 +190,9 @@ func TestEndpointsFromRouteGroups(t *testing.T) { ), want: []*endpoint.Endpoint{ { - DNSName: "rg1.k8s.example", - Targets: endpoint.Targets([]string{"1.5.1.4"}), + DNSName: "rg1.k8s.example", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets([]string{"1.5.1.4"}), }, }, }, @@ -207,12 +213,14 @@ func TestEndpointsFromRouteGroups(t *testing.T) { ), want: []*endpoint.Endpoint{ { - DNSName: "rg1.k8s.example", - Targets: endpoint.Targets([]string{"1.5.1.4"}), + DNSName: "rg1.k8s.example", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets([]string{"1.5.1.4"}), }, { - DNSName: "rg1.k8s.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "rg1.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, }, }, @@ -278,8 +286,9 @@ func TestRouteGroupsEndpoints(t *testing.T) { }, want: []*endpoint.Endpoint{ { - DNSName: "rg1.k8s.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "rg1.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, }, }, @@ -308,12 +317,14 @@ func TestRouteGroupsEndpoints(t *testing.T) { }, want: []*endpoint.Endpoint{ { - DNSName: "rg1.k8s.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "rg1.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, { - DNSName: "rg1.namespace1.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "rg1.namespace1.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, }, }, @@ -341,8 +352,9 @@ func TestRouteGroupsEndpoints(t *testing.T) { }, want: []*endpoint.Endpoint{ { - DNSName: "rg1.namespace1.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "rg1.namespace1.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, }, }, @@ -370,8 +382,9 @@ func TestRouteGroupsEndpoints(t *testing.T) { }, want: []*endpoint.Endpoint{ { - DNSName: "rg1.k8s.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "rg1.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, }, }, @@ -400,9 +413,10 @@ func TestRouteGroupsEndpoints(t *testing.T) { }, want: []*endpoint.Endpoint{ { - DNSName: "rg1.k8s.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), - RecordTTL: endpoint.TTL(2189), + DNSName: "rg1.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), + RecordTTL: endpoint.TTL(2189), }, }, }, @@ -430,12 +444,14 @@ func TestRouteGroupsEndpoints(t *testing.T) { }, want: []*endpoint.Endpoint{ { - DNSName: "rg1.k8s.example", - Targets: endpoint.Targets([]string{"1.5.1.4"}), + DNSName: "rg1.k8s.example", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets([]string{"1.5.1.4"}), }, { - DNSName: "rg1.k8s.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "rg1.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, }, }, @@ -495,20 +511,24 @@ func TestRouteGroupsEndpoints(t *testing.T) { }, want: []*endpoint.Endpoint{ { - DNSName: "rg1.k8s.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "rg1.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, { - DNSName: "rg2.k8s.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "rg2.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, { - DNSName: "rg3.k8s.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "rg3.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, { - DNSName: "rg.k8s.example", - Targets: endpoint.Targets([]string{"lb2.example.org"}), + DNSName: "rg.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb2.example.org"}), }, }, }, @@ -575,8 +595,9 @@ func TestRouteGroupsEndpoints(t *testing.T) { }, want: []*endpoint.Endpoint{ { - DNSName: "rg1.k8s.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "rg1.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, }, }, @@ -643,12 +664,14 @@ func TestRouteGroupsEndpoints(t *testing.T) { }, want: []*endpoint.Endpoint{ { - DNSName: "rg1.k8s.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "rg1.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, { - DNSName: "rg2.k8s.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "rg2.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, }, }, @@ -701,12 +724,14 @@ func TestRouteGroupsEndpoints(t *testing.T) { }, want: []*endpoint.Endpoint{ { - DNSName: "rg1.k8s.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "rg1.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, { - DNSName: "rg3.k8s.example", - Targets: endpoint.Targets([]string{"lb.example.org"}), + DNSName: "rg3.k8s.example", + RecordType: endpoint.RecordTypeCNAME, + Targets: endpoint.Targets([]string{"lb.example.org"}), }, }, }, From 94d09b37dcac4a85ec124a2821ccf675bb08aca3 Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Sat, 6 Nov 2021 19:34:34 -0700 Subject: [PATCH 065/117] Separate plan by record type --- endpoint/endpoint.go | 2 + plan/plan.go | 60 +++++++++++++---------- plan/plan_test.go | 110 ++++++++++++++++++++++++------------------- 3 files changed, 98 insertions(+), 74 deletions(-) diff --git a/endpoint/endpoint.go b/endpoint/endpoint.go index 428a7961b..f4c9405da 100644 --- a/endpoint/endpoint.go +++ b/endpoint/endpoint.go @@ -30,6 +30,8 @@ import ( const ( // RecordTypeA is a RecordType enum value RecordTypeA = "A" + // RecordTypeAAAA is a RecordType enum value + RecordTypeAAAA = "AAAA" // RecordTypeCNAME is a RecordType enum value RecordTypeCNAME = "CNAME" // RecordTypeTXT is a RecordType enum value diff --git a/plan/plan.go b/plan/plan.go index d7bac7090..9eefeb207 100644 --- a/plan/plan.go +++ b/plan/plan.go @@ -78,12 +78,12 @@ bar.com | | [->191.1.1.1, ->190.1.1.1] | = create (bar.com -> 1 "=", i.e. result of calculation relies on supplied ConflictResolver */ type planTable struct { - rows map[string]map[string]*planTableRow + rows map[string]map[string]map[string]*planTableRow resolver ConflictResolver } func newPlanTable() planTable { // TODO: make resolver configurable - return planTable{map[string]map[string]*planTableRow{}, PerResource{}} + return planTable{map[string]map[string]map[string]*planTableRow{}, PerResource{}} } // planTableRow @@ -101,23 +101,29 @@ func (t planTableRow) String() string { func (t planTable) addCurrent(e *endpoint.Endpoint) { dnsName := normalizeDNSName(e.DNSName) if _, ok := t.rows[dnsName]; !ok { - t.rows[dnsName] = make(map[string]*planTableRow) + t.rows[dnsName] = make(map[string]map[string]*planTableRow) } if _, ok := t.rows[dnsName][e.SetIdentifier]; !ok { - t.rows[dnsName][e.SetIdentifier] = &planTableRow{} + t.rows[dnsName][e.SetIdentifier] = make(map[string]*planTableRow) } - t.rows[dnsName][e.SetIdentifier].current = e + if _, ok := t.rows[e.SetIdentifier][e.RecordType]; !ok { + t.rows[dnsName][e.SetIdentifier][e.RecordType] = &planTableRow{} + } + t.rows[dnsName][e.SetIdentifier][e.RecordType].current = e } func (t planTable) addCandidate(e *endpoint.Endpoint) { dnsName := normalizeDNSName(e.DNSName) if _, ok := t.rows[dnsName]; !ok { - t.rows[dnsName] = make(map[string]*planTableRow) + t.rows[dnsName] = make(map[string]map[string]*planTableRow) } if _, ok := t.rows[dnsName][e.SetIdentifier]; !ok { - t.rows[dnsName][e.SetIdentifier] = &planTableRow{} + t.rows[dnsName][e.SetIdentifier] = make(map[string]*planTableRow) } - t.rows[dnsName][e.SetIdentifier].candidates = append(t.rows[dnsName][e.SetIdentifier].candidates, e) + if _, ok := t.rows[dnsName][e.SetIdentifier][e.RecordType]; !ok { + t.rows[dnsName][e.SetIdentifier][e.RecordType] = &planTableRow{} + } + t.rows[dnsName][e.SetIdentifier][e.RecordType].candidates = append(t.rows[dnsName][e.SetIdentifier][e.RecordType].candidates, e) } func (c *Changes) HasChanges() bool { @@ -147,24 +153,26 @@ func (p *Plan) Calculate() *Plan { changes := &Changes{} for _, topRow := range t.rows { - for _, row := range topRow { - if row.current == nil { // dns name not taken - changes.Create = append(changes.Create, t.resolver.ResolveCreate(row.candidates)) - } - if row.current != nil && len(row.candidates) == 0 { - changes.Delete = append(changes.Delete, row.current) - } - - // TODO: allows record type change, which might not be supported by all dns providers - if row.current != nil && len(row.candidates) > 0 { // dns name is taken - update := t.resolver.ResolveUpdate(row.current, row.candidates) - // compare "update" to "current" to figure out if actual update is required - if shouldUpdateTTL(update, row.current) || targetChanged(update, row.current) || p.shouldUpdateProviderSpecific(update, row.current) { - inheritOwner(row.current, update) - changes.UpdateNew = append(changes.UpdateNew, update) - changes.UpdateOld = append(changes.UpdateOld, row.current) + for _, midRow := range topRow { + for _, row := range midRow { + if row.current == nil { // dns name not taken + changes.Create = append(changes.Create, t.resolver.ResolveCreate(row.candidates)) + } + if row.current != nil && len(row.candidates) == 0 { + changes.Delete = append(changes.Delete, row.current) + } + + // TODO: allows record type change, which might not be supported by all dns providers + if row.current != nil && len(row.candidates) > 0 { // dns name is taken + update := t.resolver.ResolveUpdate(row.current, row.candidates) + // compare "update" to "current" to figure out if actual update is required + if shouldUpdateTTL(update, row.current) || targetChanged(update, row.current) || p.shouldUpdateProviderSpecific(update, row.current) { + inheritOwner(row.current, update) + changes.UpdateNew = append(changes.UpdateNew, update) + changes.UpdateOld = append(changes.UpdateOld, row.current) + } + continue } - continue } } } @@ -181,7 +189,7 @@ func (p *Plan) Calculate() *Plan { Current: p.Current, Desired: p.Desired, Changes: changes, - ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, + ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, } return plan diff --git a/plan/plan_test.go b/plan/plan_test.go index d34c93249..2b107d0e8 100644 --- a/plan/plan_test.go +++ b/plan/plan_test.go @@ -35,6 +35,9 @@ type PlanTestSuite struct { fooV2CnameNoLabel *endpoint.Endpoint fooV3CnameSameResource *endpoint.Endpoint fooA5 *endpoint.Endpoint + fooAAAA *endpoint.Endpoint + dsA *endpoint.Endpoint + dsAAAA *endpoint.Endpoint bar127A *endpoint.Endpoint bar127AWithTTL *endpoint.Endpoint bar127AWithProviderSpecificTrue *endpoint.Endpoint @@ -106,6 +109,30 @@ func (suite *PlanTestSuite) SetupTest() { endpoint.ResourceLabelKey: "ingress/default/foo-5", }, } + suite.fooAAAA = &endpoint.Endpoint{ + DNSName: "foo", + Targets: endpoint.Targets{"2001:DB8::1"}, + RecordType: "AAAA", + Labels: map[string]string{ + endpoint.ResourceLabelKey: "ingress/default/foo-AAAA", + }, + } + suite.dsA = &endpoint.Endpoint{ + DNSName: "ds", + Targets: endpoint.Targets{"1.1.1.1"}, + RecordType: "A", + Labels: map[string]string{ + endpoint.ResourceLabelKey: "ingress/default/ds", + }, + } + suite.dsAAAA = &endpoint.Endpoint{ + DNSName: "ds", + Targets: endpoint.Targets{"1.1.1.1"}, + RecordType: "AAAA", + Labels: map[string]string{ + endpoint.ResourceLabelKey: "ingress/default/ds-AAAAA", + }, + } suite.bar127A = &endpoint.Endpoint{ DNSName: "bar", Targets: endpoint.Targets{"127.0.0.1"}, @@ -438,9 +465,9 @@ func (suite *PlanTestSuite) TestIdempotency() { func (suite *PlanTestSuite) TestDifferentTypes() { current := []*endpoint.Endpoint{suite.fooV1Cname} desired := []*endpoint.Endpoint{suite.fooV2Cname, suite.fooA5} - expectedCreate := []*endpoint.Endpoint{} + expectedCreate := []*endpoint.Endpoint{suite.fooA5} expectedUpdateOld := []*endpoint.Endpoint{suite.fooV1Cname} - expectedUpdateNew := []*endpoint.Endpoint{suite.fooA5} + expectedUpdateNew := []*endpoint.Endpoint{suite.fooV2Cname} expectedDelete := []*endpoint.Endpoint{} p := &Plan{ @@ -544,52 +571,6 @@ func (suite *PlanTestSuite) TestRemoveEndpointWithUpsert() { validateEntries(suite.T(), changes.Delete, expectedDelete) } -// TODO: remove once multiple-target per endpoint is supported -func (suite *PlanTestSuite) TestDuplicatedEndpointsForSameResourceReplace() { - current := []*endpoint.Endpoint{suite.fooV3CnameSameResource, suite.bar192A} - desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV3CnameSameResource} - expectedCreate := []*endpoint.Endpoint{} - expectedUpdateOld := []*endpoint.Endpoint{suite.fooV3CnameSameResource} - expectedUpdateNew := []*endpoint.Endpoint{suite.fooV1Cname} - expectedDelete := []*endpoint.Endpoint{suite.bar192A} - - p := &Plan{ - Policies: []Policy{&SyncPolicy{}}, - Current: current, - Desired: desired, - ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, - } - - changes := p.Calculate().Changes - validateEntries(suite.T(), changes.Create, expectedCreate) - validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) - validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) - validateEntries(suite.T(), changes.Delete, expectedDelete) -} - -// TODO: remove once multiple-target per endpoint is supported -func (suite *PlanTestSuite) TestDuplicatedEndpointsForSameResourceRetain() { - current := []*endpoint.Endpoint{suite.fooV1Cname, suite.bar192A} - desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV3CnameSameResource} - expectedCreate := []*endpoint.Endpoint{} - expectedUpdateOld := []*endpoint.Endpoint{} - expectedUpdateNew := []*endpoint.Endpoint{} - expectedDelete := []*endpoint.Endpoint{suite.bar192A} - - p := &Plan{ - Policies: []Policy{&SyncPolicy{}}, - Current: current, - Desired: desired, - ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, - } - - changes := p.Calculate().Changes - validateEntries(suite.T(), changes.Create, expectedCreate) - validateEntries(suite.T(), changes.UpdateNew, expectedUpdateNew) - validateEntries(suite.T(), changes.UpdateOld, expectedUpdateOld) - validateEntries(suite.T(), changes.Delete, expectedDelete) -} - func (suite *PlanTestSuite) TestMultipleRecordsSameNameDifferentSetIdentifier() { current := []*endpoint.Endpoint{suite.multiple1} desired := []*endpoint.Endpoint{suite.multiple2, suite.multiple3} @@ -695,6 +676,39 @@ func (suite *PlanTestSuite) TestMissing() { validateEntries(suite.T(), changes.Create, expectedCreate) } +func (suite *PlanTestSuite) TestAAAARecords() { + + current := []*endpoint.Endpoint{} + desired := []*endpoint.Endpoint{suite.fooAAAA} + expectedCreate := []*endpoint.Endpoint{suite.fooAAAA} + + p := &Plan{ + Policies: []Policy{&SyncPolicy{}}, + Current: current, + Desired: desired, + ManagedRecords: []string{endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, + } + + changes := p.Calculate().Changes + validateEntries(suite.T(), changes.Create, expectedCreate) +} + +func (suite *PlanTestSuite) TestDualStackRecords() { + current := []*endpoint.Endpoint{} + desired := []*endpoint.Endpoint{suite.dsA, suite.dsAAAA} + expectedCreate := []*endpoint.Endpoint{suite.dsA, suite.dsAAAA} + + p := &Plan{ + Policies: []Policy{&SyncPolicy{}}, + Current: current, + Desired: desired, + ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, + } + + changes := p.Calculate().Changes + validateEntries(suite.T(), changes.Create, expectedCreate) +} + func TestPlan(t *testing.T) { suite.Run(t, new(PlanTestSuite)) } From 9bb9a80c710c548ab545f5b32dee8eb2517b8475 Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Sat, 4 Dec 2021 23:59:00 -0800 Subject: [PATCH 066/117] Add AAAA to supported record types --- provider/recordfilter.go | 4 ++-- provider/recordfilter_test.go | 4 ++++ provider/transip/transip_test.go | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/provider/recordfilter.go b/provider/recordfilter.go index 245d47570..97d4f11e8 100644 --- a/provider/recordfilter.go +++ b/provider/recordfilter.go @@ -17,10 +17,10 @@ limitations under the License. package provider // SupportedRecordType returns true only for supported record types. -// Currently A, CNAME, SRV, TXT and NS record types are supported. +// Currently A, AAAA, CNAME, SRV, TXT and NS record types are supported. func SupportedRecordType(recordType string) bool { switch recordType { - case "A", "CNAME", "SRV", "TXT", "NS": + case "A", "AAAA", "CNAME", "SRV", "TXT", "NS": return true default: return false diff --git a/provider/recordfilter_test.go b/provider/recordfilter_test.go index d55e07deb..f482dd1cd 100644 --- a/provider/recordfilter_test.go +++ b/provider/recordfilter_test.go @@ -27,6 +27,10 @@ func TestRecordTypeFilter(t *testing.T) { "A", true, }, + { + "AAAA", + true, + }, { "CNAME", true, diff --git a/provider/transip/transip_test.go b/provider/transip/transip_test.go index a75695282..31d13cb4a 100644 --- a/provider/transip/transip_test.go +++ b/provider/transip/transip_test.go @@ -256,7 +256,7 @@ func TestProviderRecords(t *testing.T) { endpoints, err := p.Records(context.TODO()) if assert.NoError(t, err) { - if assert.Equal(t, 2, len(endpoints)) { + if assert.Equal(t, 4, len(endpoints)) { assert.Equal(t, "www.example.org", endpoints[0].DNSName) assert.EqualValues(t, "@", endpoints[0].Targets[0]) assert.Equal(t, "CNAME", endpoints[0].RecordType) From 6f42a8a2dadbbe9f795034c649d3ba8b77e44c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Skyler=20M=C3=A4ntysaari?= Date: Fri, 12 Nov 2021 23:40:20 -0800 Subject: [PATCH 067/117] Initial IPv6 support --- endpoint/endpoint.go | 2 +- endpoint/endpoint_test.go | 16 ++++++ pkg/apis/externaldns/types.go | 4 +- pkg/apis/externaldns/types_test.go | 4 +- provider/pdns/pdns.go | 1 - source/service.go | 14 ++++++ source/service_test.go | 81 ++++++++++++++++++++++++++++++ source/source.go | 37 ++++++++++++-- source/source_test.go | 1 + 9 files changed, 151 insertions(+), 9 deletions(-) diff --git a/endpoint/endpoint.go b/endpoint/endpoint.go index f4c9405da..6c22c4fba 100644 --- a/endpoint/endpoint.go +++ b/endpoint/endpoint.go @@ -166,7 +166,7 @@ type Endpoint struct { DNSName string `json:"dnsName,omitempty"` // The targets the DNS record points to Targets Targets `json:"targets,omitempty"` - // RecordType type of record, e.g. CNAME, A, SRV, TXT etc + // RecordType type of record, e.g. CNAME, A, AAAA, SRV, TXT etc RecordType string `json:"recordType,omitempty"` // Identifier to distinguish multiple records with the same name and type (e.g. Route53 records with routing policies other than 'simple') SetIdentifier string `json:"setIdentifier,omitempty"` diff --git a/endpoint/endpoint_test.go b/endpoint/endpoint_test.go index 81e7f4c33..fe61f8dc6 100644 --- a/endpoint/endpoint_test.go +++ b/endpoint/endpoint_test.go @@ -35,6 +35,22 @@ func TestNewEndpoint(t *testing.T) { } } +func TestNewEndpointWithIPv6(t *testing.T) { + e := NewEndpoint("example.org", "AAAA", "foo.com") + if e.DNSName != "example.com" || e.Targets[0] != "foo.com" || e.RecordType != "AAAA" { + t.Error("Endpoint is not initialized correctly") + } + + if e.Labels == nil { + t.Error("Labels is not initialized") + } + + w := NewEndpoint("example.org", "", "load-balancer.com.") + if w.DNSName != "example.org" || e.Targets[0] != "load-balancer.com" || w.RecordType != "" { + t.Error("Endpoint is not initialized correctly") + } +} + func TestTargetsSame(t *testing.T) { tests := []Targets{ {""}, diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 1eb8570bf..e573669e7 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -327,7 +327,7 @@ var defaultConfig = &Config{ TransIPAccountName: "", TransIPPrivateKeyFile: "", DigitalOceanAPIPageSize: 50, - ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, + ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, GoDaddyAPIKey: "", GoDaddySecretKey: "", GoDaddyTTL: 600, @@ -424,7 +424,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("crd-source-apiversion", "API version of the CRD for crd source, e.g. `externaldns.k8s.io/v1alpha1`, valid only when using crd source").Default(defaultConfig.CRDSourceAPIVersion).StringVar(&cfg.CRDSourceAPIVersion) app.Flag("crd-source-kind", "Kind of the CRD for the crd source in API group and version specified by crd-source-apiversion").Default(defaultConfig.CRDSourceKind).StringVar(&cfg.CRDSourceKind) app.Flag("service-type-filter", "The service types to take care about (default: all, expected: ClusterIP, NodePort, LoadBalancer or ExternalName)").StringsVar(&cfg.ServiceTypeFilter) - app.Flag("managed-record-types", "Record types to manage; specify multiple times to include many; (default: A, CNAME) (supported records: CNAME, A, NS").Default("A", "CNAME").StringsVar(&cfg.ManagedDNSRecordTypes) + app.Flag("managed-record-types", "Record types to manage; specify multiple times to include many; (default: A, AAAA, CNAME) (supported records: CNAME, A, AAAA, NS").Default("A", "AAAA", "CNAME").StringsVar(&cfg.ManagedDNSRecordTypes) app.Flag("default-targets", "Set globally default IP address that will apply as a target instead of source addresses. Specify multiple times for multiple targets (optional)").StringsVar(&cfg.DefaultTargets) app.Flag("target-net-filter", "Limit possible targets by a net filter; specify multiple times for multiple possible nets (optional)").StringsVar(&cfg.TargetNetFilter) app.Flag("exclude-target-net", "Exclude target nets (optional)").StringsVar(&cfg.ExcludeTargetNets) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index 87647ee21..7d4cc6ab8 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -122,7 +122,7 @@ var ( TransIPAccountName: "", TransIPPrivateKeyFile: "", DigitalOceanAPIPageSize: 50, - ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME}, + ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, RFC2136BatchChangeSize: 50, OCPRouterName: "default", IBMCloudProxied: false, @@ -233,7 +233,7 @@ var ( TransIPAccountName: "transip", TransIPPrivateKeyFile: "/path/to/transip.key", DigitalOceanAPIPageSize: 100, - ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME, endpoint.RecordTypeNS}, + ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME, endpoint.RecordTypeNS}, RFC2136BatchChangeSize: 100, IBMCloudProxied: true, IBMCloudConfigFile: "ibmcloud.json", diff --git a/provider/pdns/pdns.go b/provider/pdns/pdns.go index 6f9ca0266..ff8611dc3 100644 --- a/provider/pdns/pdns.go +++ b/provider/pdns/pdns.go @@ -315,7 +315,6 @@ func (p *PDNSProvider) ConvertEndpointsToZones(eps []*endpoint.Endpoint, changet if ep.RecordType == "CNAME" { t = provider.EnsureTrailingDot(t) } - records = append(records, pgo.Record{Content: t}) } rrset := pgo.RrSet{ diff --git a/source/service.go b/source/service.go index 9c47579dd..aa8311f39 100644 --- a/source/service.go +++ b/source/service.go @@ -456,6 +456,14 @@ func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, pro DNSName: hostname, } + epAAAA := &endpoint.Endpoint{ + RecordTTL: ttl, + RecordType: endpoint.RecordTypeAAAA, + Labels: endpoint.NewLabels(), + Targets: make(endpoint.Targets, 0, defaultTargetsCapacity), + DNSName: hostname, + } + epCNAME := &endpoint.Endpoint{ RecordTTL: ttl, RecordType: endpoint.RecordTypeCNAME, @@ -497,6 +505,9 @@ func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, pro if suitableType(t) == endpoint.RecordTypeA { epA.Targets = append(epA.Targets, t) } + if suitableType(t) == endpoint.RecordTypeAAAA { + epAAAA.Targets = append(epAAAA.Targets, t) + } if suitableType(t) == endpoint.RecordTypeCNAME { epCNAME.Targets = append(epCNAME.Targets, t) } @@ -505,6 +516,9 @@ func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, pro if len(epA.Targets) > 0 { endpoints = append(endpoints, epA) } + if len(epAAAA.Targets) > 0 { + endpoints = append(endpoints, epAAAA) + } if len(epCNAME.Targets) > 0 { endpoints = append(endpoints, epCNAME) } diff --git a/source/service_test.go b/source/service_test.go index b0ce34c97..932d172e3 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -187,6 +187,7 @@ func testServiceSourceEndpoints(t *testing.T) { labels map[string]string annotations map[string]string clusterIP string + ipFamilies []v1.IPFamily externalIPs []string lbs []string serviceTypesFilter []string @@ -201,6 +202,7 @@ func testServiceSourceEndpoints(t *testing.T) { svcType: v1.ServiceTypeLoadBalancer, labels: map[string]string{}, annotations: map[string]string{}, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -214,6 +216,7 @@ func testServiceSourceEndpoints(t *testing.T) { ignoreHostnameAnnotation: true, labels: map[string]string{}, annotations: map[string]string{}, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -228,6 +231,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -245,6 +249,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -259,6 +264,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.2.3.4", externalIPs: []string{}, lbs: []string{}, @@ -273,6 +279,7 @@ func testServiceSourceEndpoints(t *testing.T) { fqdnTemplate: "{{.Name}}.fqdn.org,{{.Name}}.fqdn.com", labels: map[string]string{}, annotations: map[string]string{}, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -290,6 +297,7 @@ func testServiceSourceEndpoints(t *testing.T) { ignoreHostnameAnnotation: true, labels: map[string]string{}, annotations: map[string]string{}, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -309,6 +317,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org., bar.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -331,6 +340,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org., bar.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -348,6 +358,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org., bar.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -365,6 +376,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org, bar.example.org", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -382,6 +394,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"lb.example.com"}, // Kubernetes omits the trailing dot serviceTypesFilter: []string{}, @@ -398,6 +411,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org", // Trailing dot is omitted }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4", "lb.example.com"}, // Kubernetes omits the trailing dot serviceTypesFilter: []string{}, @@ -416,6 +430,7 @@ func testServiceSourceEndpoints(t *testing.T) { controllerAnnotationKey: controllerAnnotationValue, hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -434,6 +449,7 @@ func testServiceSourceEndpoints(t *testing.T) { controllerAnnotationKey: "some-other-tool", hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -449,6 +465,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -466,6 +483,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -480,6 +498,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -498,6 +517,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", "service.beta.kubernetes.io/external-traffic": "OnlyLocal", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -516,6 +536,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", "service.beta.kubernetes.io/external-traffic": "SomethingElse", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -532,6 +553,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", "service.beta.kubernetes.io/external-traffic": "OnlyLocal", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -549,6 +571,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", "service.beta.kubernetes.io/external-traffic": "Global", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -567,6 +590,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", "service.beta.kubernetes.io/external-traffic": "OnlyLocal", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -581,6 +605,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{}, serviceTypesFilter: []string{}, @@ -595,6 +620,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{"10.2.3.4", "11.2.3.4"}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -611,6 +637,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4", "8.8.8.8"}, serviceTypesFilter: []string{}, @@ -627,6 +654,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ "zalando.org/dnsname": "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -642,6 +670,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ "zalando.org/dnsname": "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -658,6 +687,7 @@ func testServiceSourceEndpoints(t *testing.T) { labels: map[string]string{ "dns": "route53", }, + ipFamilies: []v1.IPFamily{"IPv4"}, annotations: map[string]string{ "domainName": "foo.example.org., bar.example.org", }, @@ -679,6 +709,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ kopsDNSControllerInternalHostnameAnnotationKey: "internal.foo.example.org", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4", "lb.example.com"}, serviceTypesFilter: []string{}, @@ -698,6 +729,7 @@ func testServiceSourceEndpoints(t *testing.T) { kopsDNSControllerInternalHostnameAnnotationKey: "internal.foo.example.org., internal.bar.example.org", kopsDNSControllerHostnameAnnotationKey: "foo.example.org., bar.example.org", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -716,6 +748,7 @@ func testServiceSourceEndpoints(t *testing.T) { fqdnTemplate: "{{.Name}}.bar.example.com", labels: map[string]string{}, annotations: map[string]string{}, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4", "elb.com"}, serviceTypesFilter: []string{}, @@ -734,6 +767,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4", "elb.com"}, serviceTypesFilter: []string{}, @@ -753,6 +787,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ "zalando.org/dnsname": "mate.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -768,6 +803,7 @@ func testServiceSourceEndpoints(t *testing.T) { fqdnTemplate: "{{.Calibre}}.bar.example.com", labels: map[string]string{}, annotations: map[string]string{}, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -783,6 +819,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -800,6 +837,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", ttlAnnotationKey: "foo", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -817,6 +855,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", ttlAnnotationKey: "10", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -834,6 +873,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", ttlAnnotationKey: "1m", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -851,6 +891,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", ttlAnnotationKey: "-10", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -867,6 +908,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{string(v1.ServiceTypeLoadBalancer)}, @@ -883,6 +925,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{string(v1.ServiceTypeLoadBalancer)}, @@ -897,6 +940,7 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ internalHostnameAnnotationKey: "foo.internal.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.1.1.1", externalIPs: []string{}, lbs: []string{"1.2.3.4"}, @@ -915,6 +959,7 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", internalHostnameAnnotationKey: "foo.internal.example.org.", }, + ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.1.1.1", externalIPs: []string{}, lbs: []string{"1.2.3.4"}, @@ -932,6 +977,7 @@ func testServiceSourceEndpoints(t *testing.T) { labels: map[string]string{ "app": "web-external", }, + ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.1.1.1", externalIPs: []string{}, lbs: []string{"1.2.3.4"}, @@ -950,6 +996,7 @@ func testServiceSourceEndpoints(t *testing.T) { labels: map[string]string{ "app": "web-external", }, + ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.1.1.1", externalIPs: []string{}, lbs: []string{"1.2.3.4"}, @@ -968,6 +1015,7 @@ func testServiceSourceEndpoints(t *testing.T) { labels: map[string]string{ "app": "web-internal", }, + ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.1.1.1", externalIPs: []string{}, lbs: []string{"1.2.3.4"}, @@ -984,6 +1032,7 @@ func testServiceSourceEndpoints(t *testing.T) { labels: map[string]string{ "app": "web-internal", }, + ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.1.1.1", externalIPs: []string{}, lbs: []string{"1.2.3.4"}, @@ -992,6 +1041,38 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{hostnameAnnotationKey: "annotation.bar.example.com"}, expected: []*endpoint.Endpoint{}, }, + { + title: "dual-stack load-balancer service gets both addresses", + svcNamespace: "testing", + svcName: "foobar", + svcType: v1.ServiceTypeLoadBalancer, + labels: map[string]string{}, + ipFamilies: []v1.IPFamily{"IPv4", "IPv6"}, + clusterIP: "1.1.1.2,2001:db8::2", + externalIPs: []string{}, + lbs: []string{"1.1.1.1", "2001:db8::1"}, + serviceTypesFilter: []string{}, + annotations: map[string]string{hostnameAnnotationKey: "foobar.example.org"}, + expected: []*endpoint.Endpoint{ + {DNSName: "foobar.example.org", Targets: endpoint.Targets{"1.1.1.1", "2001:db8::1"}}, + }, + }, + { + title: "IPv6-only load-balancer service gets IPv6 endpoint", + svcNamespace: "testing", + svcName: "foobar-v6", + svcType: v1.ServiceTypeLoadBalancer, + labels: map[string]string{}, + ipFamilies: []v1.IPFamily{"IPv6"}, + clusterIP: "2001:db8::1", + externalIPs: []string{}, + lbs: []string{"2001:db8::2"}, + serviceTypesFilter: []string{}, + annotations: map[string]string{hostnameAnnotationKey: "foobar-v6.example.org"}, + expected: []*endpoint.Endpoint{ + {DNSName: "foobar-v6.example.org", Targets: endpoint.Targets{"2001:db8::2"}}, + }, + }, } { tc := tc t.Run(tc.title, func(t *testing.T) { diff --git a/source/source.go b/source/source.go index da9909923..5aceea763 100644 --- a/source/source.go +++ b/source/source.go @@ -239,8 +239,10 @@ func getTargetsFromTargetAnnotation(annotations map[string]string) endpoint.Targ // suitableType returns the DNS resource record type suitable for the target. // In this case type A for IPs and type CNAME for everything else. func suitableType(target string) string { - if net.ParseIP(target) != nil { + if net.ParseIP(target) != nil && net.ParseIP(target).To4() != nil { return endpoint.RecordTypeA + } else if net.ParseIP(target) != nil && net.ParseIP(target).To16() != nil { + return endpoint.RecordTypeAAAA } return endpoint.RecordTypeCNAME } @@ -250,12 +252,23 @@ func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoin var endpoints []*endpoint.Endpoint var aTargets endpoint.Targets + var aaaaTargets endpoint.Targets var cnameTargets endpoint.Targets for _, t := range targets { switch suitableType(t) { case endpoint.RecordTypeA: - aTargets = append(aTargets, t) + if !isIPv6String(t) { + aTargets = append(aTargets, t) + } else { + continue + } + case endpoint.RecordTypeAAAA: + if isIPv6String(t) { + aaaaTargets = append(aaaaTargets, t) + } else { + continue + } default: cnameTargets = append(cnameTargets, t) } @@ -274,6 +287,19 @@ func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoin endpoints = append(endpoints, epA) } + if len(aaaaTargets) > 0 { + epAAAA := &endpoint.Endpoint{ + DNSName: strings.TrimSuffix(hostname, "."), + Targets: aaaaTargets, + RecordTTL: ttl, + RecordType: endpoint.RecordTypeAAAA, + Labels: endpoint.NewLabels(), + ProviderSpecific: providerSpecific, + SetIdentifier: setIdentifier, + } + endpoints = append(endpoints, epAAAA) + } + if len(cnameTargets) > 0 { epCNAME := &endpoint.Endpoint{ DNSName: strings.TrimSuffix(hostname, "."), @@ -286,7 +312,6 @@ func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoin } endpoints = append(endpoints, epCNAME) } - return endpoints } @@ -348,3 +373,9 @@ func waitForDynamicCacheSync(ctx context.Context, factory dynamicInformerFactory } return nil } + +// isIPv6String returns if ip is IPv6. +func isIPv6String(ip string) bool { + netIP := net.ParseIP(ip) + return netIP != nil && netIP.To4() == nil +} diff --git a/source/source_test.go b/source/source_test.go index 0c31b93ba..d6befe804 100644 --- a/source/source_test.go +++ b/source/source_test.go @@ -94,6 +94,7 @@ func TestSuitableType(t *testing.T) { target, recordType, expected string }{ {"8.8.8.8", "", "A"}, + {"2001:db8::1", "", "AAAA"}, {"foo.example.org", "", "CNAME"}, {"bar.eu-central-1.elb.amazonaws.com", "", "CNAME"}, } { From de1db3f03d80f53a5d5ebfc976fb4ee889e1dd6f Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Sun, 28 Nov 2021 11:37:40 -0800 Subject: [PATCH 068/117] Unit test fixes --- endpoint/endpoint_test.go | 4 ++-- pkg/apis/externaldns/types_test.go | 3 ++- source/service_test.go | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/endpoint/endpoint_test.go b/endpoint/endpoint_test.go index fe61f8dc6..0f1392036 100644 --- a/endpoint/endpoint_test.go +++ b/endpoint/endpoint_test.go @@ -37,7 +37,7 @@ func TestNewEndpoint(t *testing.T) { func TestNewEndpointWithIPv6(t *testing.T) { e := NewEndpoint("example.org", "AAAA", "foo.com") - if e.DNSName != "example.com" || e.Targets[0] != "foo.com" || e.RecordType != "AAAA" { + if e.DNSName != "example.org" || e.Targets[0] != "foo.com" || e.RecordType != "AAAA" { t.Error("Endpoint is not initialized correctly") } @@ -46,7 +46,7 @@ func TestNewEndpointWithIPv6(t *testing.T) { } w := NewEndpoint("example.org", "", "load-balancer.com.") - if w.DNSName != "example.org" || e.Targets[0] != "load-balancer.com" || w.RecordType != "" { + if w.DNSName != "example.org" || w.Targets[0] != "load-balancer.com" || w.RecordType != "" { t.Error("Endpoint is not initialized correctly") } } diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index 7d4cc6ab8..d9c68480c 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -372,6 +372,7 @@ func TestParseFlags(t *testing.T) { "--transip-keyfile=/path/to/transip.key", "--digitalocean-api-page-size=100", "--managed-record-types=A", + "--managed-record-types=AAAA", "--managed-record-types=CNAME", "--managed-record-types=NS", "--rfc2136-batch-change-size=100", @@ -488,7 +489,7 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_TRANSIP_ACCOUNT": "transip", "EXTERNAL_DNS_TRANSIP_KEYFILE": "/path/to/transip.key", "EXTERNAL_DNS_DIGITALOCEAN_API_PAGE_SIZE": "100", - "EXTERNAL_DNS_MANAGED_RECORD_TYPES": "A\nCNAME\nNS", + "EXTERNAL_DNS_MANAGED_RECORD_TYPES": "A\nAAAA\nCNAME\nNS", "EXTERNAL_DNS_RFC2136_BATCH_CHANGE_SIZE": "100", "EXTERNAL_DNS_IBMCLOUD_PROXIED": "1", "EXTERNAL_DNS_IBMCLOUD_CONFIG_FILE": "ibmcloud.json", diff --git a/source/service_test.go b/source/service_test.go index 932d172e3..373298b0a 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -1054,7 +1054,8 @@ func testServiceSourceEndpoints(t *testing.T) { serviceTypesFilter: []string{}, annotations: map[string]string{hostnameAnnotationKey: "foobar.example.org"}, expected: []*endpoint.Endpoint{ - {DNSName: "foobar.example.org", Targets: endpoint.Targets{"1.1.1.1", "2001:db8::1"}}, + {DNSName: "foobar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.1.1.1"}}, + {DNSName: "foobar.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1"}}, }, }, { @@ -1070,7 +1071,7 @@ func testServiceSourceEndpoints(t *testing.T) { serviceTypesFilter: []string{}, annotations: map[string]string{hostnameAnnotationKey: "foobar-v6.example.org"}, expected: []*endpoint.Endpoint{ - {DNSName: "foobar-v6.example.org", Targets: endpoint.Targets{"2001:db8::2"}}, + {DNSName: "foobar-v6.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::2"}}, }, }, } { From 6b20ba301c9ae72d3cc673be0fc35f2165a1d6f0 Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Sat, 4 Dec 2021 11:18:11 -0800 Subject: [PATCH 069/117] Remove unnecessary unit test changes --- endpoint/endpoint_test.go | 16 ------------ source/service_test.go | 51 --------------------------------------- 2 files changed, 67 deletions(-) diff --git a/endpoint/endpoint_test.go b/endpoint/endpoint_test.go index 0f1392036..81e7f4c33 100644 --- a/endpoint/endpoint_test.go +++ b/endpoint/endpoint_test.go @@ -35,22 +35,6 @@ func TestNewEndpoint(t *testing.T) { } } -func TestNewEndpointWithIPv6(t *testing.T) { - e := NewEndpoint("example.org", "AAAA", "foo.com") - if e.DNSName != "example.org" || e.Targets[0] != "foo.com" || e.RecordType != "AAAA" { - t.Error("Endpoint is not initialized correctly") - } - - if e.Labels == nil { - t.Error("Labels is not initialized") - } - - w := NewEndpoint("example.org", "", "load-balancer.com.") - if w.DNSName != "example.org" || w.Targets[0] != "load-balancer.com" || w.RecordType != "" { - t.Error("Endpoint is not initialized correctly") - } -} - func TestTargetsSame(t *testing.T) { tests := []Targets{ {""}, diff --git a/source/service_test.go b/source/service_test.go index 373298b0a..e04d38024 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -187,7 +187,6 @@ func testServiceSourceEndpoints(t *testing.T) { labels map[string]string annotations map[string]string clusterIP string - ipFamilies []v1.IPFamily externalIPs []string lbs []string serviceTypesFilter []string @@ -202,7 +201,6 @@ func testServiceSourceEndpoints(t *testing.T) { svcType: v1.ServiceTypeLoadBalancer, labels: map[string]string{}, annotations: map[string]string{}, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -216,7 +214,6 @@ func testServiceSourceEndpoints(t *testing.T) { ignoreHostnameAnnotation: true, labels: map[string]string{}, annotations: map[string]string{}, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -231,7 +228,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -249,7 +245,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -264,7 +259,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.2.3.4", externalIPs: []string{}, lbs: []string{}, @@ -279,7 +273,6 @@ func testServiceSourceEndpoints(t *testing.T) { fqdnTemplate: "{{.Name}}.fqdn.org,{{.Name}}.fqdn.com", labels: map[string]string{}, annotations: map[string]string{}, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -297,7 +290,6 @@ func testServiceSourceEndpoints(t *testing.T) { ignoreHostnameAnnotation: true, labels: map[string]string{}, annotations: map[string]string{}, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -317,7 +309,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org., bar.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -340,7 +331,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org., bar.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -358,7 +348,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org., bar.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -376,7 +365,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org, bar.example.org", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -394,7 +382,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"lb.example.com"}, // Kubernetes omits the trailing dot serviceTypesFilter: []string{}, @@ -411,7 +398,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org", // Trailing dot is omitted }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4", "lb.example.com"}, // Kubernetes omits the trailing dot serviceTypesFilter: []string{}, @@ -430,7 +416,6 @@ func testServiceSourceEndpoints(t *testing.T) { controllerAnnotationKey: controllerAnnotationValue, hostnameAnnotationKey: "foo.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -449,7 +434,6 @@ func testServiceSourceEndpoints(t *testing.T) { controllerAnnotationKey: "some-other-tool", hostnameAnnotationKey: "foo.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -465,7 +449,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -483,7 +466,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -498,7 +480,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -517,7 +498,6 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", "service.beta.kubernetes.io/external-traffic": "OnlyLocal", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -536,7 +516,6 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", "service.beta.kubernetes.io/external-traffic": "SomethingElse", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -553,7 +532,6 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", "service.beta.kubernetes.io/external-traffic": "OnlyLocal", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -571,7 +549,6 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", "service.beta.kubernetes.io/external-traffic": "Global", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -590,7 +567,6 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", "service.beta.kubernetes.io/external-traffic": "OnlyLocal", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -605,7 +581,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{}, serviceTypesFilter: []string{}, @@ -620,7 +595,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{"10.2.3.4", "11.2.3.4"}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -637,7 +611,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4", "8.8.8.8"}, serviceTypesFilter: []string{}, @@ -654,7 +627,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ "zalando.org/dnsname": "foo.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -670,7 +642,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ "zalando.org/dnsname": "foo.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -687,7 +658,6 @@ func testServiceSourceEndpoints(t *testing.T) { labels: map[string]string{ "dns": "route53", }, - ipFamilies: []v1.IPFamily{"IPv4"}, annotations: map[string]string{ "domainName": "foo.example.org., bar.example.org", }, @@ -709,7 +679,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ kopsDNSControllerInternalHostnameAnnotationKey: "internal.foo.example.org", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4", "lb.example.com"}, serviceTypesFilter: []string{}, @@ -729,7 +698,6 @@ func testServiceSourceEndpoints(t *testing.T) { kopsDNSControllerInternalHostnameAnnotationKey: "internal.foo.example.org., internal.bar.example.org", kopsDNSControllerHostnameAnnotationKey: "foo.example.org., bar.example.org", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -748,7 +716,6 @@ func testServiceSourceEndpoints(t *testing.T) { fqdnTemplate: "{{.Name}}.bar.example.com", labels: map[string]string{}, annotations: map[string]string{}, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4", "elb.com"}, serviceTypesFilter: []string{}, @@ -767,7 +734,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4", "elb.com"}, serviceTypesFilter: []string{}, @@ -787,7 +753,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ "zalando.org/dnsname": "mate.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -803,7 +768,6 @@ func testServiceSourceEndpoints(t *testing.T) { fqdnTemplate: "{{.Calibre}}.bar.example.com", labels: map[string]string{}, annotations: map[string]string{}, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -819,7 +783,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -837,7 +800,6 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", ttlAnnotationKey: "foo", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -855,7 +817,6 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", ttlAnnotationKey: "10", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -873,7 +834,6 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", ttlAnnotationKey: "1m", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -891,7 +851,6 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", ttlAnnotationKey: "-10", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{}, @@ -908,7 +867,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{string(v1.ServiceTypeLoadBalancer)}, @@ -925,7 +883,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ hostnameAnnotationKey: "foo.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, externalIPs: []string{}, lbs: []string{"1.2.3.4"}, serviceTypesFilter: []string{string(v1.ServiceTypeLoadBalancer)}, @@ -940,7 +897,6 @@ func testServiceSourceEndpoints(t *testing.T) { annotations: map[string]string{ internalHostnameAnnotationKey: "foo.internal.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.1.1.1", externalIPs: []string{}, lbs: []string{"1.2.3.4"}, @@ -959,7 +915,6 @@ func testServiceSourceEndpoints(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", internalHostnameAnnotationKey: "foo.internal.example.org.", }, - ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.1.1.1", externalIPs: []string{}, lbs: []string{"1.2.3.4"}, @@ -977,7 +932,6 @@ func testServiceSourceEndpoints(t *testing.T) { labels: map[string]string{ "app": "web-external", }, - ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.1.1.1", externalIPs: []string{}, lbs: []string{"1.2.3.4"}, @@ -996,7 +950,6 @@ func testServiceSourceEndpoints(t *testing.T) { labels: map[string]string{ "app": "web-external", }, - ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.1.1.1", externalIPs: []string{}, lbs: []string{"1.2.3.4"}, @@ -1015,7 +968,6 @@ func testServiceSourceEndpoints(t *testing.T) { labels: map[string]string{ "app": "web-internal", }, - ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.1.1.1", externalIPs: []string{}, lbs: []string{"1.2.3.4"}, @@ -1032,7 +984,6 @@ func testServiceSourceEndpoints(t *testing.T) { labels: map[string]string{ "app": "web-internal", }, - ipFamilies: []v1.IPFamily{"IPv4"}, clusterIP: "1.1.1.1", externalIPs: []string{}, lbs: []string{"1.2.3.4"}, @@ -1047,7 +998,6 @@ func testServiceSourceEndpoints(t *testing.T) { svcName: "foobar", svcType: v1.ServiceTypeLoadBalancer, labels: map[string]string{}, - ipFamilies: []v1.IPFamily{"IPv4", "IPv6"}, clusterIP: "1.1.1.2,2001:db8::2", externalIPs: []string{}, lbs: []string{"1.1.1.1", "2001:db8::1"}, @@ -1064,7 +1014,6 @@ func testServiceSourceEndpoints(t *testing.T) { svcName: "foobar-v6", svcType: v1.ServiceTypeLoadBalancer, labels: map[string]string{}, - ipFamilies: []v1.IPFamily{"IPv6"}, clusterIP: "2001:db8::1", externalIPs: []string{}, lbs: []string{"2001:db8::2"}, From 2eed9cb6ba13fbfe66e737d1d16301097613fb3c Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Sat, 4 Dec 2021 23:50:59 -0800 Subject: [PATCH 070/117] Add AAAA records metrics --- controller/controller.go | 86 +++++++++++---- controller/controller_test.go | 199 ++++++++++++++++++++++++++++++---- docs/faq.md | 20 ++-- 3 files changed, 253 insertions(+), 52 deletions(-) diff --git a/controller/controller.go b/controller/controller.go index 528d870fa..b09e0bb4b 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -102,6 +102,14 @@ var ( Help: "Number of Registry A records.", }, ) + registryAAAARecords = prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: "external_dns", + Subsystem: "registry", + Name: "aaaa_records", + Help: "Number of Registry AAAA records.", + }, + ) sourceARecords = prometheus.NewGauge( prometheus.GaugeOpts{ Namespace: "external_dns", @@ -110,6 +118,14 @@ var ( Help: "Number of Source A records.", }, ) + sourceAAAARecords = prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: "external_dns", + Subsystem: "source", + Name: "aaaa_records", + Help: "Number of Source AAAA records.", + }, + ) verifiedARecords = prometheus.NewGauge( prometheus.GaugeOpts{ Namespace: "external_dns", @@ -118,6 +134,14 @@ var ( Help: "Number of DNS A-records that exists both in source and registry.", }, ) + verifiedAAAARecords = prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: "external_dns", + Subsystem: "controller", + Name: "verified_aaaa_records", + Help: "Number of DNS AAAA-records that exists both in source and registry.", + }, + ) ) func init() { @@ -130,8 +154,11 @@ func init() { prometheus.MustRegister(deprecatedSourceErrors) prometheus.MustRegister(controllerNoChangesTotal) prometheus.MustRegister(registryARecords) + prometheus.MustRegister(registryAAAARecords) prometheus.MustRegister(sourceARecords) + prometheus.MustRegister(sourceAAAARecords) prometheus.MustRegister(verifiedARecords) + prometheus.MustRegister(verifiedAAAARecords) } // Controller is responsible for orchestrating the different components. @@ -171,8 +198,9 @@ func (c *Controller) RunOnce(ctx context.Context) error { missingRecords := c.Registry.MissingRecords() registryEndpointsTotal.Set(float64(len(records))) - regARecords := filterARecords(records) - registryARecords.Set(float64(len(regARecords))) + regARecords, regAAAARecords := countAddressRecords(records) + registryARecords.Set(float64(regARecords)) + registryAAAARecords.Set(float64(regAAAARecords)) ctx = context.WithValue(ctx, provider.RecordsContextKey, records) endpoints, err := c.Source.Endpoints(ctx) @@ -182,10 +210,12 @@ func (c *Controller) RunOnce(ctx context.Context) error { return err } sourceEndpointsTotal.Set(float64(len(endpoints))) - srcARecords := filterARecords(endpoints) - sourceARecords.Set(float64(len(srcARecords))) - vRecords := fetchMatchingARecords(endpoints, records) - verifiedARecords.Set(float64(len(vRecords))) + srcARecords, srcAAAARecords := countAddressRecords(endpoints) + sourceARecords.Set(float64(srcARecords)) + sourceAAAARecords.Set(float64(srcAAAARecords)) + vARecords, vAAAARecords := countMatchingAddressRecords(endpoints, records) + verifiedARecords.Set(float64(vARecords)) + verifiedAAAARecords.Set(float64(vAAAARecords)) endpoints = c.Registry.AdjustEndpoints(endpoints) if len(missingRecords) > 0 { @@ -238,30 +268,44 @@ func (c *Controller) RunOnce(ctx context.Context) error { return nil } -// Checks and returns the intersection of A records in endpoint and registry. -func fetchMatchingARecords(endpoints []*endpoint.Endpoint, registryRecords []*endpoint.Endpoint) []string { - aRecords := filterARecords(endpoints) - recordsMap := make(map[string]struct{}) +// Counts the intersections of A and AAAA records in endpoint and registry. +func countMatchingAddressRecords(endpoints []*endpoint.Endpoint, registryRecords []*endpoint.Endpoint) (int, int) { + recordsMap := make(map[string]map[string]struct{}) for _, regRecord := range registryRecords { - recordsMap[regRecord.DNSName] = struct{}{} + if _, found := recordsMap[regRecord.DNSName]; !found { + recordsMap[regRecord.DNSName] = make(map[string]struct{}) + } + recordsMap[regRecord.DNSName][regRecord.RecordType] = struct{}{} } - var cm []string - for _, sourceRecord := range aRecords { - if _, found := recordsMap[sourceRecord]; found { - cm = append(cm, sourceRecord) + aCount := 0 + aaaaCount := 0 + for _, sourceRecord := range endpoints { + if _, found := recordsMap[sourceRecord.DNSName]; found { + if _, found := recordsMap[sourceRecord.DNSName][sourceRecord.RecordType]; found { + switch sourceRecord.RecordType { + case endpoint.RecordTypeA: + aCount++ + case endpoint.RecordTypeAAAA: + aaaaCount++ + } + } } } - return cm + return aCount, aaaaCount } -func filterARecords(endpoints []*endpoint.Endpoint) []string { - var aRecords []string +func countAddressRecords(endpoints []*endpoint.Endpoint) (int, int) { + aCount := 0 + aaaaCount := 0 for _, endPoint := range endpoints { - if endPoint.RecordType == endpoint.RecordTypeA { - aRecords = append(aRecords, endPoint.DNSName) + switch endPoint.RecordType { + case endpoint.RecordTypeA: + aCount++ + case endpoint.RecordTypeAAAA: + aaaaCount++ } } - return aRecords + return aCount, aaaaCount } // ScheduleRunOnce makes sure execution happens at most once per interval. diff --git a/controller/controller_test.go b/controller/controller_test.go index a56a0b3ac..f6b03d433 100644 --- a/controller/controller_test.go +++ b/controller/controller_test.go @@ -21,6 +21,7 @@ import ( "errors" "math" "reflect" + "sort" "testing" "time" @@ -83,32 +84,20 @@ func (p *errorMockProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, // ApplyChanges validates that the passed in changes satisfy the assumptions. func (p *mockProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { - if len(changes.Create) != len(p.ExpectChanges.Create) { - return errors.New("number of created records is wrong") + if err := verifyEndpoints(changes.Create, p.ExpectChanges.Create); err != nil { + return err } - for i := range changes.Create { - if changes.Create[i].DNSName != p.ExpectChanges.Create[i].DNSName || !changes.Create[i].Targets.Same(p.ExpectChanges.Create[i].Targets) { - return errors.New("created record is wrong") - } + if err := verifyEndpoints(changes.UpdateNew, p.ExpectChanges.UpdateNew); err != nil { + return err } - for i := range changes.UpdateNew { - if changes.UpdateNew[i].DNSName != p.ExpectChanges.UpdateNew[i].DNSName || !changes.UpdateNew[i].Targets.Same(p.ExpectChanges.UpdateNew[i].Targets) { - return errors.New("delete record is wrong") - } + if err := verifyEndpoints(changes.UpdateOld, p.ExpectChanges.UpdateOld); err != nil { + return err } - for i := range changes.UpdateOld { - if changes.UpdateOld[i].DNSName != p.ExpectChanges.UpdateOld[i].DNSName || !changes.UpdateOld[i].Targets.Same(p.ExpectChanges.UpdateOld[i].Targets) { - return errors.New("delete record is wrong") - } - } - - for i := range changes.Delete { - if changes.Delete[i].DNSName != p.ExpectChanges.Delete[i].DNSName || !changes.Delete[i].Targets.Same(p.ExpectChanges.Delete[i].Targets) { - return errors.New("delete record is wrong") - } + if err := verifyEndpoints(changes.Delete, p.ExpectChanges.Delete); err != nil { + return err } if !reflect.DeepEqual(ctx.Value(provider.RecordsContextKey), p.RecordsStore) { @@ -117,6 +106,21 @@ func (p *mockProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) return nil } +func verifyEndpoints(actual, expected []*endpoint.Endpoint) error { + if len(actual) != len(expected) { + return errors.New("number of records is wrong") + } + sort.Slice(actual, func(i, j int) bool { + return actual[i].DNSName < actual[j].DNSName + }) + for i := range actual { + if actual[i].DNSName != expected[i].DNSName || !actual[i].Targets.Same(expected[i].Targets) { + return errors.New("record is wrong") + } + } + return nil +} + // newMockProvider creates a new mockProvider returning the given endpoints and validating the desired changes. func newMockProvider(endpoints []*endpoint.Endpoint, changes *plan.Changes) provider.Provider { dnsProvider := &mockProvider{ @@ -132,7 +136,7 @@ func TestRunOnce(t *testing.T) { // Fake some desired endpoints coming from our source. source := new(testutils.MockSource) cfg := externaldns.NewConfig() - cfg.ManagedDNSRecordTypes = []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME} + cfg.ManagedDNSRecordTypes = []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME} source.On("Endpoints").Return([]*endpoint.Endpoint{ { DNSName: "create-record", @@ -144,6 +148,16 @@ func TestRunOnce(t *testing.T) { RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"8.8.4.4"}, }, + { + DNSName: "create-aaaa-record", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::1"}, + }, + { + DNSName: "update-aaaa-record", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::2"}, + }, }, nil) // Fake some existing records in our DNS provider and validate some desired changes. @@ -159,18 +173,32 @@ func TestRunOnce(t *testing.T) { RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"4.3.2.1"}, }, + { + DNSName: "update-aaaa-record", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::3"}, + }, + { + DNSName: "delete-aaaa-record", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::4"}, + }, }, &plan.Changes{ Create: []*endpoint.Endpoint{ + {DNSName: "create-aaaa-record", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1"}}, {DNSName: "create-record", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, UpdateNew: []*endpoint.Endpoint{ + {DNSName: "update-aaaa-record", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::2"}}, {DNSName: "update-record", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"8.8.4.4"}}, }, UpdateOld: []*endpoint.Endpoint{ + {DNSName: "update-aaaa-record", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::3"}}, {DNSName: "update-record", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"8.8.8.8"}}, }, Delete: []*endpoint.Endpoint{ + {DNSName: "delete-aaaa-record", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::4"}}, {DNSName: "delete-record", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"4.3.2.1"}}, }, }, @@ -193,6 +221,7 @@ func TestRunOnce(t *testing.T) { source.AssertExpectations(t) // check the verified records assert.Equal(t, math.Float64bits(1), valueFromMetric(verifiedARecords)) + assert.Equal(t, math.Float64bits(1), valueFromMetric(verifiedAAAARecords)) } func valueFromMetric(metric prometheus.Gauge) uint64 { @@ -253,7 +282,7 @@ func TestShouldRunOnce(t *testing.T) { func testControllerFiltersDomains(t *testing.T, configuredEndpoints []*endpoint.Endpoint, domainFilter endpoint.DomainFilterInterface, providerEndpoints []*endpoint.Endpoint, expectedChanges []*plan.Changes) { t.Helper() cfg := externaldns.NewConfig() - cfg.ManagedDNSRecordTypes = []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME} + cfg.ManagedDNSRecordTypes = []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME} source := new(testutils.MockSource) source.On("Endpoints").Return(configuredEndpoints, nil) @@ -526,6 +555,85 @@ func TestVerifyARecords(t *testing.T) { }}, ) assert.Equal(t, math.Float64bits(2), valueFromMetric(verifiedARecords)) + assert.Equal(t, math.Float64bits(0), valueFromMetric(verifiedAAAARecords)) +} + +func TestVerifyAAAARecords(t *testing.T) { + testControllerFiltersDomains( + t, + []*endpoint.Endpoint{ + { + DNSName: "create-record.used.tld", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::1"}, + }, + { + DNSName: "some-record.used.tld", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::2"}, + }, + }, + endpoint.NewDomainFilter([]string{"used.tld"}), + []*endpoint.Endpoint{ + { + DNSName: "some-record.used.tld", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::2"}, + }, + { + DNSName: "create-record.used.tld", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::1"}, + }, + }, + []*plan.Changes{}, + ) + assert.Equal(t, math.Float64bits(2), valueFromMetric(verifiedAAAARecords)) + + testControllerFiltersDomains( + t, + []*endpoint.Endpoint{ + { + DNSName: "some-record.1.used.tld", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::1"}, + }, + { + DNSName: "some-record.2.used.tld", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::2"}, + }, + { + DNSName: "some-record.3.used.tld", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::3"}, + }, + }, + endpoint.NewDomainFilter([]string{"used.tld"}), + []*endpoint.Endpoint{ + { + DNSName: "some-record.1.used.tld", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::1"}, + }, + { + DNSName: "some-record.2.used.tld", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::2"}, + }, + }, + []*plan.Changes{{ + Create: []*endpoint.Endpoint{ + { + DNSName: "some-record.3.used.tld", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::3"}, + }, + }, + }}, + ) + assert.Equal(t, math.Float64bits(0), valueFromMetric(verifiedARecords)) + assert.Equal(t, math.Float64bits(2), valueFromMetric(verifiedAAAARecords)) } func TestARecords(t *testing.T) { @@ -628,3 +736,50 @@ func TestMissingRecordsApply(t *testing.T) { }, }) } + +func TestAAAARecords(t *testing.T) { + testControllerFiltersDomains( + t, + []*endpoint.Endpoint{ + { + DNSName: "record1.used.tld", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::1"}, + }, + { + DNSName: "record2.used.tld", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::2"}, + }, + { + DNSName: "_mysql-svc._tcp.mysql.used.tld", + RecordType: endpoint.RecordTypeSRV, + Targets: endpoint.Targets{"0 50 30007 mysql.used.tld"}, + }, + }, + endpoint.NewDomainFilter([]string{"used.tld"}), + []*endpoint.Endpoint{ + { + DNSName: "record1.used.tld", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::1"}, + }, + { + DNSName: "_mysql-svc._tcp.mysql.used.tld", + RecordType: endpoint.RecordTypeSRV, + Targets: endpoint.Targets{"0 50 30007 mysql.used.tld"}, + }, + }, + []*plan.Changes{{ + Create: []*endpoint.Endpoint{ + { + DNSName: "record2.used.tld", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::2"}, + }, + }, + }}, + ) + assert.Equal(t, math.Float64bits(2), valueFromMetric(sourceAAAARecords)) + assert.Equal(t, math.Float64bits(1), valueFromMetric(registryAAAARecords)) +} diff --git a/docs/faq.md b/docs/faq.md index 28b111dd8..044ee7aa8 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -178,16 +178,18 @@ You can use the host label in the metric to figure out if the request was agains Here is the full list of available metrics provided by ExternalDNS: -| Name | Description | Type | -| --------------------------------------------------- | ------------------------------------------------------- | ------- | -| external_dns_controller_last_sync_timestamp_seconds | Timestamp of last successful sync with the DNS provider | Gauge | -| external_dns_registry_endpoints_total | Number of Endpoints in all sources | Gauge | -| external_dns_registry_errors_total | Number of Registry errors | Counter | -| external_dns_source_endpoints_total | Number of Endpoints in the registry | Gauge | -| external_dns_source_errors_total | Number of Source errors | Counter | -| external_dns_controller_verified_records | Number of DNS A-records that exists both in | Gauge | -| | source & registry | | +| Name | Description | Type | +| --------------------------------------------------- | ------------------------------------------------------------------ | ------- | +| external_dns_controller_last_sync_timestamp_seconds | Timestamp of last successful sync with the DNS provider | Gauge | +| external_dns_registry_endpoints_total | Number of Endpoints in all sources | Gauge | +| external_dns_registry_errors_total | Number of Registry errors | Counter | +| external_dns_source_endpoints_total | Number of Endpoints in the registry | Gauge | +| external_dns_source_errors_total | Number of Source errors | Counter | +| external_dns_controller_verified_aaaa_records | Number of DNS AAAA-records that exists both in source and registry | Gauge | +| external_dns_controller_verified_a_records | Number of DNS A-records that exists both in source and registry | Gauge | +| external_dns_registry_aaaa_records | Number of AAAA records in registry | Gauge | | external_dns_registry_a_records | Number of A records in registry | Gauge | +| external_dns_source_aaaa_records | Number of AAAA records in source | Gauge | | external_dns_source_a_records | Number of A records in source | Gauge | ### How can I run ExternalDNS under a specific GCP Service Account, e.g. to access DNS records in other projects? From 258986c7cef89c19d654c6c37657eef23890979c Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Mon, 14 Feb 2022 23:12:54 -0800 Subject: [PATCH 071/117] Add test case for extracting IPv6 from Ingress status --- source/ingress_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/source/ingress_test.go b/source/ingress_test.go index ae3dd3b7c..cdd23505a 100644 --- a/source/ingress_test.go +++ b/source/ingress_test.go @@ -409,6 +409,25 @@ func testIngressEndpoints(t *testing.T) { }, }, }, + { + title: "ipv6 ingress", + targetNamespace: "", + ingressItems: []fakeIngress{ + { + name: "fake1", + namespace: namespace, + dnsnames: []string{"example.org"}, + ips: []string{"2001:DB8::1"}, + }, + }, + expected: []*endpoint.Endpoint{ + { + DNSName: "example.org", + RecordType: endpoint.RecordTypeAAAA, + Targets: endpoint.Targets{"2001:DB8::1"}, + }, + }, + }, { title: "ignore rules", targetNamespace: "", From 98532480b0e19e54fb21534d7940597a99a36147 Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Thu, 20 Oct 2022 22:06:31 -0700 Subject: [PATCH 072/117] Support dual-stack domains in TXT registry --- registry/txt.go | 51 +++++++++++------- registry/txt_test.go | 124 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 150 insertions(+), 25 deletions(-) diff --git a/registry/txt.go b/registry/txt.go index faa3b7453..d423aaac7 100644 --- a/registry/txt.go +++ b/registry/txt.go @@ -54,6 +54,8 @@ type TXTRegistry struct { missingTXTRecords []*endpoint.Endpoint } +const keySuffixAAAA = ":AAAA" + // NewTXTRegistry returns new TXTRegistry object func NewTXTRegistry(provider provider.Provider, txtPrefix, txtSuffix, ownerID string, cacheInterval time.Duration, txtWildcardReplacement string, managedRecordTypes []string) (*TXTRegistry, error) { if ownerID == "" { @@ -77,7 +79,7 @@ func NewTXTRegistry(provider provider.Provider, txtPrefix, txtSuffix, ownerID st } func getSupportedTypes() []string { - return []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME, endpoint.RecordTypeNS} + return []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME, endpoint.RecordTypeNS} } func (im *TXTRegistry) GetDomainFilter() endpoint.DomainFilterInterface { @@ -123,7 +125,11 @@ func (im *TXTRegistry) Records(ctx context.Context) ([]*endpoint.Endpoint, error if err != nil { return nil, err } - key := fmt.Sprintf("%s::%s", im.mapper.toEndpointName(record.DNSName), record.SetIdentifier) + endpointName, isAAAA := im.mapper.toEndpointName(record.DNSName) + key := fmt.Sprintf("%s::%s", endpointName, record.SetIdentifier) + if isAAAA { + key += keySuffixAAAA + } labelMap[key] = labels txtRecordsMap[record.DNSName] = struct{}{} } @@ -139,6 +145,9 @@ func (im *TXTRegistry) Records(ctx context.Context) ([]*endpoint.Endpoint, error } dnsName := strings.Join(dnsNameSplit, ".") key := fmt.Sprintf("%s::%s", dnsName, ep.SetIdentifier) + if ep.RecordType == endpoint.RecordTypeAAAA { + key += keySuffixAAAA + } if labels, ok := labelMap[key]; ok { for k, v := range labels { ep.Labels[k] = v @@ -194,13 +203,15 @@ func (im *TXTRegistry) generateTXTRecord(r *endpoint.Endpoint) []*endpoint.Endpo endpoints := make([]*endpoint.Endpoint, 0) - // old TXT record format - txt := endpoint.NewEndpoint(im.mapper.toTXTName(r.DNSName), endpoint.RecordTypeTXT, r.Labels.Serialize(true)) - if txt != nil { - txt.WithSetIdentifier(r.SetIdentifier) - txt.Labels[endpoint.OwnedRecordLabelKey] = r.DNSName - txt.ProviderSpecific = r.ProviderSpecific - endpoints = append(endpoints, txt) + if r.RecordType != endpoint.RecordTypeAAAA { + // old TXT record format + txt := endpoint.NewEndpoint(im.mapper.toTXTName(r.DNSName), endpoint.RecordTypeTXT, r.Labels.Serialize(true)) + if txt != nil { + txt.WithSetIdentifier(r.SetIdentifier) + txt.Labels[endpoint.OwnedRecordLabelKey] = r.DNSName + txt.ProviderSpecific = r.ProviderSpecific + endpoints = append(endpoints, txt) + } } // new TXT record format (containing record type) @@ -290,12 +301,12 @@ func (im *TXTRegistry) AdjustEndpoints(endpoints []*endpoint.Endpoint) []*endpoi */ /** - nameMapper defines interface which maps the dns name defined for the source - to the dns name which TXT record will be created with + nameMapper is the interface for mapping between the endpoint for the source + and the endpoint for the TXT record. */ type nameMapper interface { - toEndpointName(string) string + toEndpointName(string) (endpointName string, isAAAA bool) toTXTName(string) string toNewTXTName(string, string) string } @@ -312,14 +323,14 @@ func newaffixNameMapper(prefix, suffix, wildcardReplacement string) affixNameMap return affixNameMapper{prefix: strings.ToLower(prefix), suffix: strings.ToLower(suffix), wildcardReplacement: strings.ToLower(wildcardReplacement)} } -func dropRecordType(name string) string { +func extractRecordType(name string) (baseName, recordType string) { nameS := strings.Split(name, "-") for _, t := range getSupportedTypes() { if nameS[0] == strings.ToLower(t) { - return strings.TrimPrefix(name, nameS[0]+"-") + return strings.TrimPrefix(name, nameS[0]+"-"), t } } - return name + return name, "" } // dropAffix strips TXT record to find an endpoint name it manages @@ -362,20 +373,20 @@ func (pr affixNameMapper) isSuffix() bool { return len(pr.prefix) == 0 && len(pr.suffix) > 0 } -func (pr affixNameMapper) toEndpointName(txtDNSName string) string { - lowerDNSName := dropRecordType(strings.ToLower(txtDNSName)) +func (pr affixNameMapper) toEndpointName(txtDNSName string) (endpointName string, isAAAA bool) { + lowerDNSName, recordType := extractRecordType(strings.ToLower(txtDNSName)) // drop prefix if strings.HasPrefix(lowerDNSName, pr.prefix) && pr.isPrefix() { - return pr.dropAffix(lowerDNSName) + return pr.dropAffix(lowerDNSName), recordType == "AAAA" } // drop suffix if pr.isSuffix() { DNSName := strings.SplitN(lowerDNSName, ".", 2) - return pr.dropAffix(DNSName[0]) + "." + DNSName[1] + return pr.dropAffix(DNSName[0]) + "." + DNSName[1], recordType == "AAAA" } - return "" + return "", false } func (pr affixNameMapper) toTXTName(endpointDNSName string) string { diff --git a/registry/txt_test.go b/registry/txt_test.go index 5e8168045..2f7297fb1 100644 --- a/registry/txt_test.go +++ b/registry/txt_test.go @@ -101,6 +101,10 @@ func testTXTRegistryRecordsPrefixed(t *testing.T) { newEndpointWithOwner("multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-2"), newEndpointWithOwner("*.wildcard.test-zone.example.org", "foo.loadbalancer.com", endpoint.RecordTypeCNAME, ""), newEndpointWithOwner("txt.wc.wildcard.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), + newEndpointWithOwner("dualstack.test-zone.example.org", "1.1.1.1", endpoint.RecordTypeA, ""), + newEndpointWithOwner("txt.dualstack.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), + newEndpointWithOwner("dualstack.test-zone.example.org", "2001:DB8::1", endpoint.RecordTypeAAAA, ""), + newEndpointWithOwner("aaaa-txt.dualstack.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner-2\"", endpoint.RecordTypeTXT, ""), }, }) expectedRecords := []*endpoint.Endpoint{ @@ -181,6 +185,22 @@ func testTXTRegistryRecordsPrefixed(t *testing.T) { endpoint.OwnerLabelKey: "owner", }, }, + { + DNSName: "dualstack.test-zone.example.org", + Targets: endpoint.Targets{"1.1.1.1"}, + RecordType: endpoint.RecordTypeA, + Labels: map[string]string{ + endpoint.OwnerLabelKey: "owner", + }, + }, + { + DNSName: "dualstack.test-zone.example.org", + Targets: endpoint.Targets{"2001:DB8::1"}, + RecordType: endpoint.RecordTypeAAAA, + Labels: map[string]string{ + endpoint.OwnerLabelKey: "owner-2", + }, + }, } r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "wc", []string{}) @@ -214,6 +234,10 @@ func testTXTRegistryRecordsSuffixed(t *testing.T) { newEndpointWithOwner("multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-1"), newEndpointWithOwner("multiple.test-zone.example.org", "lb2.loadbalancer.com", endpoint.RecordTypeCNAME, "").WithSetIdentifier("test-set-2"), newEndpointWithOwner("multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-2"), + newEndpointWithOwner("dualstack.test-zone.example.org", "1.1.1.1", endpoint.RecordTypeA, ""), + newEndpointWithOwner("dualstack-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), + newEndpointWithOwner("dualstack.test-zone.example.org", "2001:DB8::1", endpoint.RecordTypeAAAA, ""), + newEndpointWithOwner("aaaa-dualstack-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner-2\"", endpoint.RecordTypeTXT, ""), }, }) expectedRecords := []*endpoint.Endpoint{ @@ -286,6 +310,22 @@ func testTXTRegistryRecordsSuffixed(t *testing.T) { endpoint.OwnerLabelKey: "", }, }, + { + DNSName: "dualstack.test-zone.example.org", + Targets: endpoint.Targets{"1.1.1.1"}, + RecordType: endpoint.RecordTypeA, + Labels: map[string]string{ + endpoint.OwnerLabelKey: "owner", + }, + }, + { + DNSName: "dualstack.test-zone.example.org", + Targets: endpoint.Targets{"2001:DB8::1"}, + RecordType: endpoint.RecordTypeAAAA, + Labels: map[string]string{ + endpoint.OwnerLabelKey: "owner-2", + }, + }, } r, _ := NewTXTRegistry(p, "", "-txt", "owner", time.Hour, "", []string{}) @@ -315,6 +355,10 @@ func testTXTRegistryRecordsNoPrefix(t *testing.T) { newEndpointWithOwner("txt.tar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner-2\"", endpoint.RecordTypeTXT, ""), newEndpointWithOwner("foobar.test-zone.example.org", "foobar.loadbalancer.com", endpoint.RecordTypeCNAME, ""), newEndpointWithOwner("foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), + newEndpointWithOwner("dualstack.test-zone.example.org", "1.1.1.1", endpoint.RecordTypeA, ""), + newEndpointWithOwner("dualstack.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), + newEndpointWithOwner("dualstack.test-zone.example.org", "2001:DB8::1", endpoint.RecordTypeAAAA, ""), + newEndpointWithOwner("aaaa-dualstack.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner-2\"", endpoint.RecordTypeTXT, ""), }, }) expectedRecords := []*endpoint.Endpoint{ @@ -367,6 +411,22 @@ func testTXTRegistryRecordsNoPrefix(t *testing.T) { endpoint.OwnerLabelKey: "owner", }, }, + { + DNSName: "dualstack.test-zone.example.org", + Targets: endpoint.Targets{"1.1.1.1"}, + RecordType: endpoint.RecordTypeA, + Labels: map[string]string{ + endpoint.OwnerLabelKey: "owner", + }, + }, + { + DNSName: "dualstack.test-zone.example.org", + Targets: endpoint.Targets{"2001:DB8::1"}, + RecordType: endpoint.RecordTypeAAAA, + Labels: map[string]string{ + endpoint.OwnerLabelKey: "owner-2", + }, + }, } r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}) @@ -1012,6 +1072,7 @@ func TestCacheMethods(t *testing.T) { newEndpointWithOwner("thing2.com", "1.2.3.4", "CNAME", "owner"), newEndpointWithOwner("thing3.com", "1.2.3.4", "A", "owner"), newEndpointWithOwner("thing4.com", "1.2.3.4", "A", "owner"), + newEndpointWithOwner("thing4.com", "2001:DB8::1", "AAAA", "owner"), newEndpointWithOwner("thing5.com", "1.2.3.5", "A", "owner"), } @@ -1022,6 +1083,7 @@ func TestCacheMethods(t *testing.T) { newEndpointWithOwner("thing4.com", "1.2.3.4", "A", "owner"), newEndpointWithOwner("thing5.com", "1.2.3.5", "A", "owner"), newEndpointWithOwner("thing.com", "1.2.3.6", "A", "owner2"), + newEndpointWithOwner("thing4.com", "2001:DB8::2", "AAAA", "owner"), } expectedCacheAfterDelete := []*endpoint.Endpoint{ @@ -1032,6 +1094,7 @@ func TestCacheMethods(t *testing.T) { newEndpointWithOwner("thing5.com", "1.2.3.5", "A", "owner"), } // test add cache + registry.addToCache(newEndpointWithOwner("thing4.com", "2001:DB8::1", "AAAA", "owner")) registry.addToCache(newEndpointWithOwner("thing5.com", "1.2.3.5", "A", "owner")) if !reflect.DeepEqual(expectedCacheAfterAdd, registry.recordsCache) { @@ -1041,6 +1104,8 @@ func TestCacheMethods(t *testing.T) { // test update cache registry.removeFromCache(newEndpointWithOwner("thing.com", "1.2.3.4", "A", "owner")) registry.addToCache(newEndpointWithOwner("thing.com", "1.2.3.6", "A", "owner2")) + registry.removeFromCache(newEndpointWithOwner("thing4.com", "2001:DB8::1", "AAAA", "owner")) + registry.addToCache(newEndpointWithOwner("thing4.com", "2001:DB8::2", "AAAA", "owner")) // ensure it was updated if !reflect.DeepEqual(expectedCacheAfterUpdate, registry.recordsCache) { t.Fatalf("expected endpoints should match endpoints from cache: expected %v, but got %v", expectedCacheAfterUpdate, registry.recordsCache) @@ -1048,6 +1113,7 @@ func TestCacheMethods(t *testing.T) { // test deleting a record registry.removeFromCache(newEndpointWithOwner("thing.com", "1.2.3.6", "A", "owner2")) + registry.removeFromCache(newEndpointWithOwner("thing4.com", "2001:DB8::2", "AAAA", "owner")) // ensure it was deleted if !reflect.DeepEqual(expectedCacheAfterDelete, registry.recordsCache) { t.Fatalf("expected endpoints should match endpoints from cache: expected %v, but got %v", expectedCacheAfterDelete, registry.recordsCache) @@ -1075,11 +1141,40 @@ func TestDropSuffix(t *testing.T) { assert.Equal(t, expectedARecord, actualARecord) } -func TestDropRecordType(t *testing.T) { - r := "ns-zone.example.com" - expectedRecord := "zone.example.com" - actualRecord := dropRecordType(r) - assert.Equal(t, expectedRecord, actualRecord) +func TestExtractRecordType(t *testing.T) { + tests := []struct { + input string + expectedName string + expectedType string + }{ + { + input: "ns-zone.example.com", + expectedName: "zone.example.com", + expectedType: "NS", + }, + { + input: "aaaa-zone.example.com", + expectedName: "zone.example.com", + expectedType: "AAAA", + }, + { + input: "ptr-zone.example.com", + expectedName: "ptr-zone.example.com", + expectedType: "", + }, + { + input: "zone.example.com", + expectedName: "zone.example.com", + expectedType: "", + }, + } + for _, tc := range tests { + t.Run(tc.input, func(t *testing.T) { + actualName, actualType := extractRecordType(tc.input) + assert.Equal(t, tc.expectedName, actualName) + assert.Equal(t, tc.expectedType, actualType) + }) + } } func TestNewTXTScheme(t *testing.T) { @@ -1185,6 +1280,25 @@ func TestGenerateTXT(t *testing.T) { assert.Equal(t, expectedTXT, gotTXT) } +func TestGenerateTXTForAAAA(t *testing.T) { + record := newEndpointWithOwner("foo.test-zone.example.org", "2001:DB8::1", endpoint.RecordTypeAAAA, "owner") + expectedTXT := []*endpoint.Endpoint{ + { + DNSName: "aaaa-foo.test-zone.example.org", + Targets: endpoint.Targets{"\"heritage=external-dns,external-dns/owner=owner\""}, + RecordType: endpoint.RecordTypeTXT, + Labels: map[string]string{ + endpoint.OwnedRecordLabelKey: "foo.test-zone.example.org", + }, + }, + } + p := inmemory.NewInMemoryProvider() + p.CreateZone(testZone) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}) + gotTXT := r.generateTXTRecord(record) + assert.Equal(t, expectedTXT, gotTXT) +} + func TestFailGenerateTXT(t *testing.T) { cnameRecord := &endpoint.Endpoint{ From 4c02b1c64878951d59b83387f5bc0bb984c7bb2f Mon Sep 17 00:00:00 2001 From: Sewci0 Date: Fri, 31 Mar 2023 11:20:02 +0100 Subject: [PATCH 073/117] Update common.go --- provider/azure/common.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/provider/azure/common.go b/provider/azure/common.go index 15ecf64cf..dc87bc3b3 100644 --- a/provider/azure/common.go +++ b/provider/azure/common.go @@ -24,10 +24,8 @@ func parseMxTarget[T dns.MxRecord | privatedns.MxRecord](mxTarget string) (T, er if err != nil { return T{}, fmt.Errorf("invalid preference specified") } - res := T{ + return T{ Preference: to.Int32Ptr(int32(preference)), Exchange: to.StringPtr(exchange), - } - - return res, nil + }, nil } From 761f0e94f62d25c7057a2d331b6748530f4c3d70 Mon Sep 17 00:00:00 2001 From: Seweryn Chlewicki Date: Fri, 31 Mar 2023 11:44:03 +0100 Subject: [PATCH 074/117] Add licence --- provider/azure/common.go | 16 ++++++++++++++++ provider/azure/common_test.go | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/provider/azure/common.go b/provider/azure/common.go index dc87bc3b3..5b5ee2c5e 100644 --- a/provider/azure/common.go +++ b/provider/azure/common.go @@ -1,3 +1,19 @@ +/* +Copyright 2017 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 azure import ( diff --git a/provider/azure/common_test.go b/provider/azure/common_test.go index b7f4d8dfd..1009594c2 100644 --- a/provider/azure/common_test.go +++ b/provider/azure/common_test.go @@ -1,3 +1,19 @@ +/* +Copyright 2017 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 azure import ( From b265cbf937781622613ea3b7168bc26fbd3e235e Mon Sep 17 00:00:00 2001 From: Seweryn Chlewicki Date: Fri, 31 Mar 2023 13:14:27 +0100 Subject: [PATCH 075/117] Add no lint --- provider/azure/common.go | 1 + 1 file changed, 1 insertion(+) diff --git a/provider/azure/common.go b/provider/azure/common.go index 5b5ee2c5e..10aa62d03 100644 --- a/provider/azure/common.go +++ b/provider/azure/common.go @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +// nolint:staticcheck package azure import ( From 574c1025ce1386d6bab90a987e4458e93f47b3f2 Mon Sep 17 00:00:00 2001 From: Seweryn Chlewicki Date: Fri, 31 Mar 2023 13:23:14 +0100 Subject: [PATCH 076/117] Fix whitespace --- provider/azure/common.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/provider/azure/common.go b/provider/azure/common.go index 10aa62d03..e6c45ece2 100644 --- a/provider/azure/common.go +++ b/provider/azure/common.go @@ -30,17 +30,16 @@ import ( // Helper function (shared with test code) func parseMxTarget[T dns.MxRecord | privatedns.MxRecord](mxTarget string) (T, error) { targetParts := strings.SplitN(mxTarget, " ", 2) - if len(targetParts) != 2 { return T{}, fmt.Errorf("mx target needs to be of form '10 example.com'") } preferenceRaw, exchange := targetParts[0], targetParts[1] preference, err := strconv.ParseInt(preferenceRaw, 10, 32) - if err != nil { return T{}, fmt.Errorf("invalid preference specified") } + return T{ Preference: to.Int32Ptr(int32(preference)), Exchange: to.StringPtr(exchange), From 4c546fb73a4cf526e3913b996091043054ccec48 Mon Sep 17 00:00:00 2001 From: Maurice Meyer Date: Fri, 31 Mar 2023 17:34:20 +0200 Subject: [PATCH 077/117] docs: add example for provider specific config --- docs/contributing/crd-source/dnsendpoint-example.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/contributing/crd-source/dnsendpoint-example.yaml b/docs/contributing/crd-source/dnsendpoint-example.yaml index 894301d8a..2ed7d7fa0 100644 --- a/docs/contributing/crd-source/dnsendpoint-example.yaml +++ b/docs/contributing/crd-source/dnsendpoint-example.yaml @@ -9,3 +9,7 @@ spec: recordType: A targets: - 192.168.99.216 + # Provider specific configurations are set like an annotation would on other sources + providerSpecific: + - name: external-dns.alpha.kubernetes.io/cloudflare-proxied + value: "true" From 781ee3d71f7e99c44745a93d66b16dd12a744724 Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Wed, 12 Apr 2023 22:46:39 -0700 Subject: [PATCH 078/117] Address review comments --- registry/txt.go | 4 ++-- source/service.go | 9 ++++----- source/source.go | 14 ++++++-------- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/registry/txt.go b/registry/txt.go index d423aaac7..48dd028d5 100644 --- a/registry/txt.go +++ b/registry/txt.go @@ -378,13 +378,13 @@ func (pr affixNameMapper) toEndpointName(txtDNSName string) (endpointName string // drop prefix if strings.HasPrefix(lowerDNSName, pr.prefix) && pr.isPrefix() { - return pr.dropAffix(lowerDNSName), recordType == "AAAA" + return pr.dropAffix(lowerDNSName), recordType == endpoint.RecordTypeAAAA } // drop suffix if pr.isSuffix() { DNSName := strings.SplitN(lowerDNSName, ".", 2) - return pr.dropAffix(DNSName[0]) + "." + DNSName[1], recordType == "AAAA" + return pr.dropAffix(DNSName[0]) + "." + DNSName[1], recordType == endpoint.RecordTypeAAAA } return "", false } diff --git a/source/service.go b/source/service.go index aa8311f39..4355cab37 100644 --- a/source/service.go +++ b/source/service.go @@ -502,13 +502,12 @@ func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, pro } for _, t := range targets { - if suitableType(t) == endpoint.RecordTypeA { + switch suitableType(t) { + case endpoint.RecordTypeA: epA.Targets = append(epA.Targets, t) - } - if suitableType(t) == endpoint.RecordTypeAAAA { + case endpoint.RecordTypeAAAA: epAAAA.Targets = append(epAAAA.Targets, t) - } - if suitableType(t) == endpoint.RecordTypeCNAME { + case endpoint.RecordTypeCNAME: epCNAME.Targets = append(epCNAME.Targets, t) } } diff --git a/source/source.go b/source/source.go index 5aceea763..8573772c3 100644 --- a/source/source.go +++ b/source/source.go @@ -258,17 +258,15 @@ func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoin for _, t := range targets { switch suitableType(t) { case endpoint.RecordTypeA: - if !isIPv6String(t) { - aTargets = append(aTargets, t) - } else { - continue - } - case endpoint.RecordTypeAAAA: if isIPv6String(t) { - aaaaTargets = append(aaaaTargets, t) - } else { continue } + aTargets = append(aTargets, t) + case endpoint.RecordTypeAAAA: + if !isIPv6String(t) { + continue + } + aaaaTargets = append(aaaaTargets, t) default: cnameTargets = append(cnameTargets, t) } From 50b5268622737b80703b04fd95f84b9a27a4c726 Mon Sep 17 00:00:00 2001 From: Piotr Kowalczyk Date: Tue, 11 Apr 2023 11:45:59 +0200 Subject: [PATCH 079/117] Adding missing gateway-httproute cluster role permission Signed-off-by: Piotr Kowalczyk --- charts/external-dns/templates/clusterrole.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/charts/external-dns/templates/clusterrole.yaml b/charts/external-dns/templates/clusterrole.yaml index 177a7b6e0..4f33c1146 100644 --- a/charts/external-dns/templates/clusterrole.yaml +++ b/charts/external-dns/templates/clusterrole.yaml @@ -70,6 +70,11 @@ rules: resources: ["httproutes"] verbs: ["get","watch","list"] {{- end }} +{{- if has "gateway-httproute" .Values.sources }} + - apiGroups: [""] + resources: ["namespaces"] + verbs: ["get","watch","list"] +{{- end }} {{- if has "gateway-grpcroute" .Values.sources }} - apiGroups: ["gateway.networking.k8s.io"] resources: ["grpcroutes"] From ba26db45ce790c3524e351be96b99ddaebaa8059 Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Thu, 13 Apr 2023 17:09:56 -0700 Subject: [PATCH 080/117] Simplify planner's data structure --- plan/plan.go | 83 +++++++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 43 deletions(-) diff --git a/plan/plan.go b/plan/plan.go index 9eefeb207..1e8a38f1e 100644 --- a/plan/plan.go +++ b/plan/plan.go @@ -64,8 +64,15 @@ type Changes struct { Delete []*endpoint.Endpoint } +// planKey is a key for a row in `planTable`. +type planKey struct { + dnsName string + setIdentifier string + recordType string +} + // planTable is a supplementary struct for Plan -// each row correspond to a dnsName -> (current record + all desired records) +// each row correspond to a planKey -> (current record + all desired records) /* planTable: (-> = target) -------------------------------------------------------- @@ -78,12 +85,12 @@ bar.com | | [->191.1.1.1, ->190.1.1.1] | = create (bar.com -> 1 "=", i.e. result of calculation relies on supplied ConflictResolver */ type planTable struct { - rows map[string]map[string]map[string]*planTableRow + rows map[planKey]*planTableRow resolver ConflictResolver } func newPlanTable() planTable { // TODO: make resolver configurable - return planTable{map[string]map[string]map[string]*planTableRow{}, PerResource{}} + return planTable{map[planKey]*planTableRow{}, PerResource{}} } // planTableRow @@ -99,31 +106,25 @@ func (t planTableRow) String() string { } func (t planTable) addCurrent(e *endpoint.Endpoint) { - dnsName := normalizeDNSName(e.DNSName) - if _, ok := t.rows[dnsName]; !ok { - t.rows[dnsName] = make(map[string]map[string]*planTableRow) - } - if _, ok := t.rows[dnsName][e.SetIdentifier]; !ok { - t.rows[dnsName][e.SetIdentifier] = make(map[string]*planTableRow) - } - if _, ok := t.rows[e.SetIdentifier][e.RecordType]; !ok { - t.rows[dnsName][e.SetIdentifier][e.RecordType] = &planTableRow{} - } - t.rows[dnsName][e.SetIdentifier][e.RecordType].current = e + key := t.newPlanKey(e) + t.rows[key].current = e } func (t planTable) addCandidate(e *endpoint.Endpoint) { - dnsName := normalizeDNSName(e.DNSName) - if _, ok := t.rows[dnsName]; !ok { - t.rows[dnsName] = make(map[string]map[string]*planTableRow) + key := t.newPlanKey(e) + t.rows[key].candidates = append(t.rows[key].candidates, e) +} + +func (t *planTable) newPlanKey(e *endpoint.Endpoint) planKey { + key := planKey{ + dnsName: normalizeDNSName(e.DNSName), + setIdentifier: e.SetIdentifier, + recordType: e.RecordType, } - if _, ok := t.rows[dnsName][e.SetIdentifier]; !ok { - t.rows[dnsName][e.SetIdentifier] = make(map[string]*planTableRow) + if _, ok := t.rows[key]; !ok { + t.rows[key] = &planTableRow{} } - if _, ok := t.rows[dnsName][e.SetIdentifier][e.RecordType]; !ok { - t.rows[dnsName][e.SetIdentifier][e.RecordType] = &planTableRow{} - } - t.rows[dnsName][e.SetIdentifier][e.RecordType].candidates = append(t.rows[dnsName][e.SetIdentifier][e.RecordType].candidates, e) + return key } func (c *Changes) HasChanges() bool { @@ -152,28 +153,24 @@ func (p *Plan) Calculate() *Plan { changes := &Changes{} - for _, topRow := range t.rows { - for _, midRow := range topRow { - for _, row := range midRow { - if row.current == nil { // dns name not taken - changes.Create = append(changes.Create, t.resolver.ResolveCreate(row.candidates)) - } - if row.current != nil && len(row.candidates) == 0 { - changes.Delete = append(changes.Delete, row.current) - } + for _, row := range t.rows { + if row.current == nil { // dns name not taken + changes.Create = append(changes.Create, t.resolver.ResolveCreate(row.candidates)) + } + if row.current != nil && len(row.candidates) == 0 { + changes.Delete = append(changes.Delete, row.current) + } - // TODO: allows record type change, which might not be supported by all dns providers - if row.current != nil && len(row.candidates) > 0 { // dns name is taken - update := t.resolver.ResolveUpdate(row.current, row.candidates) - // compare "update" to "current" to figure out if actual update is required - if shouldUpdateTTL(update, row.current) || targetChanged(update, row.current) || p.shouldUpdateProviderSpecific(update, row.current) { - inheritOwner(row.current, update) - changes.UpdateNew = append(changes.UpdateNew, update) - changes.UpdateOld = append(changes.UpdateOld, row.current) - } - continue - } + // TODO: allows record type change, which might not be supported by all dns providers + if row.current != nil && len(row.candidates) > 0 { // dns name is taken + update := t.resolver.ResolveUpdate(row.current, row.candidates) + // compare "update" to "current" to figure out if actual update is required + if shouldUpdateTTL(update, row.current) || targetChanged(update, row.current) || p.shouldUpdateProviderSpecific(update, row.current) { + inheritOwner(row.current, update) + changes.UpdateNew = append(changes.UpdateNew, update) + changes.UpdateOld = append(changes.UpdateOld, row.current) } + continue } } for _, pol := range p.Policies { From d1ba5067b891f53de28348e6e30ab0783460f750 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Apr 2023 04:02:12 +0000 Subject: [PATCH 081/117] build(deps): bump go.etcd.io/etcd/client/v3 from 3.5.5 to 3.5.8 Bumps [go.etcd.io/etcd/client/v3](https://github.com/etcd-io/etcd) from 3.5.5 to 3.5.8. - [Release notes](https://github.com/etcd-io/etcd/releases) - [Changelog](https://github.com/etcd-io/etcd/blob/main/Dockerfile-release.ppc64le) - [Commits](https://github.com/etcd-io/etcd/compare/v3.5.5...v3.5.8) --- updated-dependencies: - dependency-name: go.etcd.io/etcd/client/v3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 33 ++++++--------------------------- 2 files changed, 9 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index de66742d3..51690a4d5 100644 --- a/go.mod +++ b/go.mod @@ -58,8 +58,8 @@ require ( github.com/ultradns/ultradns-sdk-go v0.0.0-20200616202852-e62052662f60 github.com/vinyldns/go-vinyldns v0.0.0-20200211145900-fe8a3d82e556 github.com/vultr/govultr/v2 v2.17.2 - go.etcd.io/etcd/api/v3 v3.5.5 - go.etcd.io/etcd/client/v3 v3.5.5 + go.etcd.io/etcd/api/v3 v3.5.8 + go.etcd.io/etcd/client/v3 v3.5.8 go.uber.org/ratelimit v0.2.0 golang.org/x/net v0.7.0 golang.org/x/oauth2 v0.5.0 @@ -168,7 +168,7 @@ require ( github.com/stretchr/objx v0.5.0 // indirect github.com/terra-farm/udnssdk v1.3.5 // indirect github.com/vektah/gqlparser/v2 v2.5.0 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.5.5 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.8 // indirect go.mongodb.org/mongo-driver v1.5.1 // indirect go.opencensus.io v0.24.0 // indirect go.uber.org/atomic v1.9.0 // indirect diff --git a/go.sum b/go.sum index 8786ee822..cff0de572 100644 --- a/go.sum +++ b/go.sum @@ -153,7 +153,6 @@ github.com/ans-group/go-durationstring v1.2.0 h1:UJIuQATkp0t1rBvZsHRwki33YHV9E+U github.com/ans-group/go-durationstring v1.2.0/go.mod h1:QGF9Mdpq9058QXaut8r55QWu6lcHX6i/GvF1PZVkV6o= github.com/ans-group/sdk-go v1.10.4 h1:wZzojt99wtVIEHs8zNQzp1Xhqme5tD5NqMM1VLmG6xQ= github.com/ans-group/sdk-go v1.10.4/go.mod h1:XSKXEDfKobnDtZoyia5DhJxxaDMcCjr76e1KJ9dU/xc= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/aokoli/goutils v1.1.0/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -218,8 +217,6 @@ github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381 github.com/cncf/udpa v0.0.0-20200324003616-bae28a880fdb/go.mod h1:HNVadOiXCy7Jk3R2knJ+qm++zkncJxxBMpjdGgJ+UJc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200324003616-bae28a880fdb/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= @@ -314,9 +311,6 @@ github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4s github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.3.0-java.0.20200609174644-bd816e4522c1/go.mod h1:bjmEhrMDubXDd0uKxnWwRmgSsiEv2CkJliIHnj6ETm8= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -623,7 +617,6 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= @@ -995,7 +988,6 @@ github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeD github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= @@ -1039,7 +1031,6 @@ github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqn github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -1201,12 +1192,12 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= -go.etcd.io/etcd/api/v3 v3.5.5 h1:BX4JIbQ7hl7+jL+g+2j5UAr0o1bctCm6/Ct+ArBGkf0= -go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= -go.etcd.io/etcd/client/pkg/v3 v3.5.5 h1:9S0JUVvmrVl7wCF39iTQthdaaNIiAaQbmK75ogO6GU8= -go.etcd.io/etcd/client/pkg/v3 v3.5.5/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ= -go.etcd.io/etcd/client/v3 v3.5.5 h1:q++2WTJbUgpQu4B6hCuT7VkdwaTP7Qz6Daak3WzbrlI= -go.etcd.io/etcd/client/v3 v3.5.5/go.mod h1:aApjR4WGlSumpnJ2kloS75h6aHUmAyaPLjHMxpc7E7c= +go.etcd.io/etcd/api/v3 v3.5.8 h1:Zf44zJszoU7zRV0X/nStPenegNXoFDWcB/MwrJbA+L4= +go.etcd.io/etcd/api/v3 v3.5.8/go.mod h1:uyAal843mC8uUVSLWz6eHa/d971iDGnCRpmKd2Z+X8k= +go.etcd.io/etcd/client/pkg/v3 v3.5.8 h1:tPp9YRn/UBFAHdhOQUII9eUs7aOK35eulpMhX4YBd+M= +go.etcd.io/etcd/client/pkg/v3 v3.5.8/go.mod h1:y+CzeSmkMpWN2Jyu1npecjB9BBnABxGM4pN8cGuJeL4= +go.etcd.io/etcd/client/v3 v3.5.8 h1:B6ngTKZSWWowHEoaucOKHQR/AtZKaoHLiUpWxOLG4l4= +go.etcd.io/etcd/client/v3 v3.5.8/go.mod h1:idZYIPVkttBJBiRigkB5EM0MmEyx8jcl18zCV3F5noc= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= @@ -1221,7 +1212,6 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1239,7 +1229,6 @@ go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6m go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1298,7 +1287,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -1467,7 +1455,6 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1587,7 +1574,6 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= @@ -1651,7 +1637,6 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= @@ -1660,7 +1645,6 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc h1:ijGwO+0vL2hJt5gaygqP2j6PfflOBrRot0IczKbmtio= google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -1682,11 +1666,7 @@ google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -1745,7 +1725,6 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From 4447b2a810d6d94d563a27fa61399d9dd8bf0d9a Mon Sep 17 00:00:00 2001 From: Charles Xu Date: Tue, 18 Apr 2023 15:11:08 -0700 Subject: [PATCH 082/117] cloudflare: support reading API token from file --- docs/tutorials/cloudflare.md | 2 ++ provider/cloudflare/cloudflare.go | 12 +++++++++++- provider/cloudflare/cloudflare_test.go | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/docs/tutorials/cloudflare.md b/docs/tutorials/cloudflare.md index 211dbf72c..79deffeff 100644 --- a/docs/tutorials/cloudflare.md +++ b/docs/tutorials/cloudflare.md @@ -20,6 +20,8 @@ Snippet from [Cloudflare - Getting Started](https://api.cloudflare.com/#getting- API Token will be preferred for authentication if `CF_API_TOKEN` environment variable is set. Otherwise `CF_API_KEY` and `CF_API_EMAIL` should be set to run ExternalDNS with Cloudflare. +You may provide the Cloudflare API token through a file by setting the +`CF_API_TOKEN="file:/path/to/token"`. When using API Token authentication, the token should be granted Zone `Read`, DNS `Edit` privileges, and access to `All zones`. diff --git a/provider/cloudflare/cloudflare.go b/provider/cloudflare/cloudflare.go index 0f7bdf0cc..3bfc12c0c 100644 --- a/provider/cloudflare/cloudflare.go +++ b/provider/cloudflare/cloudflare.go @@ -19,8 +19,10 @@ package cloudflare import ( "context" "fmt" + "io/ioutil" "os" "strconv" + "strings" cloudflare "github.com/cloudflare/cloudflare-go" log "github.com/sirupsen/logrus" @@ -155,7 +157,15 @@ func NewCloudFlareProvider(domainFilter endpoint.DomainFilter, zoneIDFilter prov err error ) if os.Getenv("CF_API_TOKEN") != "" { - config, err = cloudflare.NewWithAPIToken(os.Getenv("CF_API_TOKEN")) + token := os.Getenv("CF_API_TOKEN") + if strings.HasPrefix(token, "file:") { + tokenBytes, err := ioutil.ReadFile(strings.TrimPrefix(token, "file:")) + if err != nil { + return nil, fmt.Errorf("failed to read CF_API_TOKEN from file: %v", err) + } + token = string(tokenBytes) + } + config, err = cloudflare.NewWithAPIToken(token) } else { config, err = cloudflare.New(os.Getenv("CF_API_KEY"), os.Getenv("CF_API_EMAIL")) } diff --git a/provider/cloudflare/cloudflare_test.go b/provider/cloudflare/cloudflare_test.go index 3755c8c9b..6d1d47b40 100644 --- a/provider/cloudflare/cloudflare_test.go +++ b/provider/cloudflare/cloudflare_test.go @@ -677,6 +677,23 @@ func TestCloudflareProvider(t *testing.T) { if err != nil { t.Errorf("should not fail, %s", err) } + + _ = os.Unsetenv("CF_API_TOKEN") + tokenFile := "/tmp/cf_api_token" + if err := os.WriteFile(tokenFile, []byte("abc123def"), 0644); err != nil { + t.Errorf("failed to write token file, %s", err) + } + _ = os.Setenv("CF_API_TOKEN", tokenFile) + _, err = NewCloudFlareProvider( + endpoint.NewDomainFilter([]string{"bar.com"}), + provider.NewZoneIDFilter([]string{""}), + false, + true, + 5000) + if err != nil { + t.Errorf("should not fail, %s", err) + } + _ = os.Unsetenv("CF_API_TOKEN") _ = os.Setenv("CF_API_KEY", "xxxxxxxxxxxxxxxxx") _ = os.Setenv("CF_API_EMAIL", "test@test.com") @@ -689,6 +706,7 @@ func TestCloudflareProvider(t *testing.T) { if err != nil { t.Errorf("should not fail, %s", err) } + _ = os.Unsetenv("CF_API_KEY") _ = os.Unsetenv("CF_API_EMAIL") _, err = NewCloudFlareProvider( From 1d232c4b8639e5efe0ae29b288f554d73ed21167 Mon Sep 17 00:00:00 2001 From: Charles Xu Date: Tue, 18 Apr 2023 21:09:59 -0700 Subject: [PATCH 083/117] feat: resolve LB-type Service hostname to create A/AAAA instead of CNAME --- charts/external-dns/README.md | 5 +- charts/external-dns/templates/deployment.yaml | 3 + charts/external-dns/values.yaml | 2 + main.go | 1 + pkg/apis/externaldns/types.go | 2 + source/service.go | 22 ++++-- source/service_test.go | 67 +++++++++++++------ source/store.go | 3 +- 8 files changed, 79 insertions(+), 26 deletions(-) diff --git a/charts/external-dns/README.md b/charts/external-dns/README.md index bfdb56deb..f78e68c80 100644 --- a/charts/external-dns/README.md +++ b/charts/external-dns/README.md @@ -83,16 +83,17 @@ The following table lists the configurable parameters of the _ExternalDNS_ chart | `secretConfiguration.mountPath` | Mount path of secret configuration secret (this can be templated). | `""` | | `secretConfiguration.data` | Secret configuration secret data. Could be used to store DNS provider credentials. | `{}` | | `secretConfiguration.subPath` | Sub-path of secret configuration secret (this can be templated). | `""` | +| `resolveLoadBalancerHostname` | Resolve the hostname of LoadBalancer-type Service object to IP addresses in order to create DNS A/AAAA records instead of CNAMEs | `false` | ## Namespaced scoped installation -external-dns supports running on a namespaced only scope, too. +external-dns supports running on a namespaced only scope, too. If `namespaced=true` is defined, the helm chart will setup `Roles` and `RoleBindings` instead `ClusterRoles` and `ClusterRoleBindings`. ### Limited supported Not all sources are supported in namespaced scope, since some sources depends on cluster-wide resources. For example: Source `node` isn't supported, since `kind: Node` has scope `Cluster`. -Sources like `istio-virtualservice` only work, if all resources like `Gateway` and `VirtualService` are present in the same +Sources like `istio-virtualservice` only work, if all resources like `Gateway` and `VirtualService` are present in the same namespaces as `external-dns`. The annotation `external-dns.alpha.kubernetes.io/endpoints-type: NodeExternalIP` is not supported. diff --git a/charts/external-dns/templates/deployment.yaml b/charts/external-dns/templates/deployment.yaml index 5c3e1128f..41586e092 100644 --- a/charts/external-dns/templates/deployment.yaml +++ b/charts/external-dns/templates/deployment.yaml @@ -96,6 +96,9 @@ spec: - --domain-filter={{ . }} {{- end }} - --provider={{ tpl .Values.provider $ }} + {{- if .Values.resolveLoadBalancerHostname }} + - --resolve-load-balancer-hostname + {{- end }} {{- range .Values.extraArgs }} - {{ tpl . $ }} {{- end }} diff --git a/charts/external-dns/values.yaml b/charts/external-dns/values.yaml index 6e0f80265..f1989f54b 100644 --- a/charts/external-dns/values.yaml +++ b/charts/external-dns/values.yaml @@ -15,6 +15,8 @@ fullnameOverride: "" commonLabels: {} +resolveLoadBalancerHostname: false + serviceAccount: # Specifies whether a service account should be created create: true diff --git a/main.go b/main.go index c408c37ee..880affcbc 100644 --- a/main.go +++ b/main.go @@ -141,6 +141,7 @@ func main() { DefaultTargets: cfg.DefaultTargets, OCPRouterName: cfg.OCPRouterName, UpdateEvents: cfg.UpdateEvents, + ResolveLoadBalancerHostname: cfg.ResolveLoadBalancerHostname, } // Lookup all the selected sources by names and pass them the desired configuration. diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 9b6c4d3e9..f0a4d015f 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -166,6 +166,7 @@ type Config struct { CFAPIEndpoint string CFUsername string CFPassword string + ResolveLoadBalancerHostname bool RFC2136Host string RFC2136Port int RFC2136Zone string @@ -390,6 +391,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("server", "The Kubernetes API server to connect to (default: auto-detect)").Default(defaultConfig.APIServerURL).StringVar(&cfg.APIServerURL) app.Flag("kubeconfig", "Retrieve target cluster configuration from a Kubernetes configuration file (default: auto-detect)").Default(defaultConfig.KubeConfig).StringVar(&cfg.KubeConfig) app.Flag("request-timeout", "Request timeout when calling Kubernetes APIs. 0s means no timeout").Default(defaultConfig.RequestTimeout.String()).DurationVar(&cfg.RequestTimeout) + app.Flag("resolve-lb-hostname", "Resolve the hostname of LoadBalancer-type Service object to IP addresses in order to create DNS A/AAAA records instead of CNAMEs").BoolVar(&cfg.ResolveLoadBalancerHostname) // Flags related to cloud foundry app.Flag("cf-api-endpoint", "The fully-qualified domain name of the cloud foundry instance you are targeting").Default(defaultConfig.CFAPIEndpoint).StringVar(&cfg.CFAPIEndpoint) diff --git a/source/service.go b/source/service.go index 4355cab37..925c327f2 100644 --- a/source/service.go +++ b/source/service.go @@ -19,6 +19,7 @@ package source import ( "context" "fmt" + "net" "sort" "strings" "text/template" @@ -57,6 +58,7 @@ type serviceSource struct { publishInternal bool publishHostIP bool alwaysPublishNotReadyAddresses bool + resolveLoadBalancerHostname bool serviceInformer coreinformers.ServiceInformer endpointsInformer coreinformers.EndpointsInformer podInformer coreinformers.PodInformer @@ -66,7 +68,7 @@ type serviceSource struct { } // NewServiceSource creates a new serviceSource with the given config. -func NewServiceSource(ctx context.Context, kubeClient kubernetes.Interface, namespace, annotationFilter string, fqdnTemplate string, combineFqdnAnnotation bool, compatibility string, publishInternal bool, publishHostIP bool, alwaysPublishNotReadyAddresses bool, serviceTypeFilter []string, ignoreHostnameAnnotation bool, labelSelector labels.Selector) (Source, error) { +func NewServiceSource(ctx context.Context, kubeClient kubernetes.Interface, namespace, annotationFilter string, fqdnTemplate string, combineFqdnAnnotation bool, compatibility string, publishInternal bool, publishHostIP bool, alwaysPublishNotReadyAddresses bool, serviceTypeFilter []string, ignoreHostnameAnnotation bool, labelSelector labels.Selector, resolveLoadBalancerHostname bool) (Source, error) { tmpl, err := parseTemplate(fqdnTemplate) if err != nil { return nil, err @@ -137,6 +139,7 @@ func NewServiceSource(ctx context.Context, kubeClient kubernetes.Interface, name nodeInformer: nodeInformer, serviceTypeFilter: serviceTypes, labelSelector: labelSelector, + resolveLoadBalancerHostname: resolveLoadBalancerHostname, }, nil } @@ -480,7 +483,7 @@ func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, pro if useClusterIP { targets = append(targets, extractServiceIps(svc)...) } else { - targets = append(targets, extractLoadBalancerTargets(svc)...) + targets = append(targets, extractLoadBalancerTargets(svc, sc.resolveLoadBalancerHostname)...) } case v1.ServiceTypeClusterIP: if sc.publishInternal { @@ -540,7 +543,7 @@ func extractServiceExternalName(svc *v1.Service) endpoint.Targets { return endpoint.Targets{svc.Spec.ExternalName} } -func extractLoadBalancerTargets(svc *v1.Service) endpoint.Targets { +func extractLoadBalancerTargets(svc *v1.Service, resolveLoadBalancerHostname bool) endpoint.Targets { var ( targets endpoint.Targets externalIPs endpoint.Targets @@ -552,7 +555,18 @@ func extractLoadBalancerTargets(svc *v1.Service) endpoint.Targets { targets = append(targets, lb.IP) } if lb.Hostname != "" { - targets = append(targets, lb.Hostname) + if resolveLoadBalancerHostname { + ips, err := net.LookupIP(lb.Hostname) + if err != nil { + log.Errorf("Unable to resolve %q: %v", lb.Hostname, err) + continue + } + for _, ip := range ips { + targets = append(targets, ip.String()) + } + } else { + targets = append(targets, lb.Hostname) + } } } diff --git a/source/service_test.go b/source/service_test.go index e04d38024..98b400fca 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -78,6 +78,7 @@ func (suite *ServiceSuite) SetupTest() { []string{}, false, labels.Everything(), + false, ) suite.NoError(err, "should initialize service source") } @@ -158,6 +159,7 @@ func testServiceSourceNewServiceSource(t *testing.T) { ti.serviceTypesFilter, false, labels.Everything(), + false, ) if ti.expectError { @@ -174,25 +176,26 @@ func testServiceSourceEndpoints(t *testing.T) { t.Parallel() for _, tc := range []struct { - title string - targetNamespace string - annotationFilter string - svcNamespace string - svcName string - svcType v1.ServiceType - compatibility string - fqdnTemplate string - combineFQDNAndAnnotation bool - ignoreHostnameAnnotation bool - labels map[string]string - annotations map[string]string - clusterIP string - externalIPs []string - lbs []string - serviceTypesFilter []string - expected []*endpoint.Endpoint - expectError bool - serviceLabelSelector string + title string + targetNamespace string + annotationFilter string + svcNamespace string + svcName string + svcType v1.ServiceType + compatibility string + fqdnTemplate string + combineFQDNAndAnnotation bool + ignoreHostnameAnnotation bool + labels map[string]string + annotations map[string]string + clusterIP string + externalIPs []string + lbs []string + serviceTypesFilter []string + expected []*endpoint.Endpoint + expectError bool + serviceLabelSelector string + resolveLoadBalancerHostname bool }{ { title: "no annotated services return no endpoints", @@ -389,6 +392,24 @@ func testServiceSourceEndpoints(t *testing.T) { {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"lb.example.com"}}, }, }, + { + title: "annotated services return an endpoint with hostname then resolve hostname", + svcNamespace: "testing", + svcName: "foo", + svcType: v1.ServiceTypeLoadBalancer, + labels: map[string]string{}, + annotations: map[string]string{ + hostnameAnnotationKey: "foo.example.org.", + }, + externalIPs: []string{}, + lbs: []string{"example.com"}, // Use a resolvable hostname for testing. + serviceTypesFilter: []string{}, + resolveLoadBalancerHostname: true, + expected: []*endpoint.Endpoint{ + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"93.184.216.34"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2606:2800:220:1:248:1893:25c8:1946"}}, + }, + }, { title: "annotated services can omit trailing dot", svcNamespace: "testing", @@ -1086,6 +1107,7 @@ func testServiceSourceEndpoints(t *testing.T) { tc.serviceTypesFilter, tc.ignoreHostnameAnnotation, sourceLabel, + tc.resolveLoadBalancerHostname, ) require.NoError(t, err) @@ -1275,6 +1297,7 @@ func testMultipleServicesEndpoints(t *testing.T) { tc.serviceTypesFilter, tc.ignoreHostnameAnnotation, labels.Everything(), + false, ) require.NoError(t, err) @@ -1440,6 +1463,7 @@ func TestClusterIpServices(t *testing.T) { []string{}, tc.ignoreHostnameAnnotation, labelSelector, + false, ) require.NoError(t, err) @@ -2010,6 +2034,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { []string{}, tc.ignoreHostnameAnnotation, labels.Everything(), + false, ) require.NoError(t, err) @@ -2483,6 +2508,7 @@ func TestHeadlessServices(t *testing.T) { []string{}, tc.ignoreHostnameAnnotation, labels.Everything(), + false, ) require.NoError(t, err) @@ -2840,6 +2866,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { []string{}, tc.ignoreHostnameAnnotation, labels.Everything(), + false, ) require.NoError(t, err) @@ -2952,6 +2979,7 @@ func TestExternalServices(t *testing.T) { []string{}, tc.ignoreHostnameAnnotation, labels.Everything(), + false, ) require.NoError(t, err) @@ -3006,6 +3034,7 @@ func BenchmarkServiceEndpoints(b *testing.B) { []string{}, false, labels.Everything(), + false, ) require.NoError(b, err) diff --git a/source/store.go b/source/store.go index 2df28cf41..d22e16ff8 100644 --- a/source/store.go +++ b/source/store.go @@ -73,6 +73,7 @@ type Config struct { DefaultTargets []string OCPRouterName string UpdateEvents bool + ResolveLoadBalancerHostname bool } // ClientGenerator provides clients @@ -215,7 +216,7 @@ func BuildWithConfig(ctx context.Context, source string, p ClientGenerator, cfg if err != nil { return nil, err } - return NewServiceSource(ctx, client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.Compatibility, cfg.PublishInternal, cfg.PublishHostIP, cfg.AlwaysPublishNotReadyAddresses, cfg.ServiceTypeFilter, cfg.IgnoreHostnameAnnotation, cfg.LabelFilter) + return NewServiceSource(ctx, client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.Compatibility, cfg.PublishInternal, cfg.PublishHostIP, cfg.AlwaysPublishNotReadyAddresses, cfg.ServiceTypeFilter, cfg.IgnoreHostnameAnnotation, cfg.LabelFilter, cfg.ResolveLoadBalancerHostname) case "ingress": client, err := p.KubeClient() if err != nil { From bdcdcd1e25f4d5e65b9cc0769c025bf1e8779054 Mon Sep 17 00:00:00 2001 From: "Eric R. Rath" Date: Thu, 20 Apr 2023 10:20:33 -0700 Subject: [PATCH 084/117] Fix incorrect arg in Oracle OCI tutorial The recently-added support for Oracle's OCI IAM instance principal authentication included an incorrect arg name in the change to the Oracle tutorial. This changes the tutorial to specify the correct arg name. Thanks to joe.rosinski@oracle.com for spotting the problem. --- docs/tutorials/oracle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/oracle.md b/docs/tutorials/oracle.md index 2a204697e..d7d385839 100644 --- a/docs/tutorials/oracle.md +++ b/docs/tutorials/oracle.md @@ -60,7 +60,7 @@ E.g.: Allow dynamic-group to manage dns in compartment id ``` -You'll also need to add the `--oci-instance-principals=true` flag to enable +You'll also need to add the `--oci-auth-instance-principal` flag to enable this type of authentication. Finally, you'll need to add the `--oci-compartment-ocid=ocid1.compartment.oc1...` flag to provide the OCID of the compartment containing the zone to be managed. From 59ebd2b540992b22125b92893cea1c5ea8406ef5 Mon Sep 17 00:00:00 2001 From: jrosinsk Date: Sun, 16 Apr 2023 19:26:22 -0700 Subject: [PATCH 085/117] OCI provider: bump the oci-go-sdk from v24.3.0 to v65.35.0 --- go.mod | 6 ++++-- go.sum | 12 ++++++++---- provider/oci/oci.go | 6 +++--- provider/oci/oci_test.go | 4 ++-- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 51690a4d5..d7b624ae4 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,7 @@ require ( github.com/onsi/ginkgo v1.16.5 github.com/openshift/api v0.0.0-20210315202829-4b79815405ec github.com/openshift/client-go v0.0.0-20210112165513-ebc401615f47 - github.com/oracle/oci-go-sdk v24.3.0+incompatible + github.com/oracle/oci-go-sdk/v65 v65.35.0 github.com/ovh/go-ovh v1.1.0 github.com/pkg/errors v0.9.1 github.com/pluralsh/gqlclient v1.1.6 @@ -113,6 +113,7 @@ require ( github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 // indirect github.com/go-stack/stack v1.8.0 // indirect + github.com/gofrs/flock v0.8.1 // indirect github.com/gofrs/uuid v4.0.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.4.2 // indirect @@ -164,6 +165,7 @@ require ( github.com/schollz/progressbar/v3 v3.8.6 // indirect github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect github.com/smartystreets/gunit v1.3.4 // indirect + github.com/sony/gobreaker v0.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/terra-farm/udnssdk v1.3.5 // indirect @@ -176,7 +178,7 @@ require ( go.uber.org/zap v1.19.1 // indirect golang.org/x/crypto v0.5.0 // indirect golang.org/x/mod v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect + golang.org/x/sys v0.6.0 // indirect golang.org/x/term v0.5.0 // indirect golang.org/x/text v0.7.0 // indirect golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect diff --git a/go.sum b/go.sum index cff0de572..69d3d71f0 100644 --- a/go.sum +++ b/go.sum @@ -478,6 +478,8 @@ github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.13.3/go.mod h1:2ouUT4kdhUBk7TAkHWD4SN0CdI0pgEQbo8FVHhbSKWg= github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= @@ -943,8 +945,8 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/oracle/oci-go-sdk v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU= -github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= +github.com/oracle/oci-go-sdk/v65 v65.35.0 h1:zvDsEuGs0qf6hPZVbrDnnfPJYQP7CwAgidTr4Pch6E4= +github.com/oracle/oci-go-sdk/v65 v65.35.0/go.mod h1:MXMLMzHnnd9wlpgadPkdlkZ9YrwQmCOmbX5kjVEJodw= github.com/ovh/go-ovh v1.1.0 h1:bHXZmw8nTgZin4Nv7JuaLs0KG5x54EQR7migYTd1zrk= github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA= github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso= @@ -1080,6 +1082,8 @@ github.com/smartystreets/gunit v1.3.4 h1:iHc8Rfhb/uCOc9a3KGuD3ut22L+hLIVaqR1o5fS github.com/smartystreets/gunit v1.3.4/go.mod h1:ZjM1ozSIMJlAz/ay4SG8PeKF00ckUp+zMHZXV9/bvak= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= +github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= @@ -1474,8 +1478,8 @@ golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/provider/oci/oci.go b/provider/oci/oci.go index 06e9e8a3b..26d8f691f 100644 --- a/provider/oci/oci.go +++ b/provider/oci/oci.go @@ -21,9 +21,9 @@ import ( "os" "strings" - "github.com/oracle/oci-go-sdk/common" - "github.com/oracle/oci-go-sdk/common/auth" - "github.com/oracle/oci-go-sdk/dns" + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/common/auth" + "github.com/oracle/oci-go-sdk/v65/dns" "github.com/pkg/errors" log "github.com/sirupsen/logrus" yaml "gopkg.in/yaml.v2" diff --git a/provider/oci/oci_test.go b/provider/oci/oci_test.go index 33ca5c244..c11781765 100644 --- a/provider/oci/oci_test.go +++ b/provider/oci/oci_test.go @@ -22,8 +22,8 @@ import ( "strings" "testing" - "github.com/oracle/oci-go-sdk/common" - "github.com/oracle/oci-go-sdk/dns" + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/dns" "github.com/pkg/errors" "github.com/stretchr/testify/require" From 5e6f1a8b167255bf03ce2f521178d8ebf52202b5 Mon Sep 17 00:00:00 2001 From: Charles Xu Date: Sun, 23 Apr 2023 16:20:17 -0700 Subject: [PATCH 086/117] rename option to 'resolveServiceLoadBalancerHostname' --- charts/external-dns/README.md | 2 +- charts/external-dns/templates/deployment.yaml | 4 +- charts/external-dns/values.yaml | 2 +- main.go | 2 +- pkg/apis/externaldns/types.go | 320 +++++++++--------- 5 files changed, 165 insertions(+), 165 deletions(-) diff --git a/charts/external-dns/README.md b/charts/external-dns/README.md index f78e68c80..a0e543ed4 100644 --- a/charts/external-dns/README.md +++ b/charts/external-dns/README.md @@ -83,7 +83,7 @@ The following table lists the configurable parameters of the _ExternalDNS_ chart | `secretConfiguration.mountPath` | Mount path of secret configuration secret (this can be templated). | `""` | | `secretConfiguration.data` | Secret configuration secret data. Could be used to store DNS provider credentials. | `{}` | | `secretConfiguration.subPath` | Sub-path of secret configuration secret (this can be templated). | `""` | -| `resolveLoadBalancerHostname` | Resolve the hostname of LoadBalancer-type Service object to IP addresses in order to create DNS A/AAAA records instead of CNAMEs | `false` | +| `resolveServiceLoadBalancerHostname` | Resolve the hostname of LoadBalancer-type Service object to IP addresses in order to create DNS A/AAAA records instead of CNAMEs | `false` | ## Namespaced scoped installation diff --git a/charts/external-dns/templates/deployment.yaml b/charts/external-dns/templates/deployment.yaml index 41586e092..a2f0b74f9 100644 --- a/charts/external-dns/templates/deployment.yaml +++ b/charts/external-dns/templates/deployment.yaml @@ -96,8 +96,8 @@ spec: - --domain-filter={{ . }} {{- end }} - --provider={{ tpl .Values.provider $ }} - {{- if .Values.resolveLoadBalancerHostname }} - - --resolve-load-balancer-hostname + {{- if .Values.resolveServiceLoadBalancerHostname }} + - --resolve-service-load-balancer-hostname {{- end }} {{- range .Values.extraArgs }} - {{ tpl . $ }} diff --git a/charts/external-dns/values.yaml b/charts/external-dns/values.yaml index f1989f54b..1084cd2ac 100644 --- a/charts/external-dns/values.yaml +++ b/charts/external-dns/values.yaml @@ -15,7 +15,7 @@ fullnameOverride: "" commonLabels: {} -resolveLoadBalancerHostname: false +resolveServiceLoadBalancerHostname: false serviceAccount: # Specifies whether a service account should be created diff --git a/main.go b/main.go index 880affcbc..960045ce9 100644 --- a/main.go +++ b/main.go @@ -141,7 +141,7 @@ func main() { DefaultTargets: cfg.DefaultTargets, OCPRouterName: cfg.OCPRouterName, UpdateEvents: cfg.UpdateEvents, - ResolveLoadBalancerHostname: cfg.ResolveLoadBalancerHostname, + ResolveLoadBalancerHostname: cfg.ResolveServiceLoadBalancerHostname, } // Lookup all the selected sources by names and pass them the desired configuration. diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index f0a4d015f..eefd2b2bf 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -43,165 +43,165 @@ var Version = "unknown" // Config is a project-wide configuration type Config struct { - APIServerURL string - KubeConfig string - RequestTimeout time.Duration - DefaultTargets []string - ContourLoadBalancerService string - GlooNamespace string - SkipperRouteGroupVersion string - Sources []string - Namespace string - AnnotationFilter string - LabelFilter string - FQDNTemplate string - CombineFQDNAndAnnotation bool - IgnoreHostnameAnnotation bool - IgnoreIngressTLSSpec bool - IgnoreIngressRulesSpec bool - GatewayNamespace string - GatewayLabelFilter string - Compatibility string - PublishInternal bool - PublishHostIP bool - AlwaysPublishNotReadyAddresses bool - ConnectorSourceServer string - Provider string - GoogleProject string - GoogleBatchChangeSize int - GoogleBatchChangeInterval time.Duration - GoogleZoneVisibility string - DomainFilter []string - ExcludeDomains []string - RegexDomainFilter *regexp.Regexp - RegexDomainExclusion *regexp.Regexp - ZoneNameFilter []string - ZoneIDFilter []string - TargetNetFilter []string - ExcludeTargetNets []string - AlibabaCloudConfigFile string - AlibabaCloudZoneType string - AWSZoneType string - AWSZoneTagFilter []string - AWSAssumeRole string - AWSAssumeRoleExternalID string - AWSBatchChangeSize int - AWSBatchChangeInterval time.Duration - AWSEvaluateTargetHealth bool - AWSAPIRetries int - AWSPreferCNAME bool - AWSZoneCacheDuration time.Duration - AWSSDServiceCleanup bool - AzureConfigFile string - AzureResourceGroup string - AzureSubscriptionID string - AzureUserAssignedIdentityClientID string - BluecatDNSConfiguration string - BluecatConfigFile string - BluecatDNSView string - BluecatGatewayHost string - BluecatRootZone string - BluecatDNSServerName string - BluecatDNSDeployType string - BluecatSkipTLSVerify bool - CloudflareProxied bool - CloudflareDNSRecordsPerPage int - CoreDNSPrefix string - RcodezeroTXTEncrypt bool - AkamaiServiceConsumerDomain string - AkamaiClientToken string - AkamaiClientSecret string - AkamaiAccessToken string - AkamaiEdgercPath string - AkamaiEdgercSection string - InfobloxGridHost string - InfobloxWapiPort int - InfobloxWapiUsername string - InfobloxWapiPassword string `secure:"yes"` - InfobloxWapiVersion string - InfobloxSSLVerify bool - InfobloxView string - InfobloxMaxResults int - InfobloxFQDNRegEx string - InfobloxNameRegEx string - InfobloxCreatePTR bool - InfobloxCacheDuration int - DynCustomerName string - DynUsername string - DynPassword string `secure:"yes"` - DynMinTTLSeconds int - OCIConfigFile string - OCICompartmentOCID string - OCIAuthInstancePrincipal bool - InMemoryZones []string - OVHEndpoint string - OVHApiRateLimit int - PDNSServer string - PDNSAPIKey string `secure:"yes"` - PDNSTLSEnabled bool - TLSCA string - TLSClientCert string - TLSClientCertKey string - Policy string - Registry string - TXTOwnerID string - TXTPrefix string - TXTSuffix string - Interval time.Duration - MinEventSyncInterval time.Duration - Once bool - DryRun bool - UpdateEvents bool - LogFormat string - MetricsAddress string - LogLevel string - TXTCacheInterval time.Duration - TXTWildcardReplacement string - ExoscaleEndpoint string - ExoscaleAPIKey string `secure:"yes"` - ExoscaleAPISecret string `secure:"yes"` - CRDSourceAPIVersion string - CRDSourceKind string - ServiceTypeFilter []string - CFAPIEndpoint string - CFUsername string - CFPassword string - ResolveLoadBalancerHostname bool - RFC2136Host string - RFC2136Port int - RFC2136Zone string - RFC2136Insecure bool - RFC2136GSSTSIG bool - RFC2136KerberosRealm string - RFC2136KerberosUsername string - RFC2136KerberosPassword string `secure:"yes"` - RFC2136TSIGKeyName string - RFC2136TSIGSecret string `secure:"yes"` - RFC2136TSIGSecretAlg string - RFC2136TAXFR bool - RFC2136MinTTL time.Duration - RFC2136BatchChangeSize int - NS1Endpoint string - NS1IgnoreSSL bool - NS1MinTTLSeconds int - TransIPAccountName string - TransIPPrivateKeyFile string - DigitalOceanAPIPageSize int - ManagedDNSRecordTypes []string - GoDaddyAPIKey string `secure:"yes"` - GoDaddySecretKey string `secure:"yes"` - GoDaddyTTL int64 - GoDaddyOTE bool - OCPRouterName string - IBMCloudProxied bool - IBMCloudConfigFile string - TencentCloudConfigFile string - TencentCloudZoneType string - PiholeServer string - PiholePassword string `secure:"yes"` - PiholeTLSInsecureSkipVerify bool - PluralCluster string - PluralProvider string + APIServerURL string + KubeConfig string + RequestTimeout time.Duration + DefaultTargets []string + ContourLoadBalancerService string + GlooNamespace string + SkipperRouteGroupVersion string + Sources []string + Namespace string + AnnotationFilter string + LabelFilter string + FQDNTemplate string + CombineFQDNAndAnnotation bool + IgnoreHostnameAnnotation bool + IgnoreIngressTLSSpec bool + IgnoreIngressRulesSpec bool + GatewayNamespace string + GatewayLabelFilter string + Compatibility string + PublishInternal bool + PublishHostIP bool + AlwaysPublishNotReadyAddresses bool + ConnectorSourceServer string + Provider string + GoogleProject string + GoogleBatchChangeSize int + GoogleBatchChangeInterval time.Duration + GoogleZoneVisibility string + DomainFilter []string + ExcludeDomains []string + RegexDomainFilter *regexp.Regexp + RegexDomainExclusion *regexp.Regexp + ZoneNameFilter []string + ZoneIDFilter []string + TargetNetFilter []string + ExcludeTargetNets []string + AlibabaCloudConfigFile string + AlibabaCloudZoneType string + AWSZoneType string + AWSZoneTagFilter []string + AWSAssumeRole string + AWSAssumeRoleExternalID string + AWSBatchChangeSize int + AWSBatchChangeInterval time.Duration + AWSEvaluateTargetHealth bool + AWSAPIRetries int + AWSPreferCNAME bool + AWSZoneCacheDuration time.Duration + AWSSDServiceCleanup bool + AzureConfigFile string + AzureResourceGroup string + AzureSubscriptionID string + AzureUserAssignedIdentityClientID string + BluecatDNSConfiguration string + BluecatConfigFile string + BluecatDNSView string + BluecatGatewayHost string + BluecatRootZone string + BluecatDNSServerName string + BluecatDNSDeployType string + BluecatSkipTLSVerify bool + CloudflareProxied bool + CloudflareDNSRecordsPerPage int + CoreDNSPrefix string + RcodezeroTXTEncrypt bool + AkamaiServiceConsumerDomain string + AkamaiClientToken string + AkamaiClientSecret string + AkamaiAccessToken string + AkamaiEdgercPath string + AkamaiEdgercSection string + InfobloxGridHost string + InfobloxWapiPort int + InfobloxWapiUsername string + InfobloxWapiPassword string `secure:"yes"` + InfobloxWapiVersion string + InfobloxSSLVerify bool + InfobloxView string + InfobloxMaxResults int + InfobloxFQDNRegEx string + InfobloxNameRegEx string + InfobloxCreatePTR bool + InfobloxCacheDuration int + DynCustomerName string + DynUsername string + DynPassword string `secure:"yes"` + DynMinTTLSeconds int + OCIConfigFile string + OCICompartmentOCID string + OCIAuthInstancePrincipal bool + InMemoryZones []string + OVHEndpoint string + OVHApiRateLimit int + PDNSServer string + PDNSAPIKey string `secure:"yes"` + PDNSTLSEnabled bool + TLSCA string + TLSClientCert string + TLSClientCertKey string + Policy string + Registry string + TXTOwnerID string + TXTPrefix string + TXTSuffix string + Interval time.Duration + MinEventSyncInterval time.Duration + Once bool + DryRun bool + UpdateEvents bool + LogFormat string + MetricsAddress string + LogLevel string + TXTCacheInterval time.Duration + TXTWildcardReplacement string + ExoscaleEndpoint string + ExoscaleAPIKey string `secure:"yes"` + ExoscaleAPISecret string `secure:"yes"` + CRDSourceAPIVersion string + CRDSourceKind string + ServiceTypeFilter []string + CFAPIEndpoint string + CFUsername string + CFPassword string + ResolveServiceLoadBalancerHostname bool + RFC2136Host string + RFC2136Port int + RFC2136Zone string + RFC2136Insecure bool + RFC2136GSSTSIG bool + RFC2136KerberosRealm string + RFC2136KerberosUsername string + RFC2136KerberosPassword string `secure:"yes"` + RFC2136TSIGKeyName string + RFC2136TSIGSecret string `secure:"yes"` + RFC2136TSIGSecretAlg string + RFC2136TAXFR bool + RFC2136MinTTL time.Duration + RFC2136BatchChangeSize int + NS1Endpoint string + NS1IgnoreSSL bool + NS1MinTTLSeconds int + TransIPAccountName string + TransIPPrivateKeyFile string + DigitalOceanAPIPageSize int + ManagedDNSRecordTypes []string + GoDaddyAPIKey string `secure:"yes"` + GoDaddySecretKey string `secure:"yes"` + GoDaddyTTL int64 + GoDaddyOTE bool + OCPRouterName string + IBMCloudProxied bool + IBMCloudConfigFile string + TencentCloudConfigFile string + TencentCloudZoneType string + PiholeServer string + PiholePassword string `secure:"yes"` + PiholeTLSInsecureSkipVerify bool + PluralCluster string + PluralProvider string } var defaultConfig = &Config{ @@ -391,7 +391,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("server", "The Kubernetes API server to connect to (default: auto-detect)").Default(defaultConfig.APIServerURL).StringVar(&cfg.APIServerURL) app.Flag("kubeconfig", "Retrieve target cluster configuration from a Kubernetes configuration file (default: auto-detect)").Default(defaultConfig.KubeConfig).StringVar(&cfg.KubeConfig) app.Flag("request-timeout", "Request timeout when calling Kubernetes APIs. 0s means no timeout").Default(defaultConfig.RequestTimeout.String()).DurationVar(&cfg.RequestTimeout) - app.Flag("resolve-lb-hostname", "Resolve the hostname of LoadBalancer-type Service object to IP addresses in order to create DNS A/AAAA records instead of CNAMEs").BoolVar(&cfg.ResolveLoadBalancerHostname) + app.Flag("resolve-service-load-balancer-hostname", "Resolve the hostname of LoadBalancer-type Service object to IP addresses in order to create DNS A/AAAA records instead of CNAMEs").BoolVar(&cfg.ResolveServiceLoadBalancerHostname) // Flags related to cloud foundry app.Flag("cf-api-endpoint", "The fully-qualified domain name of the cloud foundry instance you are targeting").Default(defaultConfig.CFAPIEndpoint).StringVar(&cfg.CFAPIEndpoint) From f4c7466e40667f898589e927617f126eef575e67 Mon Sep 17 00:00:00 2001 From: Charles Xu Date: Mon, 24 Apr 2023 11:17:59 -0700 Subject: [PATCH 087/117] fix lint --- provider/cloudflare/cloudflare.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/provider/cloudflare/cloudflare.go b/provider/cloudflare/cloudflare.go index 3bfc12c0c..0623ca2a0 100644 --- a/provider/cloudflare/cloudflare.go +++ b/provider/cloudflare/cloudflare.go @@ -19,7 +19,6 @@ package cloudflare import ( "context" "fmt" - "io/ioutil" "os" "strconv" "strings" @@ -159,9 +158,9 @@ func NewCloudFlareProvider(domainFilter endpoint.DomainFilter, zoneIDFilter prov if os.Getenv("CF_API_TOKEN") != "" { token := os.Getenv("CF_API_TOKEN") if strings.HasPrefix(token, "file:") { - tokenBytes, err := ioutil.ReadFile(strings.TrimPrefix(token, "file:")) + tokenBytes, err := os.ReadFile(strings.TrimPrefix(token, "file:")) if err != nil { - return nil, fmt.Errorf("failed to read CF_API_TOKEN from file: %v", err) + return nil, fmt.Errorf("failed to read CF_API_TOKEN from file: %w", err) } token = string(tokenBytes) } From 498b2aa25d44478c41fc96d992a20420da7f0c1b Mon Sep 17 00:00:00 2001 From: Steve Hipwell Date: Wed, 26 Apr 2023 16:36:45 +0100 Subject: [PATCH 088/117] chore(chart): Reverted chart changes to args Signed-off-by: Steve Hipwell --- charts/external-dns/README.md | 1 - charts/external-dns/templates/deployment.yaml | 3 --- charts/external-dns/values.yaml | 2 -- 3 files changed, 6 deletions(-) diff --git a/charts/external-dns/README.md b/charts/external-dns/README.md index a0e543ed4..e3433227b 100644 --- a/charts/external-dns/README.md +++ b/charts/external-dns/README.md @@ -83,7 +83,6 @@ The following table lists the configurable parameters of the _ExternalDNS_ chart | `secretConfiguration.mountPath` | Mount path of secret configuration secret (this can be templated). | `""` | | `secretConfiguration.data` | Secret configuration secret data. Could be used to store DNS provider credentials. | `{}` | | `secretConfiguration.subPath` | Sub-path of secret configuration secret (this can be templated). | `""` | -| `resolveServiceLoadBalancerHostname` | Resolve the hostname of LoadBalancer-type Service object to IP addresses in order to create DNS A/AAAA records instead of CNAMEs | `false` | ## Namespaced scoped installation diff --git a/charts/external-dns/templates/deployment.yaml b/charts/external-dns/templates/deployment.yaml index a2f0b74f9..5c3e1128f 100644 --- a/charts/external-dns/templates/deployment.yaml +++ b/charts/external-dns/templates/deployment.yaml @@ -96,9 +96,6 @@ spec: - --domain-filter={{ . }} {{- end }} - --provider={{ tpl .Values.provider $ }} - {{- if .Values.resolveServiceLoadBalancerHostname }} - - --resolve-service-load-balancer-hostname - {{- end }} {{- range .Values.extraArgs }} - {{ tpl . $ }} {{- end }} diff --git a/charts/external-dns/values.yaml b/charts/external-dns/values.yaml index 1084cd2ac..6e0f80265 100644 --- a/charts/external-dns/values.yaml +++ b/charts/external-dns/values.yaml @@ -15,8 +15,6 @@ fullnameOverride: "" commonLabels: {} -resolveServiceLoadBalancerHostname: false - serviceAccount: # Specifies whether a service account should be created create: true From 2554f9f879d3364156f878f4db619224a69aa7a3 Mon Sep 17 00:00:00 2001 From: Viacheslav Sychov Date: Fri, 28 Apr 2023 20:29:54 +0200 Subject: [PATCH 089/117] #1828: Support encrypted DNS txt records Signed-off-by: Viacheslav Sychov --- docs/registry.md | 65 +++++++++++++++++ endpoint/crypto.go | 134 ++++++++++++++++++++++++++++++++++ endpoint/crypto_test.go | 58 +++++++++++++++ endpoint/labels.go | 56 +++++++++++++- endpoint/labels_test.go | 71 +++++++++++++----- main.go | 2 +- pkg/apis/externaldns/types.go | 6 ++ provider/awssd/aws_sd.go | 2 +- registry/aws_sd_registry.go | 4 +- registry/txt.go | 23 ++++-- registry/txt_test.go | 54 ++++++++------ 11 files changed, 423 insertions(+), 52 deletions(-) create mode 100644 endpoint/crypto.go create mode 100644 endpoint/crypto_test.go diff --git a/docs/registry.md b/docs/registry.md index 615ba305a..3ff20b93a 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -13,3 +13,68 @@ The controller will try to create the "new format" TXT records if they are not p Later on, the old format will be dropped and only the new format will be kept (-). Cleanup will be done by controller itself. + +### Encryption of TXT Records +TXT records may contain sensitive information, such as the internal ingress name or namespace, which attackers could exploit to gather information about your infrastructure. +By encrypting TXT records, you can protect this information from unauthorized access. It is strongly recommended to encrypt all TXT records to prevent potential security breaches. + +To enable encryption of TXT records, you can use the following parameters: +- `--txt-encrypt-enabled=true` +- `--txt-encrypt-aes-key=32bytesKey` (used for AES-256-GCM encryption and should be exactly 32 bytes) + +Note that the key used for encryption should be a secure key and properly managed to ensure the security of your TXT records. + +### Generating TXT encryption AES key +Python +```python +python -c 'import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())' +``` + +Bash +```shell +dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64 | tr -d -- '\n' | tr -- '+/' '-_'; echo +``` + +OpenSSL +```shell +openssl rand -base64 32 | tr -- '+/' '-_' +``` + +PowerShell +```powershell +# Add System.Web assembly to session, just in case +Add-Type -AssemblyName System.Web +[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes([System.Web.Security.Membership]::GeneratePassword(32,4))).Replace("+","-").Replace("/","_") +``` + +Terraform +```hcl +resource "random_password" "txt_key" { + length = 32 + override_special = "-_" +} +``` + +### Manually Encrypt/Decrypt TXT Records + +In some cases, you may need to edit labels generated by External-DNS, and in such cases, you can use simple Golang code to do that. + +```go +package main + +import ( + "fmt" + "sigs.k8s.io/external-dns/endpoint" +) + +func main() { + key := []byte("testtesttesttesttesttesttesttest") + encrypted, _ := endpoint.EncryptText( + "heritage=external-dns,external-dns/owner=example,external-dns/resource=ingress/default/example", + key, + nil, + ) + decrypted, _, _ := endpoint.DecryptText(encrypted, key) + fmt.Println(decrypted) +} +``` diff --git a/endpoint/crypto.go b/endpoint/crypto.go new file mode 100644 index 000000000..1d6ebd1dd --- /dev/null +++ b/endpoint/crypto.go @@ -0,0 +1,134 @@ +/* +Copyright 2017 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 endpoint + +import ( + "bytes" + "compress/gzip" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/base64" + "fmt" + "io" + + log "github.com/sirupsen/logrus" +) + +// EncryptText gzip input data and encrypts it using the supplied AES key +func EncryptText(text string, aesKey []byte, nonceEncoded []byte) (string, error) { + block, err := aes.NewCipher(aesKey) + if err != nil { + return "", err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + + nonce := make([]byte, gcm.NonceSize()) + if nonceEncoded == nil { + if _, err = io.ReadFull(rand.Reader, nonce); err != nil { + return "", err + } + } else { + if _, err = base64.StdEncoding.Decode(nonce, nonceEncoded); err != nil { + return "", err + } + } + + data, err := compressData([]byte(text)) + if err != nil { + return "", err + } + + cipherData := gcm.Seal(nonce, nonce, data, nil) + return base64.StdEncoding.EncodeToString(cipherData), nil +} + +// DecryptText decrypt gziped data using a supplied AES encryption key ang ungzip it +// in case of decryption failed, will return original input and decryption error +func DecryptText(text string, aesKey []byte) (decryptResult string, encryptNonce string, err error) { + block, err := aes.NewCipher(aesKey) + if err != nil { + return "", "", err + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return "", "", err + } + nonceSize := gcm.NonceSize() + data, err := base64.StdEncoding.DecodeString(text) + if err != nil { + return "", "", err + } + if len(data) <= nonceSize { + return "", "", fmt.Errorf("the encoded data from text %#v is shorter than %#v bytes and can't be decoded", text, nonceSize) + } + nonce, ciphertext := data[:nonceSize], data[nonceSize:] + plaindata, err := gcm.Open(nil, nonce, ciphertext, nil) + if err != nil { + return "", "", err + } + plaindata, err = decompressData(plaindata) + if err != nil { + log.Debugf("Failed to decompress data based on the base64 encoded text %#v. Got error %#v.", text, err) + return "", "", err + } + + return string(plaindata), base64.StdEncoding.EncodeToString(nonce), nil +} + +// decompressData gzip compressed data +func decompressData(data []byte) (resData []byte, err error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, err + } + defer gz.Close() + var b bytes.Buffer + if _, err = b.ReadFrom(gz); err != nil { + return nil, err + } + + return b.Bytes(), nil +} + +// compressData by gzip, for minify data stored in registry +func compressData(data []byte) (compressedData []byte, err error) { + var b bytes.Buffer + gz, err := gzip.NewWriterLevel(&b, gzip.BestCompression) + if err != nil { + return nil, err + } + + defer gz.Close() + if _, err = gz.Write(data); err != nil { + return nil, err + } + + if err = gz.Flush(); err != nil { + return nil, err + } + + if err = gz.Close(); err != nil { + return nil, err + } + + return b.Bytes(), nil +} diff --git a/endpoint/crypto_test.go b/endpoint/crypto_test.go new file mode 100644 index 000000000..880afcce3 --- /dev/null +++ b/endpoint/crypto_test.go @@ -0,0 +1,58 @@ +/* +Copyright 2017 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 endpoint + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestEncrypt(t *testing.T) { + // Verify that text encryption and decryption works + aesKey := []byte("s%zF`.*'5`9.AhI2!B,.~hmbs^.*TL?;") + plaintext := "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum." + encryptedtext, err := EncryptText(plaintext, aesKey, nil) + require.NoError(t, err) + decryptedtext, _, err := DecryptText(encryptedtext, aesKey) + require.NoError(t, err) + if plaintext != decryptedtext { + t.Errorf("Original plain text %#v differs from the resulting decrypted text %#v", plaintext, decryptedtext) + } + + // Verify that decrypt returns an error and empty data if wrong AES encryption key is used + decryptedtext, _, err = DecryptText(encryptedtext, []byte("s'J!jD`].LC?g&Oa11AgTub,j48ts/96")) + require.Error(t, err) + if decryptedtext != "" { + t.Error("Data decryption failed, empty string should be as result") + } + + // Verify that decrypt returns an error and empty data if unencrypted input is is supplied + decryptedtext, _, err = DecryptText(plaintext, aesKey) + require.Error(t, err) + if decryptedtext != "" { + t.Errorf("Data decryption failed, empty string should be as result") + } + + // Verify that a known encrypted text is decrypted to what is expected + encryptedtext = "0Mfzf6wsN8llrfX0ucDZ6nlc2+QiQfKKedjPPLu5atb2I35L9nUZeJcCnuLVW7CVW3K0h94vSuBLdXnMrj8Vcm0M09shxaoF48IcCpD03XtQbKXqk2hPbsW6+JybvplHIQGr16/PcjUSObGmR9yjf38+qEltApkKvrPjsyw43BX4eE10rL0Bln33UJD7/w+zazRDPFlAcbGtkt0ETKHnvyB3/aCddLipvrhjCXj2ZY/ktRF6h716kJRgXU10dCIQHFYU45MIdxI+k10HK3yZqhI2V0Gp2xjrFV/LRQ7/OS9SFee4asPWUYxbCEsnOzp8qc0dCPFSo1dtADzWnUZnsAcbnjtudT4milfLJc5CxDk1v3ykqQ/ajejwHjWQ7b8U6AsTErbezfdcqrb5IzkLgHb5TosnfrdDmNc9GcKfpsrCHbVY8KgNwMVdtwavLv7d9WM6sooUlZ3t0sABGkzagXQmPRvwLnkSOlie5XrnzWo8/8/4UByLga29CaXO" + decryptedtext, _, err = DecryptText(encryptedtext, aesKey) + require.NoError(t, err) + if decryptedtext != plaintext { + t.Error("Decryption of text didn't result in expected plaintext result.") + } +} diff --git a/endpoint/labels.go b/endpoint/labels.go index 797190db4..c65333dc6 100644 --- a/endpoint/labels.go +++ b/endpoint/labels.go @@ -17,6 +17,8 @@ limitations under the License. package endpoint import ( + log "github.com/sirupsen/logrus" + "errors" "fmt" "sort" @@ -41,6 +43,9 @@ const ( // DualstackLabelKey is the name of the label that identifies dualstack endpoints DualstackLabelKey = "dualstack" + + // txtEncryptionNonce label for keep same nonce for same txt records, for prevent different result of encryption for same txt record, it can cause issues for some providers + txtEncryptionNonce = "txt-encryption-nonce" ) // Labels store metadata related to the endpoint @@ -55,7 +60,7 @@ func NewLabels() Labels { // NewLabelsFromString constructs endpoints labels from a provided format string // if heritage set to another value is found then error is returned // no heritage automatically assumes is not owned by external-dns and returns invalidHeritage error -func NewLabelsFromString(labelText string) (Labels, error) { +func NewLabelsFromStringPlain(labelText string) (Labels, error) { endpointLabels := map[string]string{} labelText = strings.Trim(labelText, "\"") // drop quotes tokens := strings.Split(labelText, ",") @@ -85,9 +90,26 @@ func NewLabelsFromString(labelText string) (Labels, error) { return endpointLabels, nil } -// Serialize transforms endpoints labels into a external-dns recognizable format string +func NewLabelsFromString(labelText string, aesKey []byte) (Labels, error) { + if len(aesKey) != 0 { + decryptedText, encryptionNonce, err := DecryptText(strings.Trim(labelText, "\""), aesKey) + //in case if we have decryption error, just try process original text + //decryption errors should be ignored here, because we can already have plain-text labels in registry + if err == nil { + labels, err := NewLabelsFromStringPlain(decryptedText) + if err == nil { + labels[txtEncryptionNonce] = encryptionNonce + } + + return labels, err + } + } + return NewLabelsFromStringPlain(labelText) +} + +// SerializePlain transforms endpoints labels into a external-dns recognizable format string // withQuotes adds additional quotes -func (l Labels) Serialize(withQuotes bool) string { +func (l Labels) SerializePlain(withQuotes bool) string { var tokens []string tokens = append(tokens, fmt.Sprintf("heritage=%s", heritage)) var keys []string @@ -104,3 +126,31 @@ func (l Labels) Serialize(withQuotes bool) string { } return strings.Join(tokens, ",") } + +// Serialize same to SerializePlain, but encrypt data, if encryption enabled +func (l Labels) Serialize(withQuotes bool, txtEncryptEnabled bool, aesKey []byte) string { + if !txtEncryptEnabled { + return l.SerializePlain(withQuotes) + } + + var encryptionNonce []byte = nil + if extractedNonce, nonceExists := l[txtEncryptionNonce]; nonceExists { + encryptionNonce = []byte(extractedNonce) + delete(l, txtEncryptionNonce) + } + + text := l.SerializePlain(false) + log.Debugf("Encrypt the serialized text %#v before returning it.", text) + var err error + text, err = EncryptText(text, aesKey, encryptionNonce) + + if err != nil { + log.Fatalf("Failed to encrypt the text %#v using the encryption key %#v. Got error %#v.", text, aesKey, err) + } + + if withQuotes { + text = fmt.Sprintf("\"%s\"", text) + } + log.Debugf("Serialized text after encryption is %#v.", text) + return text +} diff --git a/endpoint/labels_test.go b/endpoint/labels_test.go index 9386a23e6..394635b71 100644 --- a/endpoint/labels_test.go +++ b/endpoint/labels_test.go @@ -25,14 +25,18 @@ import ( type LabelsSuite struct { suite.Suite - foo Labels - fooAsText string - fooAsTextWithQuotes string - barText string - barTextAsMap Labels - noHeritageText string - wrongHeritageText string - multipleHeritageText string // considered invalid + aesKey []byte + foo Labels + fooAsText string + fooAsTextWithQuotes string + fooAsTextEncrypted string + fooAsTextWithQuotesEncrypted string + barText string + barTextEncrypted string + barTextAsMap Labels + noHeritageText string + wrongHeritageText string + multipleHeritageText string // considered invalid } func (suite *LabelsSuite) SetupTest() { @@ -40,48 +44,79 @@ func (suite *LabelsSuite) SetupTest() { "owner": "foo-owner", "resource": "foo-resource", } + suite.aesKey = []byte(")K_Fy|?Z.64#UuHm`}[d!GC%WJM_fs{_") suite.fooAsText = "heritage=external-dns,external-dns/owner=foo-owner,external-dns/resource=foo-resource" suite.fooAsTextWithQuotes = fmt.Sprintf(`"%s"`, suite.fooAsText) - + suite.fooAsTextEncrypted = `+lvP8q9KHJ6BS6O81i2Q6DLNdf2JSKy8j/gbZKviTZlGYj7q+yDoYMgkQ1hPn6urtGllM5bfFMcaaHto52otQtiOYrX8990J3kQqg4s47m3bH3Ejl8RSxSSuWJM3HJtPghQzYg0/LSOsdQ0=` + suite.fooAsTextWithQuotesEncrypted = fmt.Sprintf(`"%s"`, suite.fooAsTextEncrypted) suite.barTextAsMap = map[string]string{ "owner": "bar-owner", "resource": "bar-resource", "new-key": "bar-new-key", } suite.barText = "heritage=external-dns,,external-dns/owner=bar-owner,external-dns/resource=bar-resource,external-dns/new-key=bar-new-key,random=stuff,no-equal-sign,," // also has some random gibberish - + suite.barTextEncrypted = "yi6vVATlgYN0enXBIupVK2atNUKtajofWMroWtvZjUanFZXlWvqjJPpjmMd91kv86bZj+syQEP0uR3TK6eFVV7oKFh/NxYyh238FjZ+25zlXW9TgbLoMalUNOkhKFdfXkLeeaqJjePB59t+kQBYX+ZEryK652asPs6M+xTIvtg07N7WWZ6SjJujm0RRISg==" suite.noHeritageText = "external-dns/owner=random-owner" suite.wrongHeritageText = "heritage=mate,external-dns/owner=random-owner" suite.multipleHeritageText = "heritage=mate,heritage=external-dns,external-dns/owner=random-owner" } func (suite *LabelsSuite) TestSerialize() { - suite.Equal(suite.fooAsText, suite.foo.Serialize(false), "should serializeLabel") - suite.Equal(suite.fooAsTextWithQuotes, suite.foo.Serialize(true), "should serializeLabel") + suite.Equal(suite.fooAsText, suite.foo.SerializePlain(false), "should serializeLabel") + suite.Equal(suite.fooAsTextWithQuotes, suite.foo.SerializePlain(true), "should serializeLabel") + suite.Equal(suite.fooAsText, suite.foo.Serialize(false, false, nil), "should serializeLabel") + suite.Equal(suite.fooAsTextWithQuotes, suite.foo.Serialize(true, false, nil), "should serializeLabel") + suite.Equal(suite.fooAsText, suite.foo.Serialize(false, false, suite.aesKey), "should serializeLabel") + suite.Equal(suite.fooAsTextWithQuotes, suite.foo.Serialize(true, false, suite.aesKey), "should serializeLabel") + suite.NotEqual(suite.fooAsText, suite.foo.Serialize(false, true, suite.aesKey), "should serializeLabel and encrypt") + suite.NotEqual(suite.fooAsTextWithQuotes, suite.foo.Serialize(true, true, suite.aesKey), "should serializeLabel and encrypt") +} + +func (suite *LabelsSuite) TestEncryptionNonceReUsage() { + foo, err := NewLabelsFromString(suite.fooAsTextEncrypted, suite.aesKey) + suite.NoError(err, "should succeed for valid label text") + serialized := foo.Serialize(false, true, suite.aesKey) + suite.Equal(serialized, suite.fooAsTextEncrypted, "serialized result should be equal") } func (suite *LabelsSuite) TestDeserialize() { - foo, err := NewLabelsFromString(suite.fooAsText) + foo, err := NewLabelsFromStringPlain(suite.fooAsText) suite.NoError(err, "should succeed for valid label text") suite.Equal(suite.foo, foo, "should reconstruct original label map") - foo, err = NewLabelsFromString(suite.fooAsTextWithQuotes) + foo, err = NewLabelsFromStringPlain(suite.fooAsTextWithQuotes) suite.NoError(err, "should succeed for valid label text") suite.Equal(suite.foo, foo, "should reconstruct original label map") - bar, err := NewLabelsFromString(suite.barText) + foo, err = NewLabelsFromString(suite.fooAsTextEncrypted, suite.aesKey) + suite.NoError(err, "should succeed for valid encrypted label text") + for key, val := range suite.foo { + suite.Equal(val, foo[key], "should contains all keys from original label map") + } + + foo, err = NewLabelsFromString(suite.fooAsTextWithQuotesEncrypted, suite.aesKey) + suite.NoError(err, "should succeed for valid encrypted label text") + for key, val := range suite.foo { + suite.Equal(val, foo[key], "should contains all keys from original label map") + } + + bar, err := NewLabelsFromStringPlain(suite.barText) suite.NoError(err, "should succeed for valid label text") suite.Equal(suite.barTextAsMap, bar, "should reconstruct original label map") - noHeritage, err := NewLabelsFromString(suite.noHeritageText) + bar, err = NewLabelsFromString(suite.barText, suite.aesKey) + suite.NoError(err, "should succeed for valid encrypted label text") + suite.Equal(suite.barTextAsMap, bar, "should reconstruct original label map") + + noHeritage, err := NewLabelsFromStringPlain(suite.noHeritageText) suite.Equal(ErrInvalidHeritage, err, "should fail if no heritage is found") suite.Nil(noHeritage, "should return nil") - wrongHeritage, err := NewLabelsFromString(suite.wrongHeritageText) + wrongHeritage, err := NewLabelsFromStringPlain(suite.wrongHeritageText) suite.Equal(ErrInvalidHeritage, err, "should fail if wrong heritage is found") suite.Nil(wrongHeritage, "if error should return nil") - multipleHeritage, err := NewLabelsFromString(suite.multipleHeritageText) + multipleHeritage, err := NewLabelsFromStringPlain(suite.multipleHeritageText) suite.Equal(ErrInvalidHeritage, err, "should fail if multiple heritage is found") suite.Nil(multipleHeritage, "if error should return nil") } diff --git a/main.go b/main.go index 960045ce9..0e6e37e28 100644 --- a/main.go +++ b/main.go @@ -382,7 +382,7 @@ func main() { case "noop": r, err = registry.NewNoopRegistry(p) case "txt": - r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTOwnerID, cfg.TXTCacheInterval, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes) + r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTOwnerID, cfg.TXTCacheInterval, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes, cfg.TXTEncryptEnabled, []byte(cfg.TXTEncryptAESKey)) case "aws-sd": r, err = registry.NewAWSSDRegistry(p.(*awssd.AWSSDProvider), cfg.TXTOwnerID) default: diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index eefd2b2bf..a4ca2ab66 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -147,6 +147,8 @@ type Config struct { TXTOwnerID string TXTPrefix string TXTSuffix string + TXTEncryptEnabled bool + TXTEncryptAESKey string Interval time.Duration MinEventSyncInterval time.Duration Once bool @@ -295,6 +297,8 @@ var defaultConfig = &Config{ TXTCacheInterval: 0, TXTWildcardReplacement: "", MinEventSyncInterval: 5 * time.Second, + TXTEncryptEnabled: false, + TXTEncryptAESKey: "", Interval: time.Minute, Once: false, DryRun: false, @@ -570,6 +574,8 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("txt-prefix", "When using the TXT registry, a custom string that's prefixed to each ownership DNS record (optional). Could contain record type template like '%{record_type}-prefix-'. Mutual exclusive with txt-suffix!").Default(defaultConfig.TXTPrefix).StringVar(&cfg.TXTPrefix) app.Flag("txt-suffix", "When using the TXT registry, a custom string that's suffixed to the host portion of each ownership DNS record (optional). Could contain record type template like '-%{record_type}-suffix'. Mutual exclusive with txt-prefix!").Default(defaultConfig.TXTSuffix).StringVar(&cfg.TXTSuffix) app.Flag("txt-wildcard-replacement", "When using the TXT registry, a custom string that's used instead of an asterisk for TXT records corresponding to wildcard DNS records (optional)").Default(defaultConfig.TXTWildcardReplacement).StringVar(&cfg.TXTWildcardReplacement) + app.Flag("txt-encrypt-enabled", "When using the TXT registry, set if TXT records should be encrypted before stored (default: disabled)").BoolVar(&cfg.TXTEncryptEnabled) + app.Flag("txt-encrypt-aes-key", "When using the TXT registry, set TXT record decryption and encryption 32 byte aes key (required when --txt-encrypt=true)").Default(defaultConfig.TXTEncryptAESKey).StringVar(&cfg.TXTEncryptAESKey) // Flags related to the main control loop app.Flag("txt-cache-interval", "The interval between cache synchronizations in duration format (default: disabled)").Default(defaultConfig.TXTCacheInterval.String()).DurationVar(&cfg.TXTCacheInterval) diff --git a/provider/awssd/aws_sd.go b/provider/awssd/aws_sd.go index ecdb7efc5..97cca81cc 100644 --- a/provider/awssd/aws_sd.go +++ b/provider/awssd/aws_sd.go @@ -509,7 +509,7 @@ func (p *AWSSDProvider) DeleteService(service *sd.Service) error { // convert ownerID string to service description format label := endpoint.NewLabels() label[endpoint.OwnerLabelKey] = p.ownerID - label[endpoint.AWSSDDescriptionLabel] = label.Serialize(false) + label[endpoint.AWSSDDescriptionLabel] = label.SerializePlain(false) if strings.HasPrefix(aws.StringValue(service.Description), label[endpoint.AWSSDDescriptionLabel]) { log.Infof("Deleting service \"%s\"", *service.Name) diff --git a/registry/aws_sd_registry.go b/registry/aws_sd_registry.go index a2ff3350d..f0fef584d 100644 --- a/registry/aws_sd_registry.go +++ b/registry/aws_sd_registry.go @@ -55,7 +55,7 @@ func (sdr *AWSSDRegistry) Records(ctx context.Context) ([]*endpoint.Endpoint, er } for _, record := range records { - labels, err := endpoint.NewLabelsFromString(record.Labels[endpoint.AWSSDDescriptionLabel]) + labels, err := endpoint.NewLabelsFromStringPlain(record.Labels[endpoint.AWSSDDescriptionLabel]) if err != nil { // if we fail to parse the output then simply assume the endpoint is not managed by any instance of External DNS record.Labels = endpoint.NewLabels() @@ -96,7 +96,7 @@ func (sdr *AWSSDRegistry) updateLabels(endpoints []*endpoint.Endpoint) { ep.Labels = make(map[string]string) } ep.Labels[endpoint.OwnerLabelKey] = sdr.ownerID - ep.Labels[endpoint.AWSSDDescriptionLabel] = ep.Labels.Serialize(false) + ep.Labels[endpoint.AWSSDDescriptionLabel] = ep.Labels.SerializePlain(false) } } diff --git a/registry/txt.go b/registry/txt.go index 48dd028d5..2c7cccbdf 100644 --- a/registry/txt.go +++ b/registry/txt.go @@ -52,15 +52,27 @@ type TXTRegistry struct { // missingTXTRecords stores TXT records which are missing after the migration to the new format missingTXTRecords []*endpoint.Endpoint + + // encrypt text records + txtEncryptEnabled bool + txtEncryptAESKey []byte } const keySuffixAAAA = ":AAAA" // NewTXTRegistry returns new TXTRegistry object -func NewTXTRegistry(provider provider.Provider, txtPrefix, txtSuffix, ownerID string, cacheInterval time.Duration, txtWildcardReplacement string, managedRecordTypes []string) (*TXTRegistry, error) { +func NewTXTRegistry(provider provider.Provider, txtPrefix, txtSuffix, ownerID string, cacheInterval time.Duration, txtWildcardReplacement string, managedRecordTypes []string, txtEncryptEnabled bool, txtEncryptAESKey []byte) (*TXTRegistry, error) { if ownerID == "" { return nil, errors.New("owner id cannot be empty") } + if len(txtEncryptAESKey) == 0 { + txtEncryptAESKey = nil + } else if len(txtEncryptAESKey) != 32 { + return nil, errors.New("the AES Encryption key must have a length of 32 bytes") + } + if txtEncryptEnabled && txtEncryptAESKey == nil { + return nil, errors.New("the AES Encryption key must be set when TXT record encryption is enabled") + } if len(txtPrefix) > 0 && len(txtSuffix) > 0 { return nil, errors.New("txt-prefix and txt-suffix are mutual exclusive") @@ -75,6 +87,8 @@ func NewTXTRegistry(provider provider.Provider, txtPrefix, txtSuffix, ownerID st cacheInterval: cacheInterval, wildcardReplacement: txtWildcardReplacement, managedRecordTypes: managedRecordTypes, + txtEncryptEnabled: txtEncryptEnabled, + txtEncryptAESKey: txtEncryptAESKey, }, nil } @@ -114,7 +128,7 @@ func (im *TXTRegistry) Records(ctx context.Context) ([]*endpoint.Endpoint, error continue } // We simply assume that TXT records for the registry will always have only one target. - labels, err := endpoint.NewLabelsFromString(record.Targets[0]) + labels, err := endpoint.NewLabelsFromString(record.Targets[0], im.txtEncryptAESKey) if err == endpoint.ErrInvalidHeritage { // if no heritage is found or it is invalid // case when value of txt record cannot be identified @@ -205,7 +219,7 @@ func (im *TXTRegistry) generateTXTRecord(r *endpoint.Endpoint) []*endpoint.Endpo if r.RecordType != endpoint.RecordTypeAAAA { // old TXT record format - txt := endpoint.NewEndpoint(im.mapper.toTXTName(r.DNSName), endpoint.RecordTypeTXT, r.Labels.Serialize(true)) + txt := endpoint.NewEndpoint(im.mapper.toTXTName(r.DNSName), endpoint.RecordTypeTXT, r.Labels.Serialize(true, im.txtEncryptEnabled, im.txtEncryptAESKey)) if txt != nil { txt.WithSetIdentifier(r.SetIdentifier) txt.Labels[endpoint.OwnedRecordLabelKey] = r.DNSName @@ -213,9 +227,8 @@ func (im *TXTRegistry) generateTXTRecord(r *endpoint.Endpoint) []*endpoint.Endpo endpoints = append(endpoints, txt) } } - // new TXT record format (containing record type) - txtNew := endpoint.NewEndpoint(im.mapper.toNewTXTName(r.DNSName, r.RecordType), endpoint.RecordTypeTXT, r.Labels.Serialize(true)) + txtNew := endpoint.NewEndpoint(im.mapper.toNewTXTName(r.DNSName, r.RecordType), endpoint.RecordTypeTXT, r.Labels.Serialize(true, im.txtEncryptEnabled, im.txtEncryptAESKey)) if txtNew != nil { txtNew.WithSetIdentifier(r.SetIdentifier) txtNew.Labels[endpoint.OwnedRecordLabelKey] = r.DNSName diff --git a/registry/txt_test.go b/registry/txt_test.go index 2f7297fb1..80075c81d 100644 --- a/registry/txt_test.go +++ b/registry/txt_test.go @@ -46,20 +46,20 @@ func TestTXTRegistry(t *testing.T) { func testTXTRegistryNew(t *testing.T) { p := inmemory.NewInMemoryProvider() - _, err := NewTXTRegistry(p, "txt", "", "", time.Hour, "", []string{}) + _, err := NewTXTRegistry(p, "txt", "", "", time.Hour, "", []string{}, false, nil) require.Error(t, err) - _, err = NewTXTRegistry(p, "", "txt", "", time.Hour, "", []string{}) + _, err = NewTXTRegistry(p, "", "txt", "", time.Hour, "", []string{}, false, nil) require.Error(t, err) - r, err := NewTXTRegistry(p, "txt", "", "owner", time.Hour, "", []string{}) + r, err := NewTXTRegistry(p, "txt", "", "owner", time.Hour, "", []string{}, false, nil) require.NoError(t, err) assert.Equal(t, p, r.provider) - r, err = NewTXTRegistry(p, "", "txt", "owner", time.Hour, "", []string{}) + r, err = NewTXTRegistry(p, "", "txt", "owner", time.Hour, "", []string{}, false, nil) require.NoError(t, err) - _, err = NewTXTRegistry(p, "txt", "txt", "owner", time.Hour, "", []string{}) + _, err = NewTXTRegistry(p, "txt", "txt", "owner", time.Hour, "", []string{}, false, nil) require.Error(t, err) _, ok := r.mapper.(affixNameMapper) @@ -67,7 +67,17 @@ func testTXTRegistryNew(t *testing.T) { assert.Equal(t, "owner", r.ownerID) assert.Equal(t, p, r.provider) - r, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}) + aesKey := []byte(";k&l)nUC/33:{?d{3)54+,AD?]SX%yh^") + _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, false, nil) + require.NoError(t, err) + + _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, false, aesKey) + require.NoError(t, err) + + _, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, true, nil) + require.Error(t, err) + + r, err = NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, true, aesKey) require.NoError(t, err) _, ok = r.mapper.(affixNameMapper) @@ -203,13 +213,13 @@ func testTXTRegistryRecordsPrefixed(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "wc", []string{}) + r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "wc", []string{}, false, nil) records, _ := r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) // Ensure prefix is case-insensitive - r, _ = NewTXTRegistry(p, "TxT.", "", "owner", time.Hour, "", []string{}) + r, _ = NewTXTRegistry(p, "TxT.", "", "owner", time.Hour, "", []string{}, false, nil) records, _ = r.Records(ctx) assert.True(t, testutils.SameEndpointLabels(records, expectedRecords)) @@ -328,13 +338,13 @@ func testTXTRegistryRecordsSuffixed(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "", "-txt", "owner", time.Hour, "", []string{}) + r, _ := NewTXTRegistry(p, "", "-txt", "owner", time.Hour, "", []string{}, false, nil) records, _ := r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) // Ensure prefix is case-insensitive - r, _ = NewTXTRegistry(p, "", "-TxT", "owner", time.Hour, "", []string{}) + r, _ = NewTXTRegistry(p, "", "-TxT", "owner", time.Hour, "", []string{}, false, nil) records, _ = r.Records(ctx) assert.True(t, testutils.SameEndpointLabels(records, expectedRecords)) @@ -429,7 +439,7 @@ func testTXTRegistryRecordsNoPrefix(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, false, nil) records, _ := r.Records(ctx) assert.True(t, testutils.SameEndpoints(records, expectedRecords)) @@ -472,7 +482,7 @@ func testTXTRegistryApplyChangesWithPrefix(t *testing.T) { newEndpointWithOwner("txt.cname-multiple.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-2"), }, }) - r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "", []string{}) + r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "", []string{}, false, nil) changes := &plan.Changes{ Create: []*endpoint.Endpoint{ @@ -561,7 +571,7 @@ func testTXTRegistryApplyChangesWithTemplatedPrefix(t *testing.T) { p.ApplyChanges(ctx, &plan.Changes{ Create: []*endpoint.Endpoint{}, }) - r, _ := NewTXTRegistry(p, "prefix%{record_type}.", "", "owner", time.Hour, "", []string{}) + r, _ := NewTXTRegistry(p, "prefix%{record_type}.", "", "owner", time.Hour, "", []string{}, false, nil) changes := &plan.Changes{ Create: []*endpoint.Endpoint{ newEndpointWithOwnerResource("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "", "ingress/default/my-ingress"), @@ -605,7 +615,7 @@ func testTXTRegistryApplyChangesWithTemplatedSuffix(t *testing.T) { p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { assert.Equal(t, ctxEndpoints, ctx.Value(provider.RecordsContextKey)) } - r, _ := NewTXTRegistry(p, "", "-%{record_type}suffix", "owner", time.Hour, "", []string{}) + r, _ := NewTXTRegistry(p, "", "-%{record_type}suffix", "owner", time.Hour, "", []string{}, false, nil) changes := &plan.Changes{ Create: []*endpoint.Endpoint{ newEndpointWithOwnerResource("new-record-1.test-zone.example.org", "new-loadbalancer-1.lb.com", endpoint.RecordTypeCNAME, "", "ingress/default/my-ingress"), @@ -671,7 +681,7 @@ func testTXTRegistryApplyChangesWithSuffix(t *testing.T) { newEndpointWithOwner("cname-multiple-txt.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, "").WithSetIdentifier("test-set-2"), }, }) - r, _ := NewTXTRegistry(p, "", "-txt", "owner", time.Hour, "wildcard", []string{}) + r, _ := NewTXTRegistry(p, "", "-txt", "owner", time.Hour, "wildcard", []string{}, false, nil) changes := &plan.Changes{ Create: []*endpoint.Endpoint{ @@ -775,7 +785,7 @@ func testTXTRegistryApplyChangesNoPrefix(t *testing.T) { newEndpointWithOwner("cname-foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), }, }) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, false, nil) changes := &plan.Changes{ Create: []*endpoint.Endpoint{ @@ -941,7 +951,7 @@ func testTXTRegistryMissingRecordsNoPrefix(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "wc", []string{endpoint.RecordTypeCNAME, endpoint.RecordTypeA, endpoint.RecordTypeNS}) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "wc", []string{endpoint.RecordTypeCNAME, endpoint.RecordTypeA, endpoint.RecordTypeNS}, false, nil) records, _ := r.Records(ctx) missingRecords := r.MissingRecords() @@ -1045,7 +1055,7 @@ func testTXTRegistryMissingRecordsWithPrefix(t *testing.T) { }, } - r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "wc", []string{endpoint.RecordTypeCNAME, endpoint.RecordTypeA, endpoint.RecordTypeNS}) + r, _ := NewTXTRegistry(p, "txt.", "", "owner", time.Hour, "wc", []string{endpoint.RecordTypeCNAME, endpoint.RecordTypeA, endpoint.RecordTypeNS}, false, nil) records, _ := r.Records(ctx) missingRecords := r.MissingRecords() @@ -1199,7 +1209,7 @@ func TestNewTXTScheme(t *testing.T) { newEndpointWithOwner("cname-foobar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), }, }) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, false, nil) changes := &plan.Changes{ Create: []*endpoint.Endpoint{ @@ -1275,7 +1285,7 @@ func TestGenerateTXT(t *testing.T) { } p := inmemory.NewInMemoryProvider() p.CreateZone(testZone) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, false, nil) gotTXT := r.generateTXTRecord(record) assert.Equal(t, expectedTXT, gotTXT) } @@ -1294,7 +1304,7 @@ func TestGenerateTXTForAAAA(t *testing.T) { } p := inmemory.NewInMemoryProvider() p.CreateZone(testZone) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, false, nil) gotTXT := r.generateTXTRecord(record) assert.Equal(t, expectedTXT, gotTXT) } @@ -1311,7 +1321,7 @@ func TestFailGenerateTXT(t *testing.T) { expectedTXT := []*endpoint.Endpoint{} p := inmemory.NewInMemoryProvider() p.CreateZone(testZone) - r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}) + r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, false, nil) gotTXT := r.generateTXTRecord(cnameRecord) assert.Equal(t, expectedTXT, gotTXT) } From e233a2accb963472a38b1ec0ec0bcd5fcd20d848 Mon Sep 17 00:00:00 2001 From: Houssem Dellai Date: Mon, 1 May 2023 11:57:54 +0200 Subject: [PATCH 090/117] Added video tutorial in azure.md for using Azure DNS --- docs/tutorials/azure.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/tutorials/azure.md b/docs/tutorials/azure.md index 186407aaa..b6d885034 100644 --- a/docs/tutorials/azure.md +++ b/docs/tutorials/azure.md @@ -648,3 +648,9 @@ resource group: ```bash $ az group delete --name "MyDnsResourceGroup" ``` + +## More tutorials + +A video explanantion is available here: https://www.youtube.com/watch?v=VSn6DPKIhM8&list=PLpbcUe4chE79sB7Jg7B4z3HytqUUEwcNE + +![image](https://user-images.githubusercontent.com/6548359/235437721-87611869-75f2-4f32-bb35-9da585e46299.png) From 4e9dcd096695f7f158c3f3ed4cc63f0cb6ecf118 Mon Sep 17 00:00:00 2001 From: Seweryn Chlewicki Date: Tue, 2 May 2023 16:00:07 +0100 Subject: [PATCH 091/117] Update docs --- README.md | 1 + docs/tutorials/mx-record.md | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 docs/tutorials/mx-record.md diff --git a/README.md b/README.md index a5041afbe..13c7f4b54 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,7 @@ The following tutorials are provided: * [Nginx Ingress Controller](docs/tutorials/nginx-ingress.md) * [NS1](docs/tutorials/ns1.md) * [NS Record Creation with CRD Source](docs/tutorials/ns-record.md) +* [MX Record Creation with CRD Source](docs/tutorials/mx-record.md) * [OpenStack Designate](docs/tutorials/designate.md) * [Oracle Cloud Infrastructure (OCI) DNS](docs/tutorials/oracle.md) * [PowerDNS](docs/tutorials/pdns.md) diff --git a/docs/tutorials/mx-record.md b/docs/tutorials/mx-record.md new file mode 100644 index 000000000..5b5a0eb0b --- /dev/null +++ b/docs/tutorials/mx-record.md @@ -0,0 +1,28 @@ +# Creating MX record with CRD source + +You can create and manage MX records with the help of [CRD source](/docs/contributing/crd-source.md) +and `DNSEndpoint` CRD. Currently, this feature is only supported by `aws`, `azure`, and `google` providers. + +In order to start managing MX records you need to set the `--managed-record-types MX` flag. + +```console +external-dns --source crd --provider {aws|azure|google} --managed-record-types A --managed-record-types CNAME --managed-record-types MX +``` + +Targets within the CRD need to be specified according to the RFC 1034 (section 3.6.1). Below is an example of +`example.com` DNS MX record which specifies two separate targets with distinct priorities. + +```yaml +apiVersion: externaldns.k8s.io/v1alpha1 +kind: DNSEndpoint +metadata: + name: examplemxrecord +spec: + endpoints: + - dnsName: example.com + recordTTL: 180 + recordType: MX + targets: + - 10 mailhost1.example.com + - 20 mailhost2.example.com +``` From 93665c9e7475a7202c74be05fd8af85bf217c0bc Mon Sep 17 00:00:00 2001 From: Seweryn Chlewicki Date: Tue, 2 May 2023 16:01:59 +0100 Subject: [PATCH 092/117] Update nolint comments --- provider/azure/azure.go | 2 +- provider/azure/azure_private_dns.go | 2 +- provider/azure/common.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/provider/azure/azure.go b/provider/azure/azure.go index 24a0e0ecb..b56a21320 100644 --- a/provider/azure/azure.go +++ b/provider/azure/azure.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// nolint:staticcheck +//nolint:staticcheck // Required due to the current dependency on a deprecated version of azure-sdk-for-go package azure import ( diff --git a/provider/azure/azure_private_dns.go b/provider/azure/azure_private_dns.go index fd5733bfe..e3ab40c48 100644 --- a/provider/azure/azure_private_dns.go +++ b/provider/azure/azure_private_dns.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// nolint:staticcheck +//nolint:staticcheck // Required due to the current dependency on a deprecated version of azure-sdk-for-go package azure import ( diff --git a/provider/azure/common.go b/provider/azure/common.go index e6c45ece2..95fd1a851 100644 --- a/provider/azure/common.go +++ b/provider/azure/common.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// nolint:staticcheck +//nolint:staticcheck // Required due to the current dependency on a deprecated version of azure-sdk-for-go package azure import ( From 2484da5feb173b865922857fe29c6af1839c3ae1 Mon Sep 17 00:00:00 2001 From: Ricardo Katz Date: Fri, 6 Jan 2023 12:16:24 -0300 Subject: [PATCH 093/117] Add the right query params on infoblox --- provider/infoblox/infoblox.go | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/provider/infoblox/infoblox.go b/provider/infoblox/infoblox.go index 306d6672d..c15e9dc89 100644 --- a/provider/infoblox/infoblox.go +++ b/provider/infoblox/infoblox.go @@ -192,11 +192,24 @@ func (p *ProviderConfig) Records(ctx context.Context) (endpoints []*endpoint.End for _, zone := range zones { logrus.Debugf("fetch records from zone '%s'", zone.Fqdn) + + view := p.view + if view == "" { + view = "default" + } + searchParams := ibclient.NewQueryParams( + false, + map[string]string{ + "zone": zone.Fqdn, + "view": view, + }, + ) + var resA []ibclient.RecordA objA := ibclient.NewEmptyRecordA() objA.View = p.view objA.Zone = zone.Fqdn - err = p.client.GetObject(objA, "", nil, &resA) + err = p.client.GetObject(objA, "", searchParams, &resA) if err != nil && !isNotFoundError(err) { return nil, fmt.Errorf("could not fetch A records from zone '%s': %s", zone.Fqdn, err) } @@ -242,7 +255,7 @@ func (p *ProviderConfig) Records(ctx context.Context) (endpoints []*endpoint.End objH := ibclient.NewEmptyHostRecord() objH.View = p.view objH.Zone = zone.Fqdn - err = p.client.GetObject(objH, "", nil, &resH) + err = p.client.GetObject(objH, "", searchParams, &resH) if err != nil && !isNotFoundError(err) { return nil, fmt.Errorf("could not fetch host records from zone '%s': %s", zone.Fqdn, err) } @@ -264,7 +277,7 @@ func (p *ProviderConfig) Records(ctx context.Context) (endpoints []*endpoint.End objC := ibclient.NewEmptyRecordCNAME() objC.View = p.view objC.Zone = zone.Fqdn - err = p.client.GetObject(objC, "", nil, &resC) + err = p.client.GetObject(objC, "", searchParams, &resC) if err != nil && !isNotFoundError(err) { return nil, fmt.Errorf("could not fetch CNAME records from zone '%s': %s", zone.Fqdn, err) } @@ -283,7 +296,7 @@ func (p *ProviderConfig) Records(ctx context.Context) (endpoints []*endpoint.End objP := ibclient.NewEmptyRecordPTR() objP.Zone = arpaZone objP.View = p.view - err = p.client.GetObject(objP, "", nil, &resP) + err = p.client.GetObject(objP, "", searchParams, &resP) if err != nil && !isNotFoundError(err) { return nil, fmt.Errorf("could not fetch PTR records from zone '%s': %s", zone.Fqdn, err) } @@ -300,7 +313,7 @@ func (p *ProviderConfig) Records(ctx context.Context) (endpoints []*endpoint.End View: p.view, }, ) - err = p.client.GetObject(objT, "", nil, &resT) + err = p.client.GetObject(objT, "", searchParams, &resT) if err != nil && !isNotFoundError(err) { return nil, fmt.Errorf("could not fetch TXT records from zone '%s': %s", zone.Fqdn, err) } From 2533415e24975ed1a04391b00f5f3b4cc389124f Mon Sep 17 00:00:00 2001 From: Seweryn Chlewicki Date: Tue, 2 May 2023 17:28:43 +0100 Subject: [PATCH 094/117] Add tests for AWS --- provider/aws/aws_test.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/provider/aws/aws_test.go b/provider/aws/aws_test.go index 362ebb989..91ceb8a18 100644 --- a/provider/aws/aws_test.go +++ b/provider/aws/aws_test.go @@ -356,6 +356,7 @@ func TestAWSRecords(t *testing.T) { endpoint.NewEndpointWithTTL("geolocation-subdivision-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificGeolocationSubdivisionCode, "NY"), endpoint.NewEndpoint("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "foo.example.com").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificWeight, "10").WithProviderSpecific(providerSpecificHealthCheckID, "foo-bar-healthcheck-id"), endpoint.NewEndpointWithTTL("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificWeight, "20").WithProviderSpecific(providerSpecificHealthCheckID, "abc-def-healthcheck-id"), + endpoint.NewEndpointWithTTL("mail.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 mailhost1.example.com", "20 mailhost2.example.com"), }) records, err := provider.Records(context.Background()) @@ -380,6 +381,7 @@ func TestAWSRecords(t *testing.T) { endpoint.NewEndpointWithTTL("geolocation-subdivision-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificGeolocationSubdivisionCode, "NY"), endpoint.NewEndpointWithTTL("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "foo.example.com").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificWeight, "10").WithProviderSpecific(providerSpecificHealthCheckID, "foo-bar-healthcheck-id"), endpoint.NewEndpointWithTTL("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificWeight, "20").WithProviderSpecific(providerSpecificHealthCheckID, "abc-def-healthcheck-id"), + endpoint.NewEndpointWithTTL("mail.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 mailhost1.example.com", "20 mailhost2.example.com"), }) } @@ -419,6 +421,7 @@ func TestAWSCreateRecords(t *testing.T) { endpoint.NewEndpoint("create-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "foo.eu-central-1.elb.amazonaws.com"), endpoint.NewEndpoint("create-test-cname-alias.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "alias-target.zone-2.ext-dns-test-2.teapot.zalan.do").WithProviderSpecific(providerSpecificAlias, "true"), endpoint.NewEndpoint("create-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.8.8", "8.8.4.4"), + endpoint.NewEndpoint("create-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, "10 mailhost1.example.com", "20 mailhost2.example.com"), } require.NoError(t, provider.CreateRecords(context.Background(), records)) @@ -434,6 +437,7 @@ func TestAWSCreateRecords(t *testing.T) { endpoint.NewEndpointWithTTL("create-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true").WithProviderSpecific(providerSpecificAlias, "true"), endpoint.NewEndpointWithTTL("create-test-cname-alias.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "alias-target.zone-2.ext-dns-test-2.teapot.zalan.do").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true").WithProviderSpecific(providerSpecificAlias, "true"), endpoint.NewEndpointWithTTL("create-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8", "8.8.4.4"), + endpoint.NewEndpointWithTTL("create-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 mailhost1.example.com", "20 mailhost2.example.com"), }) } @@ -444,6 +448,7 @@ func TestAWSUpdateRecords(t *testing.T) { endpoint.NewEndpointWithTTL("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.1.1.1"), endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "foo.elb.amazonaws.com"), endpoint.NewEndpointWithTTL("create-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8", "8.8.4.4"), + endpoint.NewEndpointWithTTL("update-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 mailhost1.foo.elb.amazonaws.com"), }) currentRecords := []*endpoint.Endpoint{ @@ -452,6 +457,7 @@ func TestAWSUpdateRecords(t *testing.T) { endpoint.NewEndpoint("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.1.1.1"), endpoint.NewEndpoint("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "foo.elb.amazonaws.com"), endpoint.NewEndpoint("create-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.8.8", "8.8.4.4"), + endpoint.NewEndpoint("update-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, "10 mailhost1.foo.elb.amazonaws.com"), } updatedRecords := []*endpoint.Endpoint{ endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4"), @@ -459,6 +465,7 @@ func TestAWSUpdateRecords(t *testing.T) { endpoint.NewEndpoint("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "foo.elb.amazonaws.com"), endpoint.NewEndpoint("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "bar.elb.amazonaws.com"), endpoint.NewEndpoint("create-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4", "4.3.2.1"), + endpoint.NewEndpoint("update-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, "10 mailhost1.foo.elb.amazonaws.com", "20 mailhost2.foo.elb.amazonaws.com"), } require.NoError(t, provider.UpdateRecords(context.Background(), updatedRecords, currentRecords)) @@ -472,6 +479,7 @@ func TestAWSUpdateRecords(t *testing.T) { endpoint.NewEndpointWithTTL("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "foo.elb.amazonaws.com"), endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "bar.elb.amazonaws.com"), endpoint.NewEndpointWithTTL("create-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4", "4.3.2.1"), + endpoint.NewEndpointWithTTL("update-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 mailhost1.foo.elb.amazonaws.com", "20 mailhost2.foo.elb.amazonaws.com"), }) } @@ -484,6 +492,7 @@ func TestAWSDeleteRecords(t *testing.T) { endpoint.NewEndpoint("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true"), endpoint.NewEndpoint("delete-test-cname-alias.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "delete-test.zone-2.ext-dns-test-2.teapot.zalan.do").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true").WithProviderSpecific(providerSpecificAlias, "true").WithProviderSpecific(providerSpecificTargetHostedZone, "/hostedzone/zone-2.ext-dns-test-2.teapot.zalan.do."), endpoint.NewEndpointWithTTL("delete-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8", "8.8.4.4"), + endpoint.NewEndpoint("delete-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, "10 mailhost1.foo.elb.amazonaws.com", "20 mailhost2.foo.elb.amazonaws.com"), } provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), false, false, originalEndpoints) @@ -531,6 +540,8 @@ func TestAWSApplyChanges(t *testing.T) { endpoint.NewEndpointWithTTL("policy-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("policy-change").WithProviderSpecific(providerSpecificWeight, "10"), endpoint.NewEndpointWithTTL("set-identifier-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("before").WithProviderSpecific(providerSpecificWeight, "10"), endpoint.NewEndpointWithTTL("set-identifier-no-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("no-change").WithProviderSpecific(providerSpecificWeight, "10"), + endpoint.NewEndpointWithTTL("update-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 mailhost2.bar.elb.amazonaws.com"), + endpoint.NewEndpointWithTTL("delete-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "30 mailhost1.foo.elb.amazonaws.com"), }) createRecords := []*endpoint.Endpoint{ @@ -539,6 +550,7 @@ func TestAWSApplyChanges(t *testing.T) { endpoint.NewEndpoint("create-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "foo.elb.amazonaws.com"), endpoint.NewEndpoint("create-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "foo.elb.amazonaws.com"), endpoint.NewEndpoint("create-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.8.8", "8.8.4.4"), + endpoint.NewEndpoint("create-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, "10 mailhost1.foo.elb.amazonaws.com"), } currentRecords := []*endpoint.Endpoint{ @@ -554,6 +566,7 @@ func TestAWSApplyChanges(t *testing.T) { endpoint.NewEndpoint("policy-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4").WithSetIdentifier("policy-change").WithProviderSpecific(providerSpecificWeight, "10"), endpoint.NewEndpoint("set-identifier-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4").WithSetIdentifier("before").WithProviderSpecific(providerSpecificWeight, "10"), endpoint.NewEndpoint("set-identifier-no-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4").WithSetIdentifier("no-change").WithProviderSpecific(providerSpecificWeight, "10"), + endpoint.NewEndpoint("update-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, "10 mailhost2.bar.elb.amazonaws.com"), } updatedRecords := []*endpoint.Endpoint{ endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4"), @@ -568,6 +581,7 @@ func TestAWSApplyChanges(t *testing.T) { endpoint.NewEndpoint("policy-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4").WithSetIdentifier("policy-change").WithProviderSpecific(providerSpecificRegion, "us-east-1"), endpoint.NewEndpoint("set-identifier-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4").WithSetIdentifier("after").WithProviderSpecific(providerSpecificWeight, "10"), endpoint.NewEndpoint("set-identifier-no-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4").WithSetIdentifier("no-change").WithProviderSpecific(providerSpecificWeight, "20"), + endpoint.NewEndpoint("update-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, "20 mailhost3.foo.elb.amazonaws.com"), } deleteRecords := []*endpoint.Endpoint{ @@ -576,6 +590,7 @@ func TestAWSApplyChanges(t *testing.T) { endpoint.NewEndpoint("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "qux.elb.amazonaws.com"), endpoint.NewEndpoint("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "qux.elb.amazonaws.com"), endpoint.NewEndpoint("delete-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4", "4.3.2.1"), + endpoint.NewEndpoint("delete-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, "30 mailhost1.foo.elb.amazonaws.com"), } changes := &plan.Changes{ @@ -616,6 +631,8 @@ func TestAWSApplyChanges(t *testing.T) { endpoint.NewEndpointWithTTL("policy-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("policy-change").WithProviderSpecific(providerSpecificRegion, "us-east-1"), endpoint.NewEndpointWithTTL("set-identifier-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("after").WithProviderSpecific(providerSpecificWeight, "10"), endpoint.NewEndpointWithTTL("set-identifier-no-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("no-change").WithProviderSpecific(providerSpecificWeight, "20"), + endpoint.NewEndpointWithTTL("create-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 mailhost1.foo.elb.amazonaws.com"), + endpoint.NewEndpointWithTTL("update-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "20 mailhost3.foo.elb.amazonaws.com"), }) } } @@ -633,6 +650,8 @@ func TestAWSApplyChangesDryRun(t *testing.T) { endpoint.NewEndpointWithTTL("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "qux.elb.amazonaws.com"), endpoint.NewEndpointWithTTL("update-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8", "8.8.4.4"), endpoint.NewEndpointWithTTL("delete-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4", "4.3.2.1"), + endpoint.NewEndpointWithTTL("update-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "20 mail.foo.elb.amazonaws.com"), + endpoint.NewEndpointWithTTL("delete-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 mail.bar.elb.amazonaws.com"), } provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, true, originalEndpoints) @@ -643,6 +662,7 @@ func TestAWSApplyChangesDryRun(t *testing.T) { endpoint.NewEndpoint("create-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "foo.elb.amazonaws.com"), endpoint.NewEndpoint("create-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "foo.elb.amazonaws.com"), endpoint.NewEndpoint("create-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.8.8", "8.8.4.4"), + endpoint.NewEndpoint("create-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, "30 mail.foo.elb.amazonaws.com"), } currentRecords := []*endpoint.Endpoint{ @@ -652,6 +672,7 @@ func TestAWSApplyChangesDryRun(t *testing.T) { endpoint.NewEndpoint("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "bar.elb.amazonaws.com"), endpoint.NewEndpoint("update-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "bar.elb.amazonaws.com"), endpoint.NewEndpoint("update-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.8.8", "8.8.4.4"), + endpoint.NewEndpoint("update-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, "20 mail.foo.elb.amazonaws.com"), } updatedRecords := []*endpoint.Endpoint{ endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4"), @@ -660,6 +681,7 @@ func TestAWSApplyChangesDryRun(t *testing.T) { endpoint.NewEndpoint("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "baz.elb.amazonaws.com"), endpoint.NewEndpoint("update-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "baz.elb.amazonaws.com"), endpoint.NewEndpoint("update-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4", "4.3.2.1"), + endpoint.NewEndpoint("update-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, "10 mail.bar.elb.amazonaws.com"), } deleteRecords := []*endpoint.Endpoint{ @@ -668,6 +690,7 @@ func TestAWSApplyChangesDryRun(t *testing.T) { endpoint.NewEndpoint("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "qux.elb.amazonaws.com"), endpoint.NewEndpoint("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "qux.elb.amazonaws.com"), endpoint.NewEndpoint("delete-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4", "4.3.2.1"), + endpoint.NewEndpoint("delete-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, "10 mail.bar.elb.amazonaws.com"), } changes := &plan.Changes{ From 4a2b9c60aba171f2c409150308ed134a80652473 Mon Sep 17 00:00:00 2001 From: Arnaud Lefray Date: Thu, 4 May 2023 15:10:27 +0200 Subject: [PATCH 095/117] ingress: improve ingress class name filter testing Signed-off-by: Arnaud Lefray --- source/ingress.go | 7 ++- source/ingress_test.go | 96 +++++++++++++++++++++++++++++++++--------- 2 files changed, 82 insertions(+), 21 deletions(-) diff --git a/source/ingress.go b/source/ingress.go index 56833e15c..bf204a266 100644 --- a/source/ingress.go +++ b/source/ingress.go @@ -45,6 +45,8 @@ const ( // Possible values for the ingress-hostname-source annotation IngressHostnameSourceAnnotationOnlyValue = "annotation-only" IngressHostnameSourceDefinedHostsOnlyValue = "defined-hosts-only" + + IngressClassAnnotationKey = "kubernetes.io/ingress.class" ) // ingressSource is an implementation of Source for Kubernetes ingress objects. @@ -238,11 +240,11 @@ func (sc *ingressSource) filterByAnnotations(ingresses []*networkv1.Ingress) ([] // class func (sc *ingressSource) filterByIngressClass(ingresses []*networkv1.Ingress) ([]*networkv1.Ingress, error) { // if no class filter is specified then there's nothing to do - if sc.ingressClassNames == nil { + if len(sc.ingressClassNames) == 0 { return ingresses, nil } - classNameReq, err := labels.NewRequirement("kubernetes.io/ingress.class", selection.In, sc.ingressClassNames) + classNameReq, err := labels.NewRequirement(IngressClassAnnotationKey, selection.In, sc.ingressClassNames) if err != nil { return nil, err } @@ -258,6 +260,7 @@ func (sc *ingressSource) filterByIngressClass(ingresses []*networkv1.Ingress) ([ for _, nameFilter := range sc.ingressClassNames { if ingress.Spec.IngressClassName != nil && nameFilter == *ingress.Spec.IngressClassName { matched = true + } else if matchLabelSelector(selector, ingress.Annotations) { matched = true } diff --git a/source/ingress_test.go b/source/ingress_test.go index a3bfc457a..8d3ae4175 100644 --- a/source/ingress_test.go +++ b/source/ingress_test.go @@ -136,15 +136,15 @@ func TestNewIngressSource(t *testing.T) { annotationFilter: "kubernetes.io/ingress.class=nginx", }, { - title: "non-empty ingress class name list", - expectError: false, + title: "non-empty ingress class name list", + expectError: false, ingressClassNames: []string{"internal", "external"}, }, { - title: "ingress class name and annotation filter jointly specified", - expectError: true, + title: "ingress class name and annotation filter jointly specified", + expectError: true, ingressClassNames: []string{"internal", "external"}, - annotationFilter: "kubernetes.io/ingress.class=nginx", + annotationFilter: "kubernetes.io/ingress.class=nginx", }, } { ti := ti @@ -378,7 +378,7 @@ func testIngressEndpoints(t *testing.T) { ignoreIngressTLSSpec bool ignoreIngressRulesSpec bool ingressLabelSelector labels.Selector - ingressClassNames []string + ingressClassNames []string }{ { title: "no ingress", @@ -1191,16 +1191,22 @@ func testIngressEndpoints(t *testing.T) { }, }, { - title: "ingressClassName filtering", - targetNamespace: "", - ingressClassNames: []string{"public", "dmz"}, + title: "ingressClassName filtering", + targetNamespace: "", + ingressClassNames: []string{"public", "dmz"}, ingressItems: []fakeIngress{ + { + name: "none", + namespace: namespace, + tlsdnsnames: [][]string{{"none.example.org"}}, + ips: []string{"1.0.0.0"}, + }, { name: "fake-public", namespace: namespace, tlsdnsnames: [][]string{{"example.org"}}, ips: []string{"1.2.3.4"}, - ingressClassName: "public", + ingressClassName: "public", // match }, { name: "fake-internal", @@ -1214,17 +1220,57 @@ func testIngressEndpoints(t *testing.T) { namespace: namespace, tlsdnsnames: [][]string{{"dmz.example.org"}}, ips: []string{"3.4.5.6"}, - ingressClassName: "dmz", + ingressClassName: "dmz", // match }, { - name: "annotated-dmz", - namespace: namespace, - tlsdnsnames: [][]string{{"annodmz.example.org"}}, - ips: []string{"4.5.6.7"}, + name: "annotated-dmz", + namespace: namespace, + tlsdnsnames: [][]string{{"annodmz.example.org"}}, + ips: []string{"4.5.6.7"}, annotations: map[string]string{ - "kubernetes.io/ingress.class": "dmz", + "kubernetes.io/ingress.class": "dmz", // match }, }, + { + name: "fake-internal-annotated-dmz", + namespace: namespace, + tlsdnsnames: [][]string{{"int-annodmz.example.org"}}, + ips: []string{"5.6.7.8"}, + annotations: map[string]string{ + "kubernetes.io/ingress.class": "dmz", // match + }, + ingressClassName: "internal", + }, + { + name: "fake-dmz-annotated-internal", + namespace: namespace, + tlsdnsnames: [][]string{{"dmz-annoint.example.org"}}, + ips: []string{"6.7.8.9"}, + annotations: map[string]string{ + "kubernetes.io/ingress.class": "internal", + }, + ingressClassName: "dmz", // match + }, + { + name: "empty-annotated-dmz", + namespace: namespace, + tlsdnsnames: [][]string{{"empty-annotdmz.example.org"}}, + ips: []string{"7.8.9.0"}, + annotations: map[string]string{ + "kubernetes.io/ingress.class": "dmz", // match + }, + ingressClassName: "", + }, + { + name: "empty-annotated-internal", + namespace: namespace, + tlsdnsnames: [][]string{{"empty-annotint.example.org"}}, + ips: []string{"8.9.0.1"}, + annotations: map[string]string{ + "kubernetes.io/ingress.class": "internal", + }, + ingressClassName: "", + }, }, expected: []*endpoint.Endpoint{ { @@ -1239,9 +1285,21 @@ func testIngressEndpoints(t *testing.T) { DNSName: "annodmz.example.org", Targets: endpoint.Targets{"4.5.6.7"}, }, + { + DNSName: "int-annodmz.example.org", + Targets: endpoint.Targets{"5.6.7.8"}, + }, + { + DNSName: "dmz-annoint.example.org", + Targets: endpoint.Targets{"6.7.8.9"}, + }, + { + DNSName: "empty-annotdmz.example.org", + Targets: endpoint.Targets{"7.8.9.0"}, + }, }, - }, - { + }, + { ingressLabelSelector: labels.SelectorFromSet(labels.Set{"app": "web-external"}), title: "ingress with matching labels", targetNamespace: "", @@ -1339,7 +1397,7 @@ func (ing fakeIngress) Ingress() *networkv1.Ingress { Labels: ing.labels, }, Spec: networkv1.IngressSpec{ - Rules: []networkv1.IngressRule{}, + Rules: []networkv1.IngressRule{}, IngressClassName: &ing.ingressClassName, }, Status: networkv1.IngressStatus{ From 04d7ed8ad71aa6ef4aab380836b593996e159e04 Mon Sep 17 00:00:00 2001 From: Arnaud Lefray Date: Thu, 4 May 2023 15:11:50 +0200 Subject: [PATCH 096/117] docs: update class name filtering documentation --- docs/faq.md | 20 ++++++++++---------- docs/tutorials/public-private-route53.md | 5 ++--- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 10a36422f..dda8ed9db 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -258,26 +258,26 @@ Sometimes you need to run an internal and an external dns service. The internal one should provision hostnames used on the internal network (perhaps inside a VPC), and the external one to expose DNS to the internet. -To do this with ExternalDNS you can use the `--ingress-class` to specifically tie an instance of ExternalDNS to -an instance of a ingress controller. Let's assume you have two ingress controllers `nginx-internal` and `nginx-external` -then you can start two ExternalDNS providers one with `--ingress-class=nginx-internal` and one with `--ingress-class=nginx-external`. +To do this with ExternalDNS you can use the `--ingress-class` flag to specifically tie an instance of ExternalDNS to an instance of a ingress controller. +Let's assume you have two ingress controllers, `nginx-internal` and `nginx-external`. +You can then start two ExternalDNS providers, one with `--ingress-class=nginx-internal` and one with `--ingress-class=nginx-external`. -If you need to search for multiple ingress classes, you can specify the argument multiple times, like so: +If you need to search for multiple ingress classes, you can specify the flag multiple times, like so: `--ingress-class=nginx-internal --ingress-class=alb-ingress-internal`. -The `--ingress-class` argument will check both the `ingressClassName` field as well as the deprecated `kubernetes.io/ingress.class` annotation. +The `--ingress-class` flag will check both the `ingressClassName` field and the deprecated `kubernetes.io/ingress.class` annotation. -Note: the `--ingress-class` argument cannot be used at the same time as a `kubernetes.io/ingress.class` annotation filter; if you do this an error will be raised. +Note: the `--ingress-class` flag cannot be used at the same time as a `kubernetes.io/ingress.class` annotation filter; if you do this an error will be raised. -If you use annotations to indicate different ingress classes in your cluster, you can instead use an `--annotation-filter` argument to restrict which objects ExternalDNS considers; for example, `--annotation-filter=kubernetes.io/ingress.class in (public,dmz)`. +If you use annotations to indicate different ingress classes in your cluster, you can instead use an `--annotation-filter` flag to restrict which objects ExternalDNS considers; for example, `--annotation-filter=kubernetes.io/ingress.class in (public,dmz)`. However, beware when using annotation filters with multiple sources, e.g. `--source=service --source=ingress`, since `--annotation-filter` will filter every given source objects. If you need to use annotation filters against a specific source you have to run a separated external dns service containing only the wanted `--source` and `--annotation-filter`. **Note:** Filtering based on annotation or ingress class name means that the external-dns controller will receive all resources of that kind and then filter on the client-side. -In larger clusters with many resources which change frequently this can cause performance issues. If only some resources need to be managed by an instance -of external-dns then label filtering can be used instead of annotation filtering. This means that only those resources which match the selector specified -in `--label-filter` will be passed to the controller. +In larger clusters with many resources which change frequently this can cause performance issues. +If only some resources need to be managed by an instance of external-dns then label filtering can be used instead of ingress class filtering (or legacy annotation filtering). +This means that only those resources which match the selector specified in `--label-filter` will be passed to the controller. ### How do I specify that I want the DNS record to point to either the Node's public or private IP when it has both? diff --git a/docs/tutorials/public-private-route53.md b/docs/tutorials/public-private-route53.md index b8110ecbd..02ec1a54d 100644 --- a/docs/tutorials/public-private-route53.md +++ b/docs/tutorials/public-private-route53.md @@ -351,12 +351,12 @@ metadata: certmanager.k8s.io/acme-challenge-type: "dns01" certmanager.k8s.io/acme-dns01-provider: "route53" certmanager.k8s.io/cluster-issuer: "letsencrypt-production" - kubernetes.io/ingress.class: "external-ingress" kubernetes.io/tls-acme: "true" labels: app: app name: app-public spec: + ingressClassName: "external-ingress" rules: - host: app.domain.com http: @@ -376,12 +376,11 @@ And reuse the requested certificate in private Service definition: apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - annotations: - kubernetes.io/ingress.class: "internal-ingress" labels: app: app name: app-private spec: + ingressClassName: "internal-ingress" rules: - host: app.domain.com http: From e9fd86035d1c17a8c8c843a712a7502c6d58a298 Mon Sep 17 00:00:00 2001 From: Arnaud Lefray Date: Thu, 4 May 2023 15:44:14 +0200 Subject: [PATCH 097/117] fix: add missing record check to ingress tests --- source/ingress_test.go | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/source/ingress_test.go b/source/ingress_test.go index dd3a6debc..b76dd439a 100644 --- a/source/ingress_test.go +++ b/source/ingress_test.go @@ -1319,28 +1319,34 @@ func testIngressEndpoints(t *testing.T) { }, expected: []*endpoint.Endpoint{ { - DNSName: "example.org", - Targets: endpoint.Targets{"1.2.3.4"}, + DNSName: "example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"1.2.3.4"}, }, { - DNSName: "dmz.example.org", - Targets: endpoint.Targets{"3.4.5.6"}, + DNSName: "dmz.example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"3.4.5.6"}, }, { - DNSName: "annodmz.example.org", - Targets: endpoint.Targets{"4.5.6.7"}, + DNSName: "annodmz.example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"4.5.6.7"}, }, { - DNSName: "int-annodmz.example.org", - Targets: endpoint.Targets{"5.6.7.8"}, + DNSName: "int-annodmz.example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"5.6.7.8"}, }, { - DNSName: "dmz-annoint.example.org", - Targets: endpoint.Targets{"6.7.8.9"}, + DNSName: "dmz-annoint.example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"6.7.8.9"}, }, { - DNSName: "empty-annotdmz.example.org", - Targets: endpoint.Targets{"7.8.9.0"}, + DNSName: "empty-annotdmz.example.org", + RecordType: endpoint.RecordTypeA, + Targets: endpoint.Targets{"7.8.9.0"}, }, }, }, From e3842a4be60b713d9454ada503c2af45f77d599f Mon Sep 17 00:00:00 2001 From: Arnaud Lefray Date: Fri, 5 May 2023 08:37:12 +0200 Subject: [PATCH 098/117] Apply suggestions from code review Co-authored-by: John Gardiner Myers --- docs/faq.md | 2 +- docs/tutorials/public-private-route53.md | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index cfcc13ba9..527db7ccd 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -273,7 +273,7 @@ Note: the `--ingress-class` flag cannot be used at the same time as a `kubernete If you use annotations to indicate different ingress classes in your cluster, you can instead use an `--annotation-filter` flag to restrict which objects ExternalDNS considers; for example, `--annotation-filter=kubernetes.io/ingress.class in (public,dmz)`. -However, beware when using annotation filters with multiple sources, e.g. `--source=service --source=ingress`, since `--annotation-filter` will filter every given source objects. +However, beware when using annotation filters with multiple sources, e.g. `--source=service --source=ingress`, since `--annotation-filter` will filter every given source object. If you need to use annotation filters against a specific source you have to run a separated external dns service containing only the wanted `--source` and `--annotation-filter`. **Note:** Filtering based on annotation or ingress class name means that the external-dns controller will receive all resources of that kind and then filter on the client-side. diff --git a/docs/tutorials/public-private-route53.md b/docs/tutorials/public-private-route53.md index 3b950b109..9ceb43195 100644 --- a/docs/tutorials/public-private-route53.md +++ b/docs/tutorials/public-private-route53.md @@ -242,8 +242,6 @@ spec: - --registry=txt - --txt-owner-id=external-dns - --ingress-class=external-ingress - # ... or, if you use annotations for ingress classes - # - --annotation-filter=kubernetes.io/ingress.class in (external-ingress) - --aws-zone-type=public image: registry.k8s.io/external-dns/external-dns:v0.13.4 name: external-dns-public @@ -293,7 +291,7 @@ spec: For this setup to work, you need to create two Ingress definitions for your application. -At first, create public Ingress definition (make sure to un-comment either the `annotations` or `ingressClassName` lines): +At first, create a public Ingress definition (make sure to un-comment either the `annotations` or `ingressClassName` lines): ```yaml apiVersion: networking.k8s.io/v1 @@ -320,7 +318,7 @@ spec: pathType: Prefix ``` -Then create private Ingress definition (again, make sure to un-comment either the `annotations` or `ingressClassName` lines): +Then create a private Ingress definition (again, make sure to un-comment either the `annotations` or `ingressClassName` lines): ```yaml apiVersion: networking.k8s.io/v1 From 42077cd35a2b6ebefaaee60003523291da3639de Mon Sep 17 00:00:00 2001 From: Arnaud Lefray Date: Fri, 5 May 2023 16:08:04 +0200 Subject: [PATCH 099/117] ingress: ignore annotation when ingressclassname is non empty --- source/ingress.go | 7 ++++--- source/ingress_test.go | 9 ++------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/source/ingress.go b/source/ingress.go index bf204a266..af7a9dc99 100644 --- a/source/ingress.go +++ b/source/ingress.go @@ -258,9 +258,10 @@ func (sc *ingressSource) filterByIngressClass(ingresses []*networkv1.Ingress) ([ var matched = false for _, nameFilter := range sc.ingressClassNames { - if ingress.Spec.IngressClassName != nil && nameFilter == *ingress.Spec.IngressClassName { - matched = true - + if ingress.Spec.IngressClassName != nil && len(*ingress.Spec.IngressClassName) > 0 { + if nameFilter == *ingress.Spec.IngressClassName { + matched = true + } } else if matchLabelSelector(selector, ingress.Annotations) { matched = true } diff --git a/source/ingress_test.go b/source/ingress_test.go index b76dd439a..2be3eddfd 100644 --- a/source/ingress_test.go +++ b/source/ingress_test.go @@ -1282,7 +1282,7 @@ func testIngressEndpoints(t *testing.T) { tlsdnsnames: [][]string{{"int-annodmz.example.org"}}, ips: []string{"5.6.7.8"}, annotations: map[string]string{ - "kubernetes.io/ingress.class": "dmz", // match + "kubernetes.io/ingress.class": "dmz", // match but ignored (non-empty ingressClassName) }, ingressClassName: "internal", }, @@ -1302,7 +1302,7 @@ func testIngressEndpoints(t *testing.T) { tlsdnsnames: [][]string{{"empty-annotdmz.example.org"}}, ips: []string{"7.8.9.0"}, annotations: map[string]string{ - "kubernetes.io/ingress.class": "dmz", // match + "kubernetes.io/ingress.class": "dmz", // match (empty ingressClassName) }, ingressClassName: "", }, @@ -1333,11 +1333,6 @@ func testIngressEndpoints(t *testing.T) { RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"4.5.6.7"}, }, - { - DNSName: "int-annodmz.example.org", - RecordType: endpoint.RecordTypeA, - Targets: endpoint.Targets{"5.6.7.8"}, - }, { DNSName: "dmz-annoint.example.org", RecordType: endpoint.RecordTypeA, From f42f3705c5d213a2fed5fdef4abf6c3c736a740e Mon Sep 17 00:00:00 2001 From: Arnaud Lefray Date: Fri, 5 May 2023 16:11:26 +0200 Subject: [PATCH 100/117] docs: replace mentions of ingress.class annotations for the spec.ingressClassName field --- docs/faq.md | 16 +++++++++----- docs/tutorials/alibabacloud.md | 3 +-- .../tutorials/aws-load-balancer-controller.md | 5 +---- docs/tutorials/aws.md | 6 ++--- docs/tutorials/azure-private-dns.md | 3 +-- docs/tutorials/coredns.md | 3 +-- docs/tutorials/exoscale.md | 2 +- docs/tutorials/kube-ingress-aws.md | 5 ----- docs/tutorials/nginx-ingress.md | 4 ---- docs/tutorials/public-private-route53.md | 22 +++++-------------- docs/tutorials/rdns.md | 3 +-- 11 files changed, 25 insertions(+), 47 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 527db7ccd..adefacb81 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -257,8 +257,7 @@ spec: ### Running an internal and external dns service Sometimes you need to run an internal and an external dns service. -The internal one should provision hostnames used on the internal network (perhaps inside a VPC), and the external -one to expose DNS to the internet. +The internal one should provision hostnames used on the internal network (perhaps inside a VPC), and the external one to expose DNS to the internet. To do this with ExternalDNS you can use the `--ingress-class` flag to specifically tie an instance of ExternalDNS to an instance of a ingress controller. Let's assume you have two ingress controllers, `nginx-internal` and `nginx-external`. @@ -267,16 +266,21 @@ You can then start two ExternalDNS providers, one with `--ingress-class=nginx-in If you need to search for multiple ingress classes, you can specify the flag multiple times, like so: `--ingress-class=nginx-internal --ingress-class=alb-ingress-internal`. -The `--ingress-class` flag will check both the `ingressClassName` field and the deprecated `kubernetes.io/ingress.class` annotation. +The `--ingress-class` flag will check both the `spec.ingressClassName` field and the deprecated `kubernetes.io/ingress.class` annotation. +The `spec.ingressClassName` tasks precedence over the annotation if both are supplied. -Note: the `--ingress-class` flag cannot be used at the same time as a `kubernetes.io/ingress.class` annotation filter; if you do this an error will be raised. +**Backward compatibility** -If you use annotations to indicate different ingress classes in your cluster, you can instead use an `--annotation-filter` flag to restrict which objects ExternalDNS considers; for example, `--annotation-filter=kubernetes.io/ingress.class in (public,dmz)`. +The previous `--annotation-filter` flag can still be used to restrict which objects ExternalDNS considers; for example, `--annotation-filter=kubernetes.io/ingress.class in (public,dmz)`. However, beware when using annotation filters with multiple sources, e.g. `--source=service --source=ingress`, since `--annotation-filter` will filter every given source object. If you need to use annotation filters against a specific source you have to run a separated external dns service containing only the wanted `--source` and `--annotation-filter`. -**Note:** Filtering based on annotation or ingress class name means that the external-dns controller will receive all resources of that kind and then filter on the client-side. +Note: the `--ingress-class` flag cannot be used at the same time as the `--annotation-filter=kubernetes.io/ingress.class in (...)` flag; if you do this an error will be raised. + +**Performance considerations** + +Filtering based on ingress class name or annotations means that the external-dns controller will receive all resources of that kind and then filter on the client-side. In larger clusters with many resources which change frequently this can cause performance issues. If only some resources need to be managed by an instance of external-dns then label filtering can be used instead of ingress class filtering (or legacy annotation filtering). This means that only those resources which match the selector specified in `--label-filter` will be passed to the controller. diff --git a/docs/tutorials/alibabacloud.md b/docs/tutorials/alibabacloud.md index ee0477ede..f7653237e 100644 --- a/docs/tutorials/alibabacloud.md +++ b/docs/tutorials/alibabacloud.md @@ -233,9 +233,8 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: foo - annotations: - kubernetes.io/ingress.class: "nginx" # use the one that corresponds to your ingress controller. spec: + ingressClassName: nginx # use the one that corresponds to your ingress controller. rules: - host: foo.external-dns-test.com http: diff --git a/docs/tutorials/aws-load-balancer-controller.md b/docs/tutorials/aws-load-balancer-controller.md index 9e66b1e6d..98bc5da69 100644 --- a/docs/tutorials/aws-load-balancer-controller.md +++ b/docs/tutorials/aws-load-balancer-controller.md @@ -24,7 +24,7 @@ as Kubernetes does with the AWS cloud provider. In the examples that follow, it is assumed that you configured the ALB Ingress Controller with the `ingress-class=alb` argument (not to be confused with the same argument to ExternalDNS) so that the controller will only respect Ingress -objects with the `kubernetes.io/ingress.class` annotation set to "alb". +objects with the `ingressClassName` field set to "alb". ## Deploy an example application @@ -80,7 +80,6 @@ kind: Ingress metadata: annotations: alb.ingress.kubernetes.io/scheme: internet-facing - kubernetes.io/ingress.class: alb name: echoserver spec: ingressClassName: alb @@ -120,7 +119,6 @@ metadata: annotations: alb.ingress.kubernetes.io/scheme: internet-facing external-dns.alpha.kubernetes.io/hostname: echoserver.mycluster.example.org, echoserver.example.org - kubernetes.io/ingress.class: alb name: echoserver spec: ingressClassName: alb @@ -159,7 +157,6 @@ metadata: annotations: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/ip-address-type: dualstack - kubernetes.io/ingress.class: alb name: echoserver spec: ingressClassName: alb diff --git a/docs/tutorials/aws.md b/docs/tutorials/aws.md index d5e7ad92b..e749c16d7 100644 --- a/docs/tutorials/aws.md +++ b/docs/tutorials/aws.md @@ -739,9 +739,8 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx - annotations: - kubernetes.io/ingress.class: "nginx" # use the one that corresponds to your ingress controller. spec: + ingressClassName: nginx rules: - host: server.example.com http: @@ -936,7 +935,8 @@ Running several fast polling ExternalDNS instances in a given account can easily * `--source=ingress --source=service` - specify multiple times for multiple sources * `--namespace=my-app` * `--label-filter=app in (my-app)` - * `--annotation-filter=kubernetes.io/ingress.class in (nginx-external)` - note that this filter would apply to services too.. + * `--ingress-class=nginx-external` + * `--annotation-filter=kubernetes.io/ingress.class in (nginx-external)` - note that this filter would apply to services too.. (deprecated in favor of `--ingress-class`) * Limit services watched by type (not applicable to ingress or other types) * `--service-type-filter=LoadBalancer` default `all` * Limit the hosted zones considered diff --git a/docs/tutorials/azure-private-dns.md b/docs/tutorials/azure-private-dns.md index 640036e46..0e85d9026 100644 --- a/docs/tutorials/azure-private-dns.md +++ b/docs/tutorials/azure-private-dns.md @@ -416,9 +416,8 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx - annotations: - kubernetes.io/ingress.class: nginx spec: + ingressClassName: nginx rules: - host: server.example.com http: diff --git a/docs/tutorials/coredns.md b/docs/tutorials/coredns.md index f2c11b8c2..5cd1223f2 100644 --- a/docs/tutorials/coredns.md +++ b/docs/tutorials/coredns.md @@ -198,9 +198,8 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx - annotations: - kubernetes.io/ingress.class: "nginx" spec: + ingressClassName: nginx rules: - host: nginx.example.org http: diff --git a/docs/tutorials/exoscale.md b/docs/tutorials/exoscale.md index d1e93cb0f..047c1d6b8 100644 --- a/docs/tutorials/exoscale.md +++ b/docs/tutorials/exoscale.md @@ -109,9 +109,9 @@ kind: Ingress metadata: name: nginx annotations: - kubernetes.io/ingress.class: nginx external-dns.alpha.kubernetes.io/target: {{ Elastic-IP-address }} spec: + ingressClassName: nginx rules: - host: via-ingress.example.com http: diff --git a/docs/tutorials/kube-ingress-aws.md b/docs/tutorials/kube-ingress-aws.md index bea5e56ac..5cf37d4ec 100644 --- a/docs/tutorials/kube-ingress-aws.md +++ b/docs/tutorials/kube-ingress-aws.md @@ -141,8 +141,6 @@ Create the following Ingress to expose the echoserver application to the Interne apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - annotations: - kubernetes.io/ingress.class: skipper name: echoserver spec: ingressClassName: skipper @@ -181,7 +179,6 @@ kind: Ingress metadata: annotations: external-dns.alpha.kubernetes.io/hostname: echoserver.mycluster.example.org, echoserver.example.org - kubernetes.io/ingress.class: skipper name: echoserver spec: ingressClassName: skipper @@ -218,7 +215,6 @@ kind: Ingress metadata: annotations: alb.ingress.kubernetes.io/ip-address-type: dualstack - kubernetes.io/ingress.class: skipper name: echoserver spec: ingressClassName: skipper @@ -256,7 +252,6 @@ kind: Ingress metadata: annotations: zalando.org/aws-load-balancer-type: nlb - kubernetes.io/ingress.class: skipper name: echoserver spec: ingressClassName: skipper diff --git a/docs/tutorials/nginx-ingress.md b/docs/tutorials/nginx-ingress.md index f6c170b4c..ce79f24d9 100644 --- a/docs/tutorials/nginx-ingress.md +++ b/docs/tutorials/nginx-ingress.md @@ -294,8 +294,6 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx - annotations: - kubernetes.io/ingress.class: nginx spec: ingressClassName: nginx rules: @@ -595,8 +593,6 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx - annotations: - kubernetes.io/ingress.class: nginx spec: ingressClassName: nginx rules: diff --git a/docs/tutorials/public-private-route53.md b/docs/tutorials/public-private-route53.md index 9ceb43195..66071e13d 100644 --- a/docs/tutorials/public-private-route53.md +++ b/docs/tutorials/public-private-route53.md @@ -213,7 +213,7 @@ spec: Consult [AWS ExternalDNS setup docs](aws.md) for installation guidelines. -In ExternalDNS containers args, make sure to specify `aws-zone-type` and either `ingress-class` or `annotation-filter` (depending on whether your cluster makes use of `ingressClassName`): +In ExternalDNS containers args, make sure to specify `aws-zone-type` and `ingress-class`: ```yaml apiVersion: apps/v1beta2 @@ -251,7 +251,7 @@ spec: Consult [AWS ExternalDNS setup docs](aws.md) for installation guidelines. -In ExternalDNS containers args, make sure to specify `aws-zone-type` and either `ingress-class` or `annotation-filter` (depending on whether your cluster makes use of `ingressClassName`): +In ExternalDNS containers args, make sure to specify `aws-zone-type` and `ingress-class`: ```yaml apiVersion: apps/v1beta2 @@ -280,8 +280,6 @@ spec: - --registry=txt - --txt-owner-id=dev.k8s.nexus - --ingress-class=internal-ingress - # ... or, if you use annotations for ingress classes - # - --annotation-filter=kubernetes.io/ingress.class in (internal-ingress) - --aws-zone-type=private image: registry.k8s.io/external-dns/external-dns:v0.13.4 name: external-dns-private @@ -291,21 +289,17 @@ spec: For this setup to work, you need to create two Ingress definitions for your application. -At first, create a public Ingress definition (make sure to un-comment either the `annotations` or `ingressClassName` lines): +At first, create a public Ingress definition: ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - # uncomment if you use annotations for ingress classes - # annotations: - # kubernetes.io/ingress.class: "external-ingress" labels: app: app name: app-public spec: - # uncomment if you use ingressClassName - # ingressClassName: external-ingress + ingressClassName: external-ingress rules: - host: app.domain.com http: @@ -318,21 +312,17 @@ spec: pathType: Prefix ``` -Then create a private Ingress definition (again, make sure to un-comment either the `annotations` or `ingressClassName` lines): +Then create a private Ingress definition: ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - # uncomment if you use annotations for ingress classes - # annotations: - # kubernetes.io/ingress.class: "internal-ingress" labels: app: app name: app-private spec: - # uncomment if you use ingressClassName - # ingressClassName: internal-ingress + ingressClassName: internal-ingress rules: - host: app.domain.com http: diff --git a/docs/tutorials/rdns.md b/docs/tutorials/rdns.md index 684c7c64d..529b1765c 100644 --- a/docs/tutorials/rdns.md +++ b/docs/tutorials/rdns.md @@ -142,9 +142,8 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx - annotations: - kubernetes.io/ingress.class: "nginx" spec: + ingressClassName: nginx rules: - host: nginx.lb.rancher.cloud http: From 3288cc2f980064b2157028e068c76fb12ae37aee Mon Sep 17 00:00:00 2001 From: Gabriel Martinez Date: Sat, 6 May 2023 14:40:36 +0100 Subject: [PATCH 101/117] feat(service): allow using target annotation --- docs/faq.md | 2 +- source/service.go | 50 +++++++++++---------- source/service_test.go | 99 +++++++++++++++++++++++++++++++++++++++++- source/source.go | 2 +- 4 files changed, 127 insertions(+), 26 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 044ee7aa8..a0104a0bf 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -66,7 +66,7 @@ Regarding Ingress, we'll support: ### Are other Ingress Controllers supported? -For Ingress objects, ExternalDNS will attempt to discover the target hostname of the relevant Ingress Controller automatically. If you are using an Ingress Controller that is not listed above you may have issues with ExternalDNS not discovering Endpoints and consequently not creating any DNS records. As a workaround, it is possible to force create an Endpoint by manually specifying a target host/IP for the records to be created by setting the annotation `external-dns.alpha.kubernetes.io/target` in the Ingress object. +For Ingress objects, ExternalDNS will attempt to discover the target hostname of the relevant Ingress Controller automatically. If you are using an Ingress Controller that is not listed above you may have issues with ExternalDNS not discovering Endpoints and consequently not creating any DNS records. As a workaround, it is possible to force create an Endpoint by manually specifying a target host/IP for the records to be created by setting the annotation `external-dns.alpha.kubernetes.io/target` in the Ingress object. Note that services also support this annotation. Another reason you may want to override the ingress hostname or IP address is if you have an external mechanism for handling failover across ingress endpoints. Possible scenarios for this would include using [keepalived-vip](https://github.com/kubernetes/contrib/tree/HEAD/keepalived-vip) to manage failover faster than DNS TTLs might expire. diff --git a/source/service.go b/source/service.go index 925c327f2..e05066094 100644 --- a/source/service.go +++ b/source/service.go @@ -478,30 +478,34 @@ func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, pro var endpoints []*endpoint.Endpoint var targets endpoint.Targets - switch svc.Spec.Type { - case v1.ServiceTypeLoadBalancer: - if useClusterIP { - targets = append(targets, extractServiceIps(svc)...) - } else { - targets = append(targets, extractLoadBalancerTargets(svc, sc.resolveLoadBalancerHostname)...) + targets = getTargetsFromTargetAnnotation(svc.Annotations) + + if len(targets) == 0 { + switch svc.Spec.Type { + case v1.ServiceTypeLoadBalancer: + if useClusterIP { + targets = append(targets, extractServiceIps(svc)...) + } else { + targets = append(targets, extractLoadBalancerTargets(svc, sc.resolveLoadBalancerHostname)...) + } + case v1.ServiceTypeClusterIP: + if sc.publishInternal { + targets = append(targets, extractServiceIps(svc)...) + } + if svc.Spec.ClusterIP == v1.ClusterIPNone { + endpoints = append(endpoints, sc.extractHeadlessEndpoints(svc, hostname, ttl)...) + } + case v1.ServiceTypeNodePort: + // add the nodeTargets and extract an SRV endpoint + targets, err = sc.extractNodePortTargets(svc) + if err != nil { + log.Errorf("Unable to extract targets from service %s/%s error: %v", svc.Namespace, svc.Name, err) + return endpoints + } + endpoints = append(endpoints, sc.extractNodePortEndpoints(svc, targets, hostname, ttl)...) + case v1.ServiceTypeExternalName: + targets = append(targets, extractServiceExternalName(svc)...) } - case v1.ServiceTypeClusterIP: - if sc.publishInternal { - targets = append(targets, extractServiceIps(svc)...) - } - if svc.Spec.ClusterIP == v1.ClusterIPNone { - endpoints = append(endpoints, sc.extractHeadlessEndpoints(svc, hostname, ttl)...) - } - case v1.ServiceTypeNodePort: - // add the nodeTargets and extract an SRV endpoint - targets, err = sc.extractNodePortTargets(svc) - if err != nil { - log.Errorf("Unable to extract targets from service %s/%s error: %v", svc.Namespace, svc.Name, err) - return endpoints - } - endpoints = append(endpoints, sc.extractNodePortEndpoints(svc, targets, hostname, ttl)...) - case v1.ServiceTypeExternalName: - targets = append(targets, extractServiceExternalName(svc)...) } for _, t := range targets { diff --git a/source/service_test.go b/source/service_test.go index 98b400fca..999f943ea 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -1349,7 +1349,7 @@ func TestClusterIpServices(t *testing.T) { labelSelector string }{ { - title: "annotated ClusterIp services return an endpoint with Cluster IP", + title: "hostname annotated ClusterIp services return an endpoint with Cluster IP", svcNamespace: "testing", svcName: "foo", svcType: v1.ServiceTypeClusterIP, @@ -1361,6 +1361,48 @@ func TestClusterIpServices(t *testing.T) { {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, }, }, + { + title: "target annotated ClusterIp services return an endpoint with the specified A", + svcNamespace: "testing", + svcName: "foo", + svcType: v1.ServiceTypeClusterIP, + annotations: map[string]string{ + hostnameAnnotationKey: "foo.example.org.", + targetAnnotationKey: "4.3.2.1", + }, + clusterIP: "1.2.3.4", + expected: []*endpoint.Endpoint{ + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"4.3.2.1"}}, + }, + }, + { + title: "target annotated ClusterIp services return an endpoint with the specified CNAME", + svcNamespace: "testing", + svcName: "foo", + svcType: v1.ServiceTypeClusterIP, + annotations: map[string]string{ + hostnameAnnotationKey: "foo.example.org.", + targetAnnotationKey: "bar.example.org.", + }, + clusterIP: "1.2.3.4", + expected: []*endpoint.Endpoint{ + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"bar.example.org"}}, + }, + }, + { + title: "multiple target annotated ClusterIp services return an endpoint with the specified CNAMES", + svcNamespace: "testing", + svcName: "foo", + svcType: v1.ServiceTypeClusterIP, + annotations: map[string]string{ + hostnameAnnotationKey: "foo.example.org.", + targetAnnotationKey: "bar.example.org.,baz.example.org.", + }, + clusterIP: "1.2.3.4", + expected: []*endpoint.Endpoint{ + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"bar.example.org", "baz.example.org"}}, + }, + }, { title: "hostname annotated ClusterIp services are ignored", svcNamespace: "testing", @@ -1373,6 +1415,33 @@ func TestClusterIpServices(t *testing.T) { clusterIP: "1.2.3.4", expected: []*endpoint.Endpoint{}, }, + { + title: "hostname and target annotated ClusterIp services are ignored", + svcNamespace: "testing", + svcName: "foo", + svcType: v1.ServiceTypeClusterIP, + ignoreHostnameAnnotation: true, + annotations: map[string]string{ + hostnameAnnotationKey: "foo.example.org.", + targetAnnotationKey: "bar.example.org.", + }, + clusterIP: "1.2.3.4", + expected: []*endpoint.Endpoint{}, + }, + { + title: "hostname and target annotated ClusterIp services return an endpoint with the specified CNAME", + svcNamespace: "testing", + svcName: "foo", + svcType: v1.ServiceTypeClusterIP, + annotations: map[string]string{ + hostnameAnnotationKey: "foo.example.org.", + targetAnnotationKey: "bar.example.org.", + }, + clusterIP: "1.2.3.4", + expected: []*endpoint.Endpoint{ + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"bar.example.org"}}, + }, + }, { title: "non-annotated ClusterIp services with set fqdnTemplate return an endpoint with target IP", svcNamespace: "testing", @@ -1392,6 +1461,20 @@ func TestClusterIpServices(t *testing.T) { clusterIP: v1.ClusterIPNone, expected: []*endpoint.Endpoint{}, }, + { + title: "Headless services generate endpoints when target is specified", + svcNamespace: "testing", + svcName: "foo", + svcType: v1.ServiceTypeClusterIP, + annotations: map[string]string{ + hostnameAnnotationKey: "foo.example.org.", + targetAnnotationKey: "bar.example.org.", + }, + clusterIP: v1.ClusterIPNone, + expected: []*endpoint.Endpoint{ + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"bar.example.org"}}, + }, + }, { title: "ClusterIP service with matching label generates an endpoint", svcNamespace: "testing", @@ -1405,6 +1488,20 @@ func TestClusterIpServices(t *testing.T) { }, labelSelector: "app=web-internal", }, + { + title: "ClusterIP service with matching label and target generates a CNAME endpoint", + svcNamespace: "testing", + svcName: "foo", + svcType: v1.ServiceTypeClusterIP, + fqdnTemplate: "{{.Name}}.bar.example.com", + labels: map[string]string{"app": "web-internal"}, + annotations: map[string]string{targetAnnotationKey: "bar.example.com."}, + clusterIP: "4.5.6.7", + expected: []*endpoint.Endpoint{ + {DNSName: "foo.bar.example.com", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"bar.example.com"}}, + }, + labelSelector: "app=web-internal", + }, { title: "ClusterIP service without matching label generates an endpoint", svcNamespace: "testing", diff --git a/source/source.go b/source/source.go index 8573772c3..29169926c 100644 --- a/source/source.go +++ b/source/source.go @@ -46,7 +46,7 @@ const ( accessAnnotationKey = "external-dns.alpha.kubernetes.io/access" // The annotation used for specifying the type of endpoints to use for headless services endpointsTypeAnnotationKey = "external-dns.alpha.kubernetes.io/endpoints-type" - // The annotation used for defining the desired ingress target + // The annotation used for defining the desired ingress/service target targetAnnotationKey = "external-dns.alpha.kubernetes.io/target" // The annotation used for defining the desired DNS record TTL ttlAnnotationKey = "external-dns.alpha.kubernetes.io/ttl" From 683663e9c21f0ed9a2cff073f555baec6752dadc Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Tue, 2 May 2023 22:55:08 -0700 Subject: [PATCH 102/117] IPv6 internal node IPs are usable externally --- docs/tutorials/nodes.md | 5 +- source/compatibility.go | 10 +-- source/node.go | 36 ++++++---- source/node_test.go | 57 +++++++++++++++- source/pod.go | 52 ++++++++------- source/pod_test.go | 142 +++++++++++++++++++++++++++++++++++++++- source/service.go | 21 ++++-- source/service_test.go | 37 +++++++++++ source/shared_test.go | 5 +- 9 files changed, 314 insertions(+), 51 deletions(-) diff --git a/docs/tutorials/nodes.md b/docs/tutorials/nodes.md index 46f21da5d..b99a2f9ca 100644 --- a/docs/tutorials/nodes.md +++ b/docs/tutorials/nodes.md @@ -3,8 +3,9 @@ This tutorial describes how to configure ExternalDNS to use the cluster nodes as source. Using nodes (`--source=node`) as source is possible to synchronize a DNS zone with the nodes of a cluster. -The node source adds an `A` record per each node `externalIP` (if not found, node's `internalIP` is used). -The TTL record can be set with the `external-dns.alpha.kubernetes.io/ttl` node annotation. +The node source adds an `A` record per each node `externalIP` (if not found, any IPv4 `internalIP` is used instead). +It also adds an `AAAA` record per each node IPv6 `internalIP`. +The TTL of the records can be set with the `external-dns.alpha.kubernetes.io/ttl` node annotation. ## Manifest (for cluster without RBAC enabled) diff --git a/source/compatibility.go b/source/compatibility.go index bc6e19abf..1953b76ca 100644 --- a/source/compatibility.go +++ b/source/compatibility.go @@ -157,11 +157,13 @@ func legacyEndpointsFromDNSControllerNodePortService(svc *v1.Service, sc *servic continue } for _, address := range node.Status.Addresses { - if address.Type == v1.NodeExternalIP && isExternal { - endpoints = append(endpoints, endpoint.NewEndpoint(hostname, endpoint.RecordTypeA, address.Address)) + recordType := suitableType(address.Address) + // IPv6 addresses are labeled as NodeInternalIP despite being usable externally as well. + if isExternal && (address.Type == v1.NodeExternalIP || (address.Type == v1.NodeInternalIP && recordType == endpoint.RecordTypeAAAA)) { + endpoints = append(endpoints, endpoint.NewEndpoint(hostname, recordType, address.Address)) } - if address.Type == v1.NodeInternalIP && isInternal { - endpoints = append(endpoints, endpoint.NewEndpoint(hostname, endpoint.RecordTypeA, address.Address)) + if isInternal && address.Type == v1.NodeInternalIP { + endpoints = append(endpoints, endpoint.NewEndpoint(hostname, recordType, address.Address)) } } } diff --git a/source/node.go b/source/node.go index b0e672d73..39135ceaf 100644 --- a/source/node.go +++ b/source/node.go @@ -76,6 +76,11 @@ func NewNodeSource(ctx context.Context, kubeClient kubernetes.Interface, annotat }, nil } +type endpointsKey struct { + dnsName string + recordType string +} + // Endpoints returns endpoint objects for each service that should be processed. func (ns *nodeSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) { nodes, err := ns.nodeInformer.Lister().List(labels.Everything()) @@ -88,7 +93,7 @@ func (ns *nodeSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, erro return nil, err } - endpoints := map[string]*endpoint.Endpoint{} + endpoints := map[endpointsKey]*endpoint.Endpoint{} // create endpoints for all nodes for _, node := range nodes { @@ -109,8 +114,7 @@ func (ns *nodeSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, erro // create new endpoint with the information we already have ep := &endpoint.Endpoint{ - RecordType: "A", // hardcoded DNS record type - RecordTTL: ttl, + RecordTTL: ttl, } if ns.fqdnTemplate != nil { @@ -134,14 +138,19 @@ func (ns *nodeSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, erro return nil, fmt.Errorf("failed to get node address from %s: %s", node.Name, err.Error()) } - ep.Targets = endpoint.Targets(addrs) ep.Labels = endpoint.NewLabels() - - log.Debugf("adding endpoint %s", ep) - if _, ok := endpoints[ep.DNSName]; ok { - endpoints[ep.DNSName].Targets = append(endpoints[ep.DNSName].Targets, ep.Targets...) - } else { - endpoints[ep.DNSName] = ep + for _, addr := range addrs { + log.Debugf("adding endpoint %s target %s", ep, addr) + key := endpointsKey{ + dnsName: ep.DNSName, + recordType: suitableType(addr), + } + if _, ok := endpoints[key]; !ok { + epCopy := *ep + epCopy.RecordType = key.recordType + endpoints[key] = &epCopy + } + endpoints[key].Targets = append(endpoints[key].Targets, addr) } } @@ -163,13 +172,18 @@ func (ns *nodeSource) nodeAddresses(node *v1.Node) ([]string, error) { v1.NodeExternalIP: {}, v1.NodeInternalIP: {}, } + var ipv6Addresses []string for _, addr := range node.Status.Addresses { addresses[addr.Type] = append(addresses[addr.Type], addr.Address) + // IPv6 addresses are labeled as NodeInternalIP despite being usable externally as well. + if addr.Type == v1.NodeInternalIP && suitableType(addr.Address) == endpoint.RecordTypeAAAA { + ipv6Addresses = append(ipv6Addresses, addr.Address) + } } if len(addresses[v1.NodeExternalIP]) > 0 { - return addresses[v1.NodeExternalIP], nil + return append(addresses[v1.NodeExternalIP], ipv6Addresses...), nil } if len(addresses[v1.NodeInternalIP]) > 0 { diff --git a/source/node_test.go b/source/node_test.go index 901c1baa1..885d9f54e 100644 --- a/source/node_test.go +++ b/source/node_test.go @@ -127,6 +127,19 @@ func testNodeSourceEndpoints(t *testing.T) { }, false, }, + { + "ipv6 node with fqdn returns one endpoint", + "", + "", + "node1.example.org", + []v1.NodeAddress{{Type: v1.NodeInternalIP, Address: "2001:DB8::8"}}, + map[string]string{}, + map[string]string{}, + []*endpoint.Endpoint{ + {RecordType: "AAAA", DNSName: "node1.example.org", Targets: endpoint.Targets{"2001:DB8::8"}}, + }, + false, + }, { "node with fqdn template returns endpoint with expanded hostname", "", @@ -166,6 +179,20 @@ func testNodeSourceEndpoints(t *testing.T) { }, false, }, + { + "node with fqdn template returns two endpoints with dual-stack IP addresses and expanded hostname", + "", + "{{.Name}}.example.org", + "node1", + []v1.NodeAddress{{Type: v1.NodeExternalIP, Address: "1.2.3.4"}, {Type: v1.NodeInternalIP, Address: "2001:DB8::8"}}, + map[string]string{}, + map[string]string{}, + []*endpoint.Endpoint{ + {RecordType: "A", DNSName: "node1.example.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {RecordType: "AAAA", DNSName: "node1.example.org", Targets: endpoint.Targets{"2001:DB8::8"}}, + }, + false, + }, { "node with both external and internal IP returns an endpoint with external IP", "", @@ -179,6 +206,20 @@ func testNodeSourceEndpoints(t *testing.T) { }, false, }, + { + "node with both external, internal, and IPv6 IP returns endpoints with external IPs", + "", + "", + "node1", + []v1.NodeAddress{{Type: v1.NodeExternalIP, Address: "1.2.3.4"}, {Type: v1.NodeInternalIP, Address: "2.3.4.5"}, {Type: v1.NodeInternalIP, Address: "2001:DB8::8"}}, + map[string]string{}, + map[string]string{}, + []*endpoint.Endpoint{ + {RecordType: "A", DNSName: "node1", Targets: endpoint.Targets{"1.2.3.4"}}, + {RecordType: "AAAA", DNSName: "node1", Targets: endpoint.Targets{"2001:DB8::8"}}, + }, + false, + }, { "node with only internal IP returns an endpoint with internal IP", "", @@ -192,6 +233,20 @@ func testNodeSourceEndpoints(t *testing.T) { }, false, }, + { + "node with only internal IPs returns endpoints with internal IPs", + "", + "", + "node1", + []v1.NodeAddress{{Type: v1.NodeInternalIP, Address: "2.3.4.5"}, {Type: v1.NodeInternalIP, Address: "2001:DB8::8"}}, + map[string]string{}, + map[string]string{}, + []*endpoint.Endpoint{ + {RecordType: "A", DNSName: "node1", Targets: endpoint.Targets{"2.3.4.5"}}, + {RecordType: "AAAA", DNSName: "node1", Targets: endpoint.Targets{"2001:DB8::8"}}, + }, + false, + }, { "node with neither external nor internal IP returns no endpoints", "", @@ -318,7 +373,7 @@ func testNodeSourceEndpoints(t *testing.T) { false, }, { - "node with nil Lables returns valid endpoint", + "node with nil Labels returns valid endpoint", "", "", "node1", diff --git a/source/pod.go b/source/pod.go index 36e6ffe50..87772d75b 100644 --- a/source/pod.go +++ b/source/pod.go @@ -76,13 +76,18 @@ func NewPodSource(ctx context.Context, kubeClient kubernetes.Interface, namespac func (*podSource) AddEventHandler(ctx context.Context, handler func()) { } +type endpointKey struct { + domain string + recordType string +} + func (ps *podSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) { pods, err := ps.podInformer.Lister().Pods(ps.namespace).List(labels.Everything()) if err != nil { return nil, err } - domains := make(map[string][]string) + endpointMap := make(map[endpointKey][]string) for _, pod := range pods { if !pod.Spec.HostNetwork { log.Debugf("skipping pod %s. hostNetwork=false", pod.Name) @@ -90,50 +95,51 @@ func (ps *podSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error } if domain, ok := pod.Annotations[internalHostnameAnnotationKey]; ok { - if _, ok := domains[domain]; !ok { - domains[domain] = []string{} - } - domains[domain] = append(domains[domain], pod.Status.PodIP) + addToEndpointMap(endpointMap, domain, suitableType(pod.Status.PodIP), pod.Status.PodIP) } if domain, ok := pod.Annotations[hostnameAnnotationKey]; ok { - if _, ok := domains[domain]; !ok { - domains[domain] = []string{} - } - node, _ := ps.nodeInformer.Lister().Get(pod.Spec.NodeName) for _, address := range node.Status.Addresses { - if address.Type == corev1.NodeExternalIP { - domains[domain] = append(domains[domain], address.Address) + recordType := suitableType(address.Address) + // IPv6 addresses are labeled as NodeInternalIP despite being usable externally as well. + if address.Type == corev1.NodeExternalIP || (address.Type == corev1.NodeInternalIP && recordType == endpoint.RecordTypeAAAA) { + addToEndpointMap(endpointMap, domain, recordType, address.Address) } } } if ps.compatibility == "kops-dns-controller" { if domain, ok := pod.Annotations[kopsDNSControllerInternalHostnameAnnotationKey]; ok { - if _, ok := domains[domain]; !ok { - domains[domain] = []string{} - } - domains[domain] = append(domains[domain], pod.Status.PodIP) + addToEndpointMap(endpointMap, domain, suitableType(pod.Status.PodIP), pod.Status.PodIP) } if domain, ok := pod.Annotations[kopsDNSControllerHostnameAnnotationKey]; ok { - if _, ok := domains[domain]; !ok { - domains[domain] = []string{} - } - node, _ := ps.nodeInformer.Lister().Get(pod.Spec.NodeName) for _, address := range node.Status.Addresses { - if address.Type == corev1.NodeExternalIP { - domains[domain] = append(domains[domain], address.Address) + recordType := suitableType(address.Address) + // IPv6 addresses are labeled as NodeInternalIP despite being usable externally as well. + if address.Type == corev1.NodeExternalIP || (address.Type == corev1.NodeInternalIP && recordType == endpoint.RecordTypeAAAA) { + addToEndpointMap(endpointMap, domain, recordType, address.Address) } } } } } endpoints := []*endpoint.Endpoint{} - for domain, targets := range domains { - endpoints = append(endpoints, endpoint.NewEndpoint(domain, endpoint.RecordTypeA, targets...)) + for key, targets := range endpointMap { + endpoints = append(endpoints, endpoint.NewEndpoint(key.domain, key.recordType, targets...)) } return endpoints, nil } + +func addToEndpointMap(endpointMap map[endpointKey][]string, domain string, recordType string, address string) { + key := endpointKey{ + domain: domain, + recordType: recordType, + } + if _, ok := endpointMap[key]; !ok { + endpointMap[key] = []string{} + } + endpointMap[key] = append(endpointMap[key], address) +} diff --git a/source/pod_test.go b/source/pod_test.go index c138aaf5f..5a57aedc2 100644 --- a/source/pod_test.go +++ b/source/pod_test.go @@ -41,7 +41,7 @@ func TestPodSource(t *testing.T) { pods []*corev1.Pod }{ { - "create records based on pod's external and internal IPs", + "create IPv4 records based on pod's external and internal IPs", "", "", []*endpoint.Endpoint{ @@ -111,7 +111,7 @@ func TestPodSource(t *testing.T) { }, }, { - "create records based on pod's external and internal IPs using DNS Controller annotations", + "create IPv4 records based on pod's external and internal IPs using DNS Controller annotations", "", "kops-dns-controller", []*endpoint.Endpoint{ @@ -180,12 +180,149 @@ func TestPodSource(t *testing.T) { }, }, }, + { + "create IPv6 records based on pod's external and internal IPs", + "", + "", + []*endpoint.Endpoint{ + {DNSName: "a.foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, + {DNSName: "internal.a.foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, + }, + false, + []*corev1.Node{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-node1", + }, + Status: corev1.NodeStatus{ + Addresses: []corev1.NodeAddress{ + {Type: corev1.NodeInternalIP, Address: "2001:DB8::1"}, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-node2", + }, + Status: corev1.NodeStatus{ + Addresses: []corev1.NodeAddress{ + {Type: corev1.NodeInternalIP, Address: "2001:DB8::2"}, + }, + }, + }, + }, + []*corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-pod1", + Namespace: "kube-system", + Annotations: map[string]string{ + internalHostnameAnnotationKey: "internal.a.foo.example.org", + hostnameAnnotationKey: "a.foo.example.org", + }, + }, + Spec: corev1.PodSpec{ + HostNetwork: true, + NodeName: "my-node1", + }, + Status: corev1.PodStatus{ + PodIP: "2001:DB8::1", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-pod2", + Namespace: "kube-system", + Annotations: map[string]string{ + internalHostnameAnnotationKey: "internal.a.foo.example.org", + hostnameAnnotationKey: "a.foo.example.org", + }, + }, + Spec: corev1.PodSpec{ + HostNetwork: true, + NodeName: "my-node2", + }, + Status: corev1.PodStatus{ + PodIP: "2001:DB8::2", + }, + }, + }, + }, + { + "create IPv6 records based on pod's external and internal IPs using DNS Controller annotations", + "", + "kops-dns-controller", + []*endpoint.Endpoint{ + {DNSName: "a.foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, + {DNSName: "internal.a.foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, + }, + false, + []*corev1.Node{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-node1", + }, + Status: corev1.NodeStatus{ + Addresses: []corev1.NodeAddress{ + {Type: corev1.NodeInternalIP, Address: "2001:DB8::1"}, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-node2", + }, + Status: corev1.NodeStatus{ + Addresses: []corev1.NodeAddress{ + {Type: corev1.NodeInternalIP, Address: "2001:DB8::2"}, + }, + }, + }, + }, + []*corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-pod1", + Namespace: "kube-system", + Annotations: map[string]string{ + kopsDNSControllerInternalHostnameAnnotationKey: "internal.a.foo.example.org", + kopsDNSControllerHostnameAnnotationKey: "a.foo.example.org", + }, + }, + Spec: corev1.PodSpec{ + HostNetwork: true, + NodeName: "my-node1", + }, + Status: corev1.PodStatus{ + PodIP: "2001:DB8::1", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-pod2", + Namespace: "kube-system", + Annotations: map[string]string{ + kopsDNSControllerInternalHostnameAnnotationKey: "internal.a.foo.example.org", + kopsDNSControllerHostnameAnnotationKey: "a.foo.example.org", + }, + }, + Spec: corev1.PodSpec{ + HostNetwork: true, + NodeName: "my-node2", + }, + Status: corev1.PodStatus{ + PodIP: "2001:DB8::2", + }, + }, + }, + }, { "create multiple records", "", "", []*endpoint.Endpoint{ {DNSName: "a.foo.example.org", Targets: endpoint.Targets{"54.10.11.1"}, RecordType: endpoint.RecordTypeA}, + {DNSName: "a.foo.example.org", Targets: endpoint.Targets{"2001:DB8::1"}, RecordType: endpoint.RecordTypeAAAA}, {DNSName: "b.foo.example.org", Targets: endpoint.Targets{"54.10.11.2"}, RecordType: endpoint.RecordTypeA}, }, false, @@ -197,6 +334,7 @@ func TestPodSource(t *testing.T) { Status: corev1.NodeStatus{ Addresses: []corev1.NodeAddress{ {Type: corev1.NodeExternalIP, Address: "54.10.11.1"}, + {Type: corev1.NodeInternalIP, Address: "2001:DB8::1"}, {Type: corev1.NodeInternalIP, Address: "10.0.1.1"}, }, }, diff --git a/source/service.go b/source/service.go index 925c327f2..58270cdcc 100644 --- a/source/service.go +++ b/source/service.go @@ -216,7 +216,10 @@ func (sc *serviceSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, e }) // Use stable sort to not disrupt the order of services sort.SliceStable(endpoints, func(i, j int) bool { - return endpoints[i].DNSName < endpoints[j].DNSName + if endpoints[i].DNSName != endpoints[j].DNSName { + return endpoints[i].DNSName < endpoints[j].DNSName + } + return endpoints[i].RecordType < endpoints[j].RecordType }) mergedEndpoints := []*endpoint.Endpoint{} mergedEndpoints = append(mergedEndpoints, endpoints[0]) @@ -308,8 +311,8 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri return endpoints } for _, address := range node.Status.Addresses { - if address.Type == v1.NodeExternalIP { - targets = endpoint.Targets{address.Address} + if address.Type == v1.NodeExternalIP || (address.Type == v1.NodeInternalIP && suitableType(address.Address) == endpoint.RecordTypeAAAA) { + targets = append(targets, address.Address) log.Debugf("Generating matching endpoint %s with NodeExternalIP %s", headlessDomain, address.Address) } } @@ -499,7 +502,7 @@ func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, pro log.Errorf("Unable to extract targets from service %s/%s error: %v", svc.Namespace, svc.Name, err) return endpoints } - endpoints = append(endpoints, sc.extractNodePortEndpoints(svc, targets, hostname, ttl)...) + endpoints = append(endpoints, sc.extractNodePortEndpoints(svc, hostname, ttl)...) case v1.ServiceTypeExternalName: targets = append(targets, extractServiceExternalName(svc)...) } @@ -587,6 +590,7 @@ func (sc *serviceSource) extractNodePortTargets(svc *v1.Service) (endpoint.Targe var ( internalIPs endpoint.Targets externalIPs endpoint.Targets + ipv6IPs endpoint.Targets nodes []*v1.Node err error ) @@ -634,24 +638,27 @@ func (sc *serviceSource) extractNodePortTargets(svc *v1.Service) (endpoint.Targe externalIPs = append(externalIPs, address.Address) case v1.NodeInternalIP: internalIPs = append(internalIPs, address.Address) + if suitableType(address.Address) == endpoint.RecordTypeAAAA { + ipv6IPs = append(ipv6IPs, address.Address) + } } } } access := getAccessFromAnnotations(svc.Annotations) if access == "public" { - return externalIPs, nil + return append(externalIPs, ipv6IPs...), nil } if access == "private" { return internalIPs, nil } if len(externalIPs) > 0 { - return externalIPs, nil + return append(externalIPs, ipv6IPs...), nil } return internalIPs, nil } -func (sc *serviceSource) extractNodePortEndpoints(svc *v1.Service, nodeTargets endpoint.Targets, hostname string, ttl endpoint.TTL) []*endpoint.Endpoint { +func (sc *serviceSource) extractNodePortEndpoints(svc *v1.Service, hostname string, ttl endpoint.TTL) []*endpoint.Endpoint { var endpoints []*endpoint.Endpoint for _, port := range svc.Spec.Ports { diff --git a/source/service_test.go b/source/service_test.go index 98b400fca..45737adb7 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -1518,6 +1518,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { expected: []*endpoint.Endpoint{ {DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}, RecordType: endpoint.RecordTypeA}, + {DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, }, nodes: []*v1.Node{{ ObjectMeta: metav1.ObjectMeta{ @@ -1527,6 +1528,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.1"}, {Type: v1.NodeInternalIP, Address: "10.0.1.1"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::1"}, }, }, }, { @@ -1537,6 +1539,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.2"}, {Type: v1.NodeInternalIP, Address: "10.0.1.2"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::2"}, }, }, }}, @@ -1559,6 +1562,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.1"}, {Type: v1.NodeInternalIP, Address: "10.0.1.1"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::1"}, }, }, }, { @@ -1569,6 +1573,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.2"}, {Type: v1.NodeInternalIP, Address: "10.0.1.2"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::2"}, }, }, }}, @@ -1584,6 +1589,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { expected: []*endpoint.Endpoint{ {DNSName: "_foo._tcp.foo.bar.example.com", Targets: endpoint.Targets{"0 50 30192 foo.bar.example.com"}, RecordType: endpoint.RecordTypeSRV}, {DNSName: "foo.bar.example.com", Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}, RecordType: endpoint.RecordTypeA}, + {DNSName: "foo.bar.example.com", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, }, nodes: []*v1.Node{{ ObjectMeta: metav1.ObjectMeta{ @@ -1593,6 +1599,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.1"}, {Type: v1.NodeInternalIP, Address: "10.0.1.1"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::1"}, }, }, }, { @@ -1603,6 +1610,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.2"}, {Type: v1.NodeInternalIP, Address: "10.0.1.2"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::2"}, }, }, }}, @@ -1619,6 +1627,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { expected: []*endpoint.Endpoint{ {DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"10.0.1.1", "10.0.1.2"}, RecordType: endpoint.RecordTypeA}, + {DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, }, nodes: []*v1.Node{{ ObjectMeta: metav1.ObjectMeta{ @@ -1627,6 +1636,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Status: v1.NodeStatus{ Addresses: []v1.NodeAddress{ {Type: v1.NodeInternalIP, Address: "10.0.1.1"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::1"}, }, }, }, { @@ -1636,6 +1646,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Status: v1.NodeStatus{ Addresses: []v1.NodeAddress{ {Type: v1.NodeInternalIP, Address: "10.0.1.2"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::2"}, }, }, }}, @@ -1652,6 +1663,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { expected: []*endpoint.Endpoint{ {DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.2"}, RecordType: endpoint.RecordTypeA}, + {DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, }, nodes: []*v1.Node{{ ObjectMeta: metav1.ObjectMeta{ @@ -1661,6 +1673,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.1"}, {Type: v1.NodeInternalIP, Address: "10.0.1.1"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::1"}, }, }, }, { @@ -1671,6 +1684,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.2"}, {Type: v1.NodeInternalIP, Address: "10.0.1.2"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::2"}, }, }, }}, @@ -1691,6 +1705,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { expected: []*endpoint.Endpoint{ {DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.2"}, RecordType: endpoint.RecordTypeA}, + {DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, }, nodes: []*v1.Node{{ ObjectMeta: metav1.ObjectMeta{ @@ -1700,6 +1715,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.1"}, {Type: v1.NodeInternalIP, Address: "10.0.1.1"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::1"}, }, }, }, { @@ -1710,6 +1726,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.2"}, {Type: v1.NodeInternalIP, Address: "10.0.1.2"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::2"}, }, }, }}, @@ -1731,6 +1748,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { expected: []*endpoint.Endpoint{ {DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"10.0.1.1", "10.0.1.2"}, RecordType: endpoint.RecordTypeA}, + {DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, }, nodes: []*v1.Node{{ ObjectMeta: metav1.ObjectMeta{ @@ -1740,6 +1758,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.1"}, {Type: v1.NodeInternalIP, Address: "10.0.1.1"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::1"}, }, }, }, { @@ -1750,6 +1769,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.2"}, {Type: v1.NodeInternalIP, Address: "10.0.1.2"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::2"}, }, }, }}, @@ -1768,6 +1788,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { expected: []*endpoint.Endpoint{ {DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}, RecordType: endpoint.RecordTypeA}, + {DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, }, nodes: []*v1.Node{{ ObjectMeta: metav1.ObjectMeta{ @@ -1777,6 +1798,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.1"}, {Type: v1.NodeInternalIP, Address: "10.0.1.1"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::1"}, }, }, }, { @@ -1787,6 +1809,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.2"}, {Type: v1.NodeInternalIP, Address: "10.0.1.2"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::2"}, }, }, }}, @@ -1804,7 +1827,9 @@ func TestServiceSourceNodePortServices(t *testing.T) { }, expected: []*endpoint.Endpoint{ {DNSName: "internal.foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.0.1.1"}}, + {DNSName: "internal.foo.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1"}}, {DNSName: "internal.bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.0.1.1"}}, + {DNSName: "internal.bar.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1"}}, }, nodes: []*v1.Node{{ ObjectMeta: metav1.ObjectMeta{ @@ -1817,6 +1842,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.1"}, {Type: v1.NodeInternalIP, Address: "10.0.1.1"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::1"}, }, }, }, { @@ -1830,6 +1856,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.2"}, {Type: v1.NodeInternalIP, Address: "10.0.1.2"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::2"}, }, }, }}, @@ -1846,7 +1873,9 @@ func TestServiceSourceNodePortServices(t *testing.T) { }, expected: []*endpoint.Endpoint{ {DNSName: "internal.foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.0.1.1", "10.0.1.2"}}, + {DNSName: "internal.foo.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}}, {DNSName: "internal.bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"10.0.1.1", "10.0.1.2"}}, + {DNSName: "internal.bar.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}}, }, nodes: []*v1.Node{{ ObjectMeta: metav1.ObjectMeta{ @@ -1859,6 +1888,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.1"}, {Type: v1.NodeInternalIP, Address: "10.0.1.1"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::1"}, }, }, }, { @@ -1872,6 +1902,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.2"}, {Type: v1.NodeInternalIP, Address: "10.0.1.2"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::2"}, }, }, }}, @@ -1888,7 +1919,9 @@ func TestServiceSourceNodePortServices(t *testing.T) { }, expected: []*endpoint.Endpoint{ {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}}, {DNSName: "bar.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}}, + {DNSName: "bar.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}}, }, nodes: []*v1.Node{{ ObjectMeta: metav1.ObjectMeta{ @@ -1901,6 +1934,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.1"}, {Type: v1.NodeInternalIP, Address: "10.0.1.1"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::1"}, }, }, }, { @@ -1914,6 +1948,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.2"}, {Type: v1.NodeInternalIP, Address: "10.0.1.2"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::2"}, }, }, }}, @@ -1942,6 +1977,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.1"}, {Type: v1.NodeInternalIP, Address: "10.0.1.1"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::1"}, }, }, }, { @@ -1955,6 +1991,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { Addresses: []v1.NodeAddress{ {Type: v1.NodeExternalIP, Address: "54.10.11.2"}, {Type: v1.NodeInternalIP, Address: "10.0.1.2"}, + {Type: v1.NodeInternalIP, Address: "2001:DB8::2"}, }, }, }}, diff --git a/source/shared_test.go b/source/shared_test.go index 9cdc58d22..11828dbe2 100644 --- a/source/shared_test.go +++ b/source/shared_test.go @@ -29,11 +29,14 @@ func sortEndpoints(endpoints []*endpoint.Endpoint) { sort.Strings([]string(ep.Targets)) } sort.Slice(endpoints, func(i, k int) bool { - // Sort by DNSName and Targets + // Sort by DNSName, RecordType, and Targets ei, ek := endpoints[i], endpoints[k] if ei.DNSName != ek.DNSName { return ei.DNSName < ek.DNSName } + if ei.RecordType != ek.RecordType { + return ei.RecordType < ek.RecordType + } // Targets are sorted ahead of time. for j, ti := range ei.Targets { if j >= len(ek.Targets) { From 7feb8d67e9173eee9f5114de31007a42a85f1e04 Mon Sep 17 00:00:00 2001 From: Arnaud Lefray Date: Tue, 9 May 2023 08:45:14 +0200 Subject: [PATCH 103/117] docs: remove product mention. --- docs/faq.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index adefacb81..43731a371 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -260,11 +260,11 @@ Sometimes you need to run an internal and an external dns service. The internal one should provision hostnames used on the internal network (perhaps inside a VPC), and the external one to expose DNS to the internet. To do this with ExternalDNS you can use the `--ingress-class` flag to specifically tie an instance of ExternalDNS to an instance of a ingress controller. -Let's assume you have two ingress controllers, `nginx-internal` and `nginx-external`. -You can then start two ExternalDNS providers, one with `--ingress-class=nginx-internal` and one with `--ingress-class=nginx-external`. +Let's assume you have two ingress controllers, `internal` and `external`. +You can then start two ExternalDNS providers, one with `--ingress-class=internal` and one with `--ingress-class=external`. If you need to search for multiple ingress classes, you can specify the flag multiple times, like so: -`--ingress-class=nginx-internal --ingress-class=alb-ingress-internal`. +`--ingress-class=internal --ingress-class=external`. The `--ingress-class` flag will check both the `spec.ingressClassName` field and the deprecated `kubernetes.io/ingress.class` annotation. The `spec.ingressClassName` tasks precedence over the annotation if both are supplied. From 7b940026bef96a78d37a841671cf2b6f69550145 Mon Sep 17 00:00:00 2001 From: Arnaud Lefray Date: Tue, 9 May 2023 08:46:56 +0200 Subject: [PATCH 104/117] docs(aws): remove mention of deprecated annotation filter --- docs/tutorials/aws.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/tutorials/aws.md b/docs/tutorials/aws.md index e749c16d7..f77f8017f 100644 --- a/docs/tutorials/aws.md +++ b/docs/tutorials/aws.md @@ -936,7 +936,6 @@ Running several fast polling ExternalDNS instances in a given account can easily * `--namespace=my-app` * `--label-filter=app in (my-app)` * `--ingress-class=nginx-external` - * `--annotation-filter=kubernetes.io/ingress.class in (nginx-external)` - note that this filter would apply to services too.. (deprecated in favor of `--ingress-class`) * Limit services watched by type (not applicable to ingress or other types) * `--service-type-filter=LoadBalancer` default `all` * Limit the hosted zones considered From 4745ddbb0eca0fc9d1571bb77c7aec148d662c99 Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Tue, 9 May 2023 19:42:56 -0700 Subject: [PATCH 105/117] Address review comment --- source/node.go | 9 ++------- source/pod.go | 9 ++------- source/source.go | 6 ++++++ 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/source/node.go b/source/node.go index 39135ceaf..5e287e9a0 100644 --- a/source/node.go +++ b/source/node.go @@ -76,11 +76,6 @@ func NewNodeSource(ctx context.Context, kubeClient kubernetes.Interface, annotat }, nil } -type endpointsKey struct { - dnsName string - recordType string -} - // Endpoints returns endpoint objects for each service that should be processed. func (ns *nodeSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) { nodes, err := ns.nodeInformer.Lister().List(labels.Everything()) @@ -93,7 +88,7 @@ func (ns *nodeSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, erro return nil, err } - endpoints := map[endpointsKey]*endpoint.Endpoint{} + endpoints := map[endpointKey]*endpoint.Endpoint{} // create endpoints for all nodes for _, node := range nodes { @@ -141,7 +136,7 @@ func (ns *nodeSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, erro ep.Labels = endpoint.NewLabels() for _, addr := range addrs { log.Debugf("adding endpoint %s target %s", ep, addr) - key := endpointsKey{ + key := endpointKey{ dnsName: ep.DNSName, recordType: suitableType(addr), } diff --git a/source/pod.go b/source/pod.go index 87772d75b..123468539 100644 --- a/source/pod.go +++ b/source/pod.go @@ -76,11 +76,6 @@ func NewPodSource(ctx context.Context, kubeClient kubernetes.Interface, namespac func (*podSource) AddEventHandler(ctx context.Context, handler func()) { } -type endpointKey struct { - domain string - recordType string -} - func (ps *podSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) { pods, err := ps.podInformer.Lister().Pods(ps.namespace).List(labels.Everything()) if err != nil { @@ -128,14 +123,14 @@ func (ps *podSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error } endpoints := []*endpoint.Endpoint{} for key, targets := range endpointMap { - endpoints = append(endpoints, endpoint.NewEndpoint(key.domain, key.recordType, targets...)) + endpoints = append(endpoints, endpoint.NewEndpoint(key.dnsName, key.recordType, targets...)) } return endpoints, nil } func addToEndpointMap(endpointMap map[endpointKey][]string, domain string, recordType string, address string) { key := endpointKey{ - domain: domain, + dnsName: domain, recordType: recordType, } if _, ok := endpointMap[key]; !ok { diff --git a/source/source.go b/source/source.go index 8573772c3..91b83bb4f 100644 --- a/source/source.go +++ b/source/source.go @@ -86,6 +86,12 @@ type Source interface { AddEventHandler(context.Context, func()) } +// endpointKey is the type of a map key for separating endpoints or targets. +type endpointKey struct { + dnsName string + recordType string +} + func getTTLFromAnnotations(annotations map[string]string) (endpoint.TTL, error) { ttlNotConfigured := endpoint.TTL(0) ttlAnnotation, exists := annotations[ttlAnnotationKey] From e03df8b50497a6977dd360dd49878a5c981bb9bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 May 2023 04:00:34 +0000 Subject: [PATCH 106/117] build(deps): bump alpine from 3.17 to 3.18 Bumps alpine from 3.17 to 3.18. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 55b72749d..8590ad633 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,7 +25,7 @@ RUN go mod download COPY . . -FROM alpine:3.17 +FROM alpine:3.18 RUN apk update && apk add "libcrypto3>=3.0.8-r1" "libssl3>=3.0.8-r1" && rm -rf /var/cache/apt/* From 41c705e471c8447a12965c8eea2d5e55bf270513 Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Sat, 6 May 2023 16:46:01 -0700 Subject: [PATCH 107/117] Support AAAA records from headless services --- source/service.go | 31 ++-- source/service_test.go | 373 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 385 insertions(+), 19 deletions(-) diff --git a/source/service.go b/source/service.go index 58270cdcc..7eac4c511 100644 --- a/source/service.go +++ b/source/service.go @@ -271,7 +271,7 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri endpointsType := getEndpointsTypeFromAnnotations(svc.Annotations) - targetsByHeadlessDomain := make(map[string]endpoint.Targets) + targetsByHeadlessDomainAndType := make(map[endpointKey]endpoint.Targets) for _, subset := range endpointsObject.Subsets { addresses := subset.Addresses if svc.Spec.PublishNotReadyAddresses || sc.alwaysPublishNotReadyAddresses { @@ -324,18 +324,29 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri log.Debugf("Generating matching endpoint %s with EndpointAddress IP %s", headlessDomain, address.IP) } } - targetsByHeadlessDomain[headlessDomain] = append(targetsByHeadlessDomain[headlessDomain], targets...) + for _, target := range targets { + key := endpointKey{ + dnsName: headlessDomain, + recordType: suitableType(target), + } + targetsByHeadlessDomainAndType[key] = append(targetsByHeadlessDomainAndType[key], target) + } } } } - headlessDomains := []string{} - for headlessDomain := range targetsByHeadlessDomain { - headlessDomains = append(headlessDomains, headlessDomain) + headlessKeys := []endpointKey{} + for headlessKey := range targetsByHeadlessDomainAndType { + headlessKeys = append(headlessKeys, headlessKey) } - sort.Strings(headlessDomains) - for _, headlessDomain := range headlessDomains { - allTargets := targetsByHeadlessDomain[headlessDomain] + sort.Slice(headlessKeys, func(i, j int) bool { + if headlessKeys[i].dnsName != headlessKeys[j].dnsName { + return headlessKeys[i].dnsName < headlessKeys[j].dnsName + } + return headlessKeys[i].recordType < headlessKeys[j].recordType + }) + for _, headlessKey := range headlessKeys { + allTargets := targetsByHeadlessDomainAndType[headlessKey] targets := []string{} deduppedTargets := map[string]struct{}{} @@ -350,9 +361,9 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri } if ttl.IsConfigured() { - endpoints = append(endpoints, endpoint.NewEndpointWithTTL(headlessDomain, endpoint.RecordTypeA, ttl, targets...)) + endpoints = append(endpoints, endpoint.NewEndpointWithTTL(headlessKey.dnsName, headlessKey.recordType, ttl, targets...)) } else { - endpoints = append(endpoints, endpoint.NewEndpoint(headlessDomain, endpoint.RecordTypeA, targets...)) + endpoints = append(endpoints, endpoint.NewEndpoint(headlessKey.dnsName, headlessKey.recordType, targets...)) } } diff --git a/source/service_test.go b/source/service_test.go index 45737adb7..66ef2a830 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -2118,7 +2118,7 @@ func TestHeadlessServices(t *testing.T) { expectError bool }{ { - "annotated Headless services return endpoints for each selected Pod", + "annotated Headless services return IPv4 endpoints for each selected Pod", "", "testing", "foo", @@ -2150,6 +2150,39 @@ func TestHeadlessServices(t *testing.T) { }, false, }, + { + "annotated Headless services return IPv6 endpoints for each selected Pod", + "", + "testing", + "foo", + v1.ServiceTypeClusterIP, + "", + "", + false, + map[string]string{"component": "foo"}, + map[string]string{ + hostnameAnnotationKey: "service.example.org", + }, + map[string]string{}, + v1.ClusterIPNone, + []string{"2001:db8::1", "2001:db8::2"}, + []string{"", ""}, + map[string]string{ + "component": "foo", + }, + []string{}, + []string{"foo-0", "foo-1"}, + []string{"foo-0", "foo-1"}, + []bool{true, true}, + false, + []v1.Node{}, + []*endpoint.Endpoint{ + {DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1"}}, + {DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::2"}}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}}, + }, + false, + }, { "hostname annotated Headless services are ignored", "", @@ -2180,7 +2213,7 @@ func TestHeadlessServices(t *testing.T) { false, }, { - "annotated Headless services return endpoints with TTL for each selected Pod", + "annotated Headless services return IPv4 endpoints with TTL for each selected Pod", "", "testing", "foo", @@ -2213,6 +2246,40 @@ func TestHeadlessServices(t *testing.T) { }, false, }, + { + "annotated Headless services return IPv6 endpoints with TTL for each selected Pod", + "", + "testing", + "foo", + v1.ServiceTypeClusterIP, + "", + "", + false, + map[string]string{"component": "foo"}, + map[string]string{ + hostnameAnnotationKey: "service.example.org", + ttlAnnotationKey: "1", + }, + map[string]string{}, + v1.ClusterIPNone, + []string{"2001:db8::1", "2001:db8::2"}, + []string{"", ""}, + map[string]string{ + "component": "foo", + }, + []string{}, + []string{"foo-0", "foo-1"}, + []string{"foo-0", "foo-1"}, + []bool{true, true}, + false, + []v1.Node{}, + []*endpoint.Endpoint{ + {DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1"}, RecordTTL: endpoint.TTL(1)}, + {DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::2"}, RecordTTL: endpoint.TTL(1)}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}, RecordTTL: endpoint.TTL(1)}, + }, + false, + }, { "annotated Headless services return endpoints for each selected Pod, which are in running state", "", @@ -2310,7 +2377,7 @@ func TestHeadlessServices(t *testing.T) { false, }, { - "annotated Headless services return only a unique set of targets", + "annotated Headless services return only a unique set of IPv4 targets", "", "testing", "foo", @@ -2341,7 +2408,38 @@ func TestHeadlessServices(t *testing.T) { false, }, { - "annotated Headless services return targets from pod annotation", + "annotated Headless services return only a unique set of IPv6 targets", + "", + "testing", + "foo", + v1.ServiceTypeClusterIP, + "", + "", + false, + map[string]string{"component": "foo"}, + map[string]string{ + hostnameAnnotationKey: "service.example.org", + }, + map[string]string{}, + v1.ClusterIPNone, + []string{"2001:db8::1", "2001:db8::1", "2001:db8::2"}, + []string{"", "", ""}, + map[string]string{ + "component": "foo", + }, + []string{}, + []string{"foo-0", "foo-1", "foo-3"}, + []string{"", "", ""}, + []bool{true, true, true}, + false, + []v1.Node{}, + []*endpoint.Endpoint{ + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}}, + }, + false, + }, + { + "annotated Headless services return IPv4 targets from pod annotation", "", "testing", "foo", @@ -2374,7 +2472,40 @@ func TestHeadlessServices(t *testing.T) { false, }, { - "annotated Headless services return targets from node external IP if endpoints-type annotation is set", + "annotated Headless services return IPv6 targets from pod annotation", + "", + "testing", + "foo", + v1.ServiceTypeClusterIP, + "", + "", + false, + map[string]string{"component": "foo"}, + map[string]string{ + hostnameAnnotationKey: "service.example.org", + }, + map[string]string{ + targetAnnotationKey: "2001:db8::4", + }, + v1.ClusterIPNone, + []string{"2001:db8::1"}, + []string{""}, + map[string]string{ + "component": "foo", + }, + []string{}, + []string{"foo"}, + []string{"", "", ""}, + []bool{true, true, true}, + false, + []v1.Node{}, + []*endpoint.Endpoint{ + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}}, + }, + false, + }, + { + "annotated Headless services return IPv4 targets from node external IP if endpoints-type annotation is set", "", "testing", "foo", @@ -2417,7 +2548,98 @@ func TestHeadlessServices(t *testing.T) { false, }, { - "annotated Headless services return targets from hostIP if endpoints-type annotation is set", + "annotated Headless services return IPv6 targets from node external IP if endpoints-type annotation is set", + "", + "testing", + "foo", + v1.ServiceTypeClusterIP, + "", + "", + false, + map[string]string{"component": "foo"}, + map[string]string{ + hostnameAnnotationKey: "service.example.org", + endpointsTypeAnnotationKey: EndpointsTypeNodeExternalIP, + }, + map[string]string{}, + v1.ClusterIPNone, + []string{"2001:db8::1"}, + []string{""}, + map[string]string{ + "component": "foo", + }, + []string{}, + []string{"foo"}, + []string{"", "", ""}, + []bool{true, true, true}, + false, + []v1.Node{ + { + Status: v1.NodeStatus{ + Addresses: []v1.NodeAddress{ + { + Type: v1.NodeInternalIP, + Address: "2001:db8::4", + }, + }, + }, + }, + }, + []*endpoint.Endpoint{ + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}}, + }, + false, + }, + { + "annotated Headless services return dual-stack targets from node external IP if endpoints-type annotation is set", + "", + "testing", + "foo", + v1.ServiceTypeClusterIP, + "", + "", + false, + map[string]string{"component": "foo"}, + map[string]string{ + hostnameAnnotationKey: "service.example.org", + endpointsTypeAnnotationKey: EndpointsTypeNodeExternalIP, + }, + map[string]string{}, + v1.ClusterIPNone, + []string{"1.1.1.1"}, + []string{""}, + map[string]string{ + "component": "foo", + }, + []string{}, + []string{"foo"}, + []string{"", "", ""}, + []bool{true, true, true}, + false, + []v1.Node{ + { + Status: v1.NodeStatus{ + Addresses: []v1.NodeAddress{ + { + Type: v1.NodeExternalIP, + Address: "1.2.3.4", + }, + { + Type: v1.NodeInternalIP, + Address: "2001:db8::4", + }, + }, + }, + }, + }, + []*endpoint.Endpoint{ + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}}, + }, + false, + }, + { + "annotated Headless services return IPv4 targets from hostIP if endpoints-type annotation is set", "", "testing", "foo", @@ -2448,6 +2670,38 @@ func TestHeadlessServices(t *testing.T) { }, false, }, + { + "annotated Headless services return IPv6 targets from hostIP if endpoints-type annotation is set", + "", + "testing", + "foo", + v1.ServiceTypeClusterIP, + "", + "", + false, + map[string]string{"component": "foo"}, + map[string]string{ + hostnameAnnotationKey: "service.example.org", + endpointsTypeAnnotationKey: EndpointsTypeHostIP, + }, + map[string]string{}, + v1.ClusterIPNone, + []string{"2001:db8::1"}, + []string{"2001:db8::4"}, + map[string]string{ + "component": "foo", + }, + []string{}, + []string{"foo"}, + []string{"", "", ""}, + []bool{true, true, true}, + false, + []v1.Node{}, + []*endpoint.Endpoint{ + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::4"}}, + }, + false, + }, } { tc := tc t.Run(tc.title, func(t *testing.T) { @@ -2590,7 +2844,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { expectError bool }{ { - "annotated Headless services return endpoints for each selected Pod", + "annotated Headless services return IPv4 endpoints for each selected Pod", "", "testing", "foo", @@ -2623,6 +2877,40 @@ func TestHeadlessServicesHostIP(t *testing.T) { }, false, }, + { + "annotated Headless services return IPv6 endpoints for each selected Pod", + "", + "testing", + "foo", + v1.ServiceTypeClusterIP, + "", + "", + false, + map[string]string{"component": "foo"}, + map[string]string{ + hostnameAnnotationKey: "service.example.org", + }, + v1.ClusterIPNone, + []string{"2001:db8::1", "2001:db8::2"}, + map[string]string{ + "component": "foo", + }, + []string{}, + []string{"foo-0", "foo-1"}, + []string{"foo-0", "foo-1"}, + []bool{true, true}, + []*v1.ObjectReference{ + {APIVersion: "", Kind: "Pod", Name: "foo-0"}, + {APIVersion: "", Kind: "Pod", Name: "foo-1"}, + }, + false, + []*endpoint.Endpoint{ + {DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1"}}, + {DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::2"}}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}}, + }, + false, + }, { "hostname annotated Headless services are ignored", "", @@ -2654,7 +2942,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { false, }, { - "annotated Headless services return endpoints with TTL for each selected Pod", + "annotated Headless services return IPv4 endpoints with TTL for each selected Pod", "", "testing", "foo", @@ -2688,6 +2976,41 @@ func TestHeadlessServicesHostIP(t *testing.T) { }, false, }, + { + "annotated Headless services return IPv6 endpoints with TTL for each selected Pod", + "", + "testing", + "foo", + v1.ServiceTypeClusterIP, + "", + "", + false, + map[string]string{"component": "foo"}, + map[string]string{ + hostnameAnnotationKey: "service.example.org", + ttlAnnotationKey: "1", + }, + v1.ClusterIPNone, + []string{"2001:db8::1", "2001:db8::2"}, + map[string]string{ + "component": "foo", + }, + []string{}, + []string{"foo-0", "foo-1"}, + []string{"foo-0", "foo-1"}, + []bool{true, true}, + []*v1.ObjectReference{ + {APIVersion: "", Kind: "Pod", Name: "foo-0"}, + {APIVersion: "", Kind: "Pod", Name: "foo-1"}, + }, + false, + []*endpoint.Endpoint{ + {DNSName: "foo-0.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1"}, RecordTTL: endpoint.TTL(1)}, + {DNSName: "foo-1.service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::2"}, RecordTTL: endpoint.TTL(1)}, + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}, RecordTTL: endpoint.TTL(1)}, + }, + false, + }, { "annotated Headless services return endpoints for each selected Pod, which are in running state", "", @@ -2756,7 +3079,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { false, }, { - "annotated Headless services return endpoints for pods missing hostname", + "annotated Headless services return IPv4 endpoints for pods missing hostname", "", "testing", "foo", @@ -2787,6 +3110,38 @@ func TestHeadlessServicesHostIP(t *testing.T) { }, false, }, + { + "annotated Headless services return IPv6 endpoints for pods missing hostname", + "", + "testing", + "foo", + v1.ServiceTypeClusterIP, + "", + "", + false, + map[string]string{"component": "foo"}, + map[string]string{ + hostnameAnnotationKey: "service.example.org", + }, + v1.ClusterIPNone, + []string{"2001:db8::1", "2001:db8::2"}, + map[string]string{ + "component": "foo", + }, + []string{}, + []string{"foo-0", "foo-1"}, + []string{"", ""}, + []bool{true, true}, + []*v1.ObjectReference{ + {APIVersion: "", Kind: "Pod", Name: "foo-0"}, + {APIVersion: "", Kind: "Pod", Name: "foo-1"}, + }, + false, + []*endpoint.Endpoint{ + {DNSName: "service.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:db8::1", "2001:db8::2"}}, + }, + false, + }, { "annotated Headless services without a targetRef has no endpoints", "", From 47a0f74f61c85a6899487558d3c04c26b23c875b Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Sat, 6 May 2023 16:59:39 -0700 Subject: [PATCH 108/117] Add test for IPv6 ExternalName service --- source/service_test.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/source/service_test.go b/source/service_test.go index 66ef2a830..7f3255dd5 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -3295,7 +3295,7 @@ func TestExternalServices(t *testing.T) { expectError bool }{ { - "external services return an A endpoint for the external name that is an IP address", + "external services return an A endpoint for the external name that is an IPv4 address", "", "testing", "foo", @@ -3313,6 +3313,25 @@ func TestExternalServices(t *testing.T) { }, false, }, + { + "external services return an AAAA endpoint for the external name that is an IPv6 address", + "", + "testing", + "foo", + v1.ServiceTypeExternalName, + "", + "", + false, + map[string]string{"component": "foo"}, + map[string]string{ + hostnameAnnotationKey: "service.example.org", + }, + "2001:db8::111", + []*endpoint.Endpoint{ + {DNSName: "service.example.org", Targets: endpoint.Targets{"2001:db8::111"}, RecordType: endpoint.RecordTypeAAAA}, + }, + false, + }, { "external services return a CNAME endpoint for the external name that is a domain", "", From 140444749b7e9a3e43b1acbf00f4b2b4b25c2c9a Mon Sep 17 00:00:00 2001 From: Gabriel Martinez Date: Thu, 11 May 2023 11:44:36 +0100 Subject: [PATCH 109/117] remove comment from ingress --- docs/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.md b/docs/faq.md index a0104a0bf..044ee7aa8 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -66,7 +66,7 @@ Regarding Ingress, we'll support: ### Are other Ingress Controllers supported? -For Ingress objects, ExternalDNS will attempt to discover the target hostname of the relevant Ingress Controller automatically. If you are using an Ingress Controller that is not listed above you may have issues with ExternalDNS not discovering Endpoints and consequently not creating any DNS records. As a workaround, it is possible to force create an Endpoint by manually specifying a target host/IP for the records to be created by setting the annotation `external-dns.alpha.kubernetes.io/target` in the Ingress object. Note that services also support this annotation. +For Ingress objects, ExternalDNS will attempt to discover the target hostname of the relevant Ingress Controller automatically. If you are using an Ingress Controller that is not listed above you may have issues with ExternalDNS not discovering Endpoints and consequently not creating any DNS records. As a workaround, it is possible to force create an Endpoint by manually specifying a target host/IP for the records to be created by setting the annotation `external-dns.alpha.kubernetes.io/target` in the Ingress object. Another reason you may want to override the ingress hostname or IP address is if you have an external mechanism for handling failover across ingress endpoints. Possible scenarios for this would include using [keepalived-vip](https://github.com/kubernetes/contrib/tree/HEAD/keepalived-vip) to manage failover faster than DNS TTLs might expire. From 599590c1410bd64b6ea302f8d167b2f1ce988657 Mon Sep 17 00:00:00 2001 From: Gabriel Martinez Date: Thu, 11 May 2023 12:05:54 +0100 Subject: [PATCH 110/117] add AAAA tests --- source/service_test.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/source/service_test.go b/source/service_test.go index 970ae2845..0dd2983ba 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -1389,6 +1389,20 @@ func TestClusterIpServices(t *testing.T) { {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"bar.example.org"}}, }, }, + { + title: "target annotated ClusterIp services return an endpoint with the specified AAAA", + svcNamespace: "testing", + svcName: "foo", + svcType: v1.ServiceTypeClusterIP, + annotations: map[string]string{ + hostnameAnnotationKey: "foo.example.org.", + targetAnnotationKey: "2001:DB8::1", + }, + clusterIP: "1.2.3.4", + expected: []*endpoint.Endpoint{ + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1"}}, + }, + }, { title: "multiple target annotated ClusterIp services return an endpoint with the specified CNAMES", svcNamespace: "testing", @@ -1403,6 +1417,21 @@ func TestClusterIpServices(t *testing.T) { {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"bar.example.org", "baz.example.org"}}, }, }, + { + title: "multiple target annotated ClusterIp services return two endpoints with the specified CNAMES and AAAA", + svcNamespace: "testing", + svcName: "foo", + svcType: v1.ServiceTypeClusterIP, + annotations: map[string]string{ + hostnameAnnotationKey: "foo.example.org.", + targetAnnotationKey: "bar.example.org.,baz.example.org.,2001:DB8::1", + }, + clusterIP: "1.2.3.4", + expected: []*endpoint.Endpoint{ + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeCNAME, Targets: endpoint.Targets{"bar.example.org", "baz.example.org"}}, + {DNSName: "foo.example.org", RecordType: endpoint.RecordTypeAAAA, Targets: endpoint.Targets{"2001:DB8::1"}}, + }, + }, { title: "hostname annotated ClusterIp services are ignored", svcNamespace: "testing", From f385139abcfb2ea4ed1ea19df18720f078cabb3a Mon Sep 17 00:00:00 2001 From: Antoine Bardoux Date: Fri, 12 May 2023 12:16:08 +0200 Subject: [PATCH 111/117] Allow multiple hostnames in internal annotation for pods --- source/pod.go | 51 +++++++++++++++++++++++++++++----------------- source/pod_test.go | 40 ++++++++++++++++++++++++++++++++++++ source/source.go | 8 ++++++-- 3 files changed, 78 insertions(+), 21 deletions(-) diff --git a/source/pod.go b/source/pod.go index 123468539..912879b95 100644 --- a/source/pod.go +++ b/source/pod.go @@ -89,27 +89,17 @@ func (ps *podSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error continue } - if domain, ok := pod.Annotations[internalHostnameAnnotationKey]; ok { - addToEndpointMap(endpointMap, domain, suitableType(pod.Status.PodIP), pod.Status.PodIP) - } - - if domain, ok := pod.Annotations[hostnameAnnotationKey]; ok { - node, _ := ps.nodeInformer.Lister().Get(pod.Spec.NodeName) - for _, address := range node.Status.Addresses { - recordType := suitableType(address.Address) - // IPv6 addresses are labeled as NodeInternalIP despite being usable externally as well. - if address.Type == corev1.NodeExternalIP || (address.Type == corev1.NodeInternalIP && recordType == endpoint.RecordTypeAAAA) { - addToEndpointMap(endpointMap, domain, recordType, address.Address) - } - } - } - - if ps.compatibility == "kops-dns-controller" { - if domain, ok := pod.Annotations[kopsDNSControllerInternalHostnameAnnotationKey]; ok { + if domainAnnotation, ok := pod.Annotations[internalHostnameAnnotationKey]; ok { + domainList := splitHostnameAnnotation(domainAnnotation) + for _, domain := range domainList { addToEndpointMap(endpointMap, domain, suitableType(pod.Status.PodIP), pod.Status.PodIP) - } - if domain, ok := pod.Annotations[kopsDNSControllerHostnameAnnotationKey]; ok { + } + } + + if domainAnnotation, ok := pod.Annotations[hostnameAnnotationKey]; ok { + domainList := splitHostnameAnnotation(domainAnnotation) + for _, domain := range domainList { node, _ := ps.nodeInformer.Lister().Get(pod.Spec.NodeName) for _, address := range node.Status.Addresses { recordType := suitableType(address.Address) @@ -120,6 +110,29 @@ func (ps *podSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error } } } + + if ps.compatibility == "kops-dns-controller" { + if domainAnnotation, ok := pod.Annotations[kopsDNSControllerInternalHostnameAnnotationKey]; ok { + domainList := splitHostnameAnnotation(domainAnnotation) + for _, domain := range domainList { + addToEndpointMap(endpointMap, domain, suitableType(pod.Status.PodIP), pod.Status.PodIP) + } + } + + if domainAnnotation, ok := pod.Annotations[kopsDNSControllerHostnameAnnotationKey]; ok { + domainList := splitHostnameAnnotation(domainAnnotation) + for _, domain := range domainList { + node, _ := ps.nodeInformer.Lister().Get(pod.Spec.NodeName) + for _, address := range node.Status.Addresses { + recordType := suitableType(address.Address) + // IPv6 addresses are labeled as NodeInternalIP despite being usable externally as well. + if address.Type == corev1.NodeExternalIP || (address.Type == corev1.NodeInternalIP && recordType == endpoint.RecordTypeAAAA) { + addToEndpointMap(endpointMap, domain, recordType, address.Address) + } + } + } + } + } } endpoints := []*endpoint.Endpoint{} for key, targets := range endpointMap { diff --git a/source/pod_test.go b/source/pod_test.go index 5a57aedc2..549a9ebf9 100644 --- a/source/pod_test.go +++ b/source/pod_test.go @@ -526,6 +526,46 @@ func TestPodSource(t *testing.T) { }, }, }, + { + "split record for internal hostname annotation", + "", + "", + []*endpoint.Endpoint{ + {DNSName: "internal.a.foo.example.org", Targets: endpoint.Targets{"10.0.1.1"}, RecordType: endpoint.RecordTypeA}, + {DNSName: "internal.b.foo.example.org", Targets: endpoint.Targets{"10.0.1.1"}, RecordType: endpoint.RecordTypeA}, + }, + false, + []*corev1.Node{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-node1", + }, + Status: corev1.NodeStatus{ + Addresses: []corev1.NodeAddress{ + {Type: corev1.NodeInternalIP, Address: "10.0.1.1"}, + }, + }, + }, + }, + []*corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-pod1", + Namespace: "kube-system", + Annotations: map[string]string{ + internalHostnameAnnotationKey: "internal.a.foo.example.org,internal.b.foo.example.org", + }, + }, + Spec: corev1.PodSpec{ + HostNetwork: true, + NodeName: "my-node1", + }, + Status: corev1.PodStatus{ + PodIP: "10.0.1.1", + }, + }, + }, + }, } { tc := tc t.Run(tc.title, func(t *testing.T) { diff --git a/source/source.go b/source/source.go index 91b83bb4f..e63c511ba 100644 --- a/source/source.go +++ b/source/source.go @@ -157,7 +157,7 @@ func getHostnamesFromAnnotations(annotations map[string]string) []string { if !exists { return nil } - return strings.Split(strings.Replace(hostnameAnnotation, " ", "", -1), ",") + return splitHostnameAnnotation(hostnameAnnotation) } func getAccessFromAnnotations(annotations map[string]string) string { @@ -173,7 +173,11 @@ func getInternalHostnamesFromAnnotations(annotations map[string]string) []string if !exists { return nil } - return strings.Split(strings.Replace(internalHostnameAnnotation, " ", "", -1), ",") + return splitHostnameAnnotation(internalHostnameAnnotation) +} + +func splitHostnameAnnotation(annotation string) []string { + return strings.Split(strings.Replace(annotation, " ", "", -1), ",") } func getAliasFromAnnotations(annotations map[string]string) bool { From 65b1fd71313f10ee6f3fc355c17292a1e59f069c Mon Sep 17 00:00:00 2001 From: Maksym Pylypenko Date: Fri, 12 May 2023 15:37:07 +0300 Subject: [PATCH 112/117] Add Canonical Hosted Zone IDs for ELB/NLB in eu-south-2 (Spain) AWS region --- provider/aws/aws.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/provider/aws/aws.go b/provider/aws/aws.go index 5f7457420..935edffd3 100644 --- a/provider/aws/aws.go +++ b/provider/aws/aws.go @@ -86,6 +86,7 @@ var canonicalHostedZones = map[string]string{ "eu-west-3.elb.amazonaws.com": "Z3Q77PNBQS71R4", "eu-north-1.elb.amazonaws.com": "Z23TAZ6LKFMNIO", "eu-south-1.elb.amazonaws.com": "Z3ULH7SSC9OV64", + "eu-south-2.elb.amazonaws.com": "Z0956581394HF5D5LXGAP", "sa-east-1.elb.amazonaws.com": "Z2P70J7HTTTPLU", "cn-north-1.elb.amazonaws.com.cn": "Z1GDH35T77C1KE", "cn-northwest-1.elb.amazonaws.com.cn": "ZM7IZAIOVVDZF", @@ -115,6 +116,7 @@ var canonicalHostedZones = map[string]string{ "elb.eu-west-3.amazonaws.com": "Z1CMS0P5QUZ6D5", "elb.eu-north-1.amazonaws.com": "Z1UDT6IFJ4EJM", "elb.eu-south-1.amazonaws.com": "Z23146JA1KNAFP", + "elb.eu-south-2.amazonaws.com": "Z1011216NVTVYADP1SSV", "elb.sa-east-1.amazonaws.com": "ZTK26PT1VY4CU", "elb.cn-north-1.amazonaws.com.cn": "Z3QFB96KMJ7ED6", "elb.cn-northwest-1.amazonaws.com.cn": "ZQEIKTCZ8352D", From a7992331251983e9566c0dcc817b75822a80dfab Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Fri, 12 May 2023 08:20:52 -0700 Subject: [PATCH 113/117] Refactor route53 test initial state Makes tests cover errors in conversion between endpoints and records and allows testing of records that can't be generated from an endpoint. --- provider/aws/aws_test.go | 618 ++++++++++++++++++++++++++++++++------- 1 file changed, 505 insertions(+), 113 deletions(-) diff --git a/provider/aws/aws_test.go b/provider/aws/aws_test.go index 91ceb8a18..84d63138c 100644 --- a/provider/aws/aws_test.go +++ b/provider/aws/aws_test.go @@ -304,7 +304,7 @@ func TestAWSZones(t *testing.T) { {"zone id filter", provider.NewZoneIDFilter([]string{"/hostedzone/zone-3.ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneTypeFilter(""), provider.NewZoneTagFilter([]string{}), privateZones}, {"tag filter", provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), provider.NewZoneTagFilter([]string{"zone=3"}), privateZones}, } { - provider, _ := newAWSProviderWithTagFilter(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), ti.zoneIDFilter, ti.zoneTypeFilter, ti.zoneTagFilter, defaultEvaluateTargetHealth, false, []*endpoint.Endpoint{}) + provider, _ := newAWSProviderWithTagFilter(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), ti.zoneIDFilter, ti.zoneTypeFilter, ti.zoneTagFilter, defaultEvaluateTargetHealth, false, nil) zones, err := provider.Zones(context.Background()) require.NoError(t, err) @@ -337,26 +337,158 @@ func TestAWSRecordsFilter(t *testing.T) { } func TestAWSRecords(t *testing.T) { - provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), false, false, []*endpoint.Endpoint{ - endpoint.NewEndpointWithTTL("list-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4"), - endpoint.NewEndpointWithTTL("list-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8"), - endpoint.NewEndpointWithTTL("*.wildcard-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8"), - endpoint.NewEndpoint("list-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false"), - endpoint.NewEndpoint("*.wildcard-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false"), - endpoint.NewEndpoint("list-test-alias-evaluate.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true"), - endpoint.NewEndpointWithTTL("list-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8", "8.8.4.4"), - endpoint.NewEndpointWithTTL("prefix-*.wildcard.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "random"), - endpoint.NewEndpointWithTTL("weight-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificWeight, "10"), - endpoint.NewEndpointWithTTL("weight-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificWeight, "20"), - endpoint.NewEndpointWithTTL("latency-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificRegion, "us-east-1"), - endpoint.NewEndpointWithTTL("failover-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificFailover, "PRIMARY"), - endpoint.NewEndpointWithTTL("multi-value-answer-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificMultiValueAnswer, ""), - endpoint.NewEndpointWithTTL("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificGeolocationContinentCode, "EU"), - endpoint.NewEndpointWithTTL("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificGeolocationCountryCode, "DE"), - endpoint.NewEndpointWithTTL("geolocation-subdivision-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificGeolocationSubdivisionCode, "NY"), - endpoint.NewEndpoint("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "foo.example.com").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificWeight, "10").WithProviderSpecific(providerSpecificHealthCheckID, "foo-bar-healthcheck-id"), - endpoint.NewEndpointWithTTL("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificWeight, "20").WithProviderSpecific(providerSpecificHealthCheckID, "abc-def-healthcheck-id"), - endpoint.NewEndpointWithTTL("mail.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 mailhost1.example.com", "20 mailhost2.example.com"), + provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), false, false, []*route53.ResourceRecordSet{ + { + Name: aws.String("list-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + }, + { + Name: aws.String("list-test.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.8.8")}}, + }, + { + Name: aws.String("*.wildcard-test.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.8.8")}}, + }, + { + Name: aws.String("list-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + AliasTarget: &route53.AliasTarget{ + DNSName: aws.String("foo.eu-central-1.elb.amazonaws.com."), + EvaluateTargetHealth: aws.Bool(false), + HostedZoneId: aws.String("Z215JYRZR1TBD5"), + }, + }, + { + Name: aws.String("*.wildcard-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + AliasTarget: &route53.AliasTarget{ + DNSName: aws.String("foo.eu-central-1.elb.amazonaws.com."), + EvaluateTargetHealth: aws.Bool(false), + HostedZoneId: aws.String("Z215JYRZR1TBD5"), + }, + }, + { + Name: aws.String("list-test-alias-evaluate.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + AliasTarget: &route53.AliasTarget{ + DNSName: aws.String("foo.eu-central-1.elb.amazonaws.com."), + EvaluateTargetHealth: aws.Bool(true), + HostedZoneId: aws.String("Z215JYRZR1TBD5"), + }, + }, + { + Name: aws.String("list-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.8.8")}, {Value: aws.String("8.8.4.4")}}, + }, + { + Name: aws.String("prefix-*.wildcard.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeTxt), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("random")}}, + }, + { + Name: aws.String("weight-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + SetIdentifier: aws.String("test-set-1"), + Weight: aws.Int64(10), + }, + { + Name: aws.String("weight-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("4.3.2.1")}}, + SetIdentifier: aws.String("test-set-2"), + Weight: aws.Int64(20), + }, + { + Name: aws.String("latency-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + SetIdentifier: aws.String("test-set"), + Region: aws.String("us-east-1"), + }, + { + Name: aws.String("failover-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + SetIdentifier: aws.String("test-set"), + Failover: aws.String("PRIMARY"), + }, + { + Name: aws.String("multi-value-answer-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + SetIdentifier: aws.String("test-set"), + MultiValueAnswer: aws.Bool(true), + }, + { + Name: aws.String("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + SetIdentifier: aws.String("test-set-1"), + GeoLocation: &route53.GeoLocation{ + ContinentCode: aws.String("EU"), + }, + }, + { + Name: aws.String("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("4.3.2.1")}}, + SetIdentifier: aws.String("test-set-2"), + GeoLocation: &route53.GeoLocation{ + CountryCode: aws.String("DE"), + }, + }, + { + Name: aws.String("geolocation-subdivision-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + SetIdentifier: aws.String("test-set-1"), + GeoLocation: &route53.GeoLocation{ + SubdivisionCode: aws.String("NY"), + }, + }, + { + Name: aws.String("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("foo.example.com")}}, + SetIdentifier: aws.String("test-set-1"), + HealthCheckId: aws.String("foo-bar-healthcheck-id"), + Weight: aws.Int64(10), + }, + { + Name: aws.String("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("4.3.2.1")}}, + SetIdentifier: aws.String("test-set-2"), + HealthCheckId: aws.String("abc-def-healthcheck-id"), + Weight: aws.Int64(20), + }, + { + Name: aws.String("mail.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeMx), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("10 mailhost1.example.com")}, {Value: aws.String("20 mailhost2.example.com")}}, + }, }) records, err := provider.Records(context.Background()) @@ -386,7 +518,7 @@ func TestAWSRecords(t *testing.T) { } func TestAWSAdjustEndpoints(t *testing.T) { - provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, []*endpoint.Endpoint{}) + provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, nil) records := []*endpoint.Endpoint{ endpoint.NewEndpoint("a-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.8.8"), @@ -411,7 +543,7 @@ func TestAWSAdjustEndpoints(t *testing.T) { func TestAWSCreateRecords(t *testing.T) { customTTL := endpoint.TTL(60) - provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, []*endpoint.Endpoint{}) + provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, nil) records := []*endpoint.Endpoint{ endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4"), @@ -442,13 +574,43 @@ func TestAWSCreateRecords(t *testing.T) { } func TestAWSUpdateRecords(t *testing.T) { - provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, []*endpoint.Endpoint{ - endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8"), - endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.4.4"), - endpoint.NewEndpointWithTTL("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.1.1.1"), - endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "foo.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("create-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8", "8.8.4.4"), - endpoint.NewEndpointWithTTL("update-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 mailhost1.foo.elb.amazonaws.com"), + provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, []*route53.ResourceRecordSet{ + { + Name: aws.String("update-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.8.8")}}, + }, + { + Name: aws.String("update-test.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.4.4")}}, + }, + { + Name: aws.String("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.1.1.1")}}, + }, + { + Name: aws.String("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("foo.elb.amazonaws.com")}}, + }, + { + Name: aws.String("create-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.8.8")}, {Value: aws.String("8.8.4.4")}}, + }, + { + Name: aws.String("update-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeMx), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("10 mailhost1.foo.elb.amazonaws.com")}}, + }, }) currentRecords := []*endpoint.Endpoint{ @@ -484,7 +646,67 @@ func TestAWSUpdateRecords(t *testing.T) { } func TestAWSDeleteRecords(t *testing.T) { - originalEndpoints := []*endpoint.Endpoint{ + provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), false, false, []*route53.ResourceRecordSet{ + { + Name: aws.String("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.8.8")}}, + }, + { + Name: aws.String("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.8.8")}}, + }, + { + Name: aws.String("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("baz.elb.amazonaws.com")}}, + }, + { + Name: aws.String("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + AliasTarget: &route53.AliasTarget{ + DNSName: aws.String("foo.eu-central-1.elb.amazonaws.com."), + EvaluateTargetHealth: aws.Bool(false), + HostedZoneId: aws.String("Z215JYRZR1TBD5"), + }, + }, + { + Name: aws.String("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + AliasTarget: &route53.AliasTarget{ + DNSName: aws.String("foo.eu-central-1.elb.amazonaws.com."), + EvaluateTargetHealth: aws.Bool(true), + HostedZoneId: aws.String("Z215JYRZR1TBD5"), + }, + }, + { + Name: aws.String("delete-test-cname-alias.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + AliasTarget: &route53.AliasTarget{ + DNSName: aws.String("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do."), + EvaluateTargetHealth: aws.Bool(true), + HostedZoneId: aws.String("zone-2.ext-dns-test-2.teapot.zalan.do."), + }, + }, + { + Name: aws.String("delete-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.8.8")}, {Value: aws.String("8.8.4.4")}}, + }, + { + Name: aws.String("delete-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeMx), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("10 mailhost1.foo.elb.amazonaws.com")}, {Value: aws.String("20 mailhost2.foo.elb.amazonaws.com")}}, + }, + }) + + require.NoError(t, provider.DeleteRecords(context.Background(), []*endpoint.Endpoint{ endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4"), endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8"), endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "baz.elb.amazonaws.com"), @@ -493,11 +715,7 @@ func TestAWSDeleteRecords(t *testing.T) { endpoint.NewEndpoint("delete-test-cname-alias.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "delete-test.zone-2.ext-dns-test-2.teapot.zalan.do").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true").WithProviderSpecific(providerSpecificAlias, "true").WithProviderSpecific(providerSpecificTargetHostedZone, "/hostedzone/zone-2.ext-dns-test-2.teapot.zalan.do."), endpoint.NewEndpointWithTTL("delete-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8", "8.8.4.4"), endpoint.NewEndpoint("delete-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, "10 mailhost1.foo.elb.amazonaws.com", "20 mailhost2.foo.elb.amazonaws.com"), - } - - provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), false, false, originalEndpoints) - - require.NoError(t, provider.DeleteRecords(context.Background(), originalEndpoints)) + })) records, err := provider.Records(context.Background()) @@ -522,26 +740,132 @@ func TestAWSApplyChanges(t *testing.T) { } for _, tt := range tests { - provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, []*endpoint.Endpoint{ - endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8"), - endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8"), - endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.4.4"), - endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.4.4"), - endpoint.NewEndpointWithTTL("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.1.1.1"), - endpoint.NewEndpointWithTTL("update-test-alias-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificAlias, "true"), - endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "bar.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "qux.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("update-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "bar.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "qux.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("update-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8", "8.8.4.4"), - endpoint.NewEndpointWithTTL("delete-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4", "4.3.2.1"), - endpoint.NewEndpointWithTTL("weighted-to-simple.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("weighted-to-simple").WithProviderSpecific(providerSpecificWeight, "10"), - endpoint.NewEndpointWithTTL("simple-to-weighted.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4"), - endpoint.NewEndpointWithTTL("policy-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("policy-change").WithProviderSpecific(providerSpecificWeight, "10"), - endpoint.NewEndpointWithTTL("set-identifier-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("before").WithProviderSpecific(providerSpecificWeight, "10"), - endpoint.NewEndpointWithTTL("set-identifier-no-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("no-change").WithProviderSpecific(providerSpecificWeight, "10"), - endpoint.NewEndpointWithTTL("update-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 mailhost2.bar.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("delete-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "30 mailhost1.foo.elb.amazonaws.com"), + provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, []*route53.ResourceRecordSet{ + { + Name: aws.String("update-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.8.8")}}, + }, + { + Name: aws.String("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.8.8")}}, + }, + { + Name: aws.String("update-test.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.4.4")}}, + }, + { + Name: aws.String("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.4.4")}}, + }, + { + Name: aws.String("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.1.1.1")}}, + }, + { + Name: aws.String("update-test-alias-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + AliasTarget: &route53.AliasTarget{ + DNSName: aws.String("foo.eu-central-1.elb.amazonaws.com."), + EvaluateTargetHealth: aws.Bool(true), + HostedZoneId: aws.String("Z215JYRZR1TBD5"), + }, + }, + { + Name: aws.String("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("bar.elb.amazonaws.com")}}, + }, + { + Name: aws.String("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("qux.elb.amazonaws.com")}}, + }, + { + Name: aws.String("update-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("bar.elb.amazonaws.com")}}, + }, + { + Name: aws.String("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("qux.elb.amazonaws.com")}}, + }, + { + Name: aws.String("update-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.8.8")}, {Value: aws.String("8.8.4.4")}}, + }, + { + Name: aws.String("delete-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}, {Value: aws.String("4.3.2.1")}}, + }, + { + Name: aws.String("weighted-to-simple.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + SetIdentifier: aws.String("weighted-to-simple"), + Weight: aws.Int64(10), + }, + { + Name: aws.String("simple-to-weighted.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + }, + { + Name: aws.String("policy-change.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + SetIdentifier: aws.String("policy-change"), + Weight: aws.Int64(10), + }, + { + Name: aws.String("set-identifier-change.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + SetIdentifier: aws.String("before"), + Weight: aws.Int64(10), + }, + { + Name: aws.String("set-identifier-no-change.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + SetIdentifier: aws.String("no-change"), + Weight: aws.Int64(10), + }, + { + Name: aws.String("update-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeMx), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("10 mailhost2.bar.elb.amazonaws.com")}}, + }, + { + Name: aws.String("delete-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeMx), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("30 mailhost1.foo.elb.amazonaws.com")}}, + }, }) createRecords := []*endpoint.Endpoint{ @@ -638,23 +962,88 @@ func TestAWSApplyChanges(t *testing.T) { } func TestAWSApplyChangesDryRun(t *testing.T) { - originalEndpoints := []*endpoint.Endpoint{ - endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8"), - endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8"), - endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.4.4"), - endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.4.4"), - endpoint.NewEndpointWithTTL("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.1.1.1"), - endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "bar.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "qux.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("update-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "bar.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "qux.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("update-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8", "8.8.4.4"), - endpoint.NewEndpointWithTTL("delete-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4", "4.3.2.1"), - endpoint.NewEndpointWithTTL("update-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "20 mail.foo.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("delete-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 mail.bar.elb.amazonaws.com"), + originalRecords := []*route53.ResourceRecordSet{ + { + Name: aws.String("update-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.8.8")}}, + }, + { + Name: aws.String("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.8.8")}}, + }, + { + Name: aws.String("update-test.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.4.4")}}, + }, + { + Name: aws.String("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.4.4")}}, + }, + { + Name: aws.String("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.1.1.1")}}, + }, + { + Name: aws.String("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("bar.elb.amazonaws.com")}}, + }, + { + Name: aws.String("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("qux.elb.amazonaws.com")}}, + }, + { + Name: aws.String("update-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("bar.elb.amazonaws.com")}}, + }, + { + Name: aws.String("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("qux.elb.amazonaws.com")}}, + }, + { + Name: aws.String("update-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.8.8")}, {Value: aws.String("8.8.4.4")}}, + }, + { + Name: aws.String("delete-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}, {Value: aws.String("4.3.2.1")}}, + }, + { + Name: aws.String("update-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeMx), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("20 mail.foo.elb.amazonaws.com")}}, + }, + { + Name: aws.String("delete-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeMx), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("10 mail.bar.elb.amazonaws.com")}}, + }, } - provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, true, originalEndpoints) + provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, true, originalRecords) createRecords := []*endpoint.Endpoint{ endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.8.8"), @@ -707,7 +1096,21 @@ func TestAWSApplyChangesDryRun(t *testing.T) { records, err := provider.Records(ctx) require.NoError(t, err) - validateEndpoints(t, records, originalEndpoints) + validateEndpoints(t, records, []*endpoint.Endpoint{ + endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8"), + endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8"), + endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.4.4"), + endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.4.4"), + endpoint.NewEndpointWithTTL("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.1.1.1"), + endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "bar.elb.amazonaws.com"), + endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "qux.elb.amazonaws.com"), + endpoint.NewEndpointWithTTL("update-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "bar.elb.amazonaws.com"), + endpoint.NewEndpointWithTTL("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "qux.elb.amazonaws.com"), + endpoint.NewEndpointWithTTL("update-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8", "8.8.4.4"), + endpoint.NewEndpointWithTTL("delete-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4", "4.3.2.1"), + endpoint.NewEndpointWithTTL("update-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "20 mail.foo.elb.amazonaws.com"), + endpoint.NewEndpointWithTTL("delete-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 mail.bar.elb.amazonaws.com"), + }) } func TestAWSChangesByZones(t *testing.T) { @@ -828,7 +1231,7 @@ func TestAWSChangesByZones(t *testing.T) { } func TestAWSsubmitChanges(t *testing.T) { - provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, []*endpoint.Endpoint{}) + provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, nil) const subnets = 16 const hosts = defaultBatchChangeSize / subnets @@ -857,7 +1260,7 @@ func TestAWSsubmitChanges(t *testing.T) { } func TestAWSsubmitChangesError(t *testing.T) { - provider, clientStub := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, []*endpoint.Endpoint{}) + provider, clientStub := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, nil) clientStub.MockMethod("ChangeResourceRecordSets", mock.Anything).Return(nil, fmt.Errorf("Mock route53 failure")) ctx := context.Background() @@ -871,7 +1274,7 @@ func TestAWSsubmitChangesError(t *testing.T) { } func TestAWSsubmitChangesRetryOnError(t *testing.T) { - provider, clientStub := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, []*endpoint.Endpoint{}) + provider, clientStub := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, nil) ctx := context.Background() zones, err := provider.Zones(ctx) @@ -1066,7 +1469,7 @@ func validateAWSChangeRecord(t *testing.T, record *Route53Change, expected *Rout } func TestAWSCreateRecordsWithCNAME(t *testing.T) { - provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, []*endpoint.Endpoint{}) + provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, nil) records := []*endpoint.Endpoint{ {DNSName: "create-test.zone-1.ext-dns-test-2.teapot.zalan.do", Targets: endpoint.Targets{"foo.example.org"}, RecordType: endpoint.RecordTypeCNAME}, @@ -1096,7 +1499,7 @@ func TestAWSCreateRecordsWithALIAS(t *testing.T) { "false": false, "": false, } { - provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, []*endpoint.Endpoint{}) + provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, nil) // Test dualstack and ipv4 load balancer targets records := []*endpoint.Endpoint{ @@ -1312,22 +1715,33 @@ func createAWSZone(t *testing.T, provider *AWSProvider, zone *route53.HostedZone } } -func setupAWSRecords(t *testing.T, provider *AWSProvider, endpoints []*endpoint.Endpoint) { - clearAWSRecords(t, provider, "/hostedzone/zone-1.ext-dns-test-2.teapot.zalan.do.") - clearAWSRecords(t, provider, "/hostedzone/zone-2.ext-dns-test-2.teapot.zalan.do.") - clearAWSRecords(t, provider, "/hostedzone/zone-3.ext-dns-test-2.teapot.zalan.do.") +func setAWSRecords(t *testing.T, provider *AWSProvider, records []*route53.ResourceRecordSet) { + dryRun := provider.dryRun + provider.dryRun = false + defer func() { + provider.dryRun = dryRun + }() ctx := context.Background() - records, err := provider.Records(ctx) + endpoints, err := provider.Records(ctx) require.NoError(t, err) - validateEndpoints(t, records, []*endpoint.Endpoint{}) + validateEndpoints(t, endpoints, []*endpoint.Endpoint{}) - require.NoError(t, provider.CreateRecords(context.Background(), endpoints)) + var changes Route53Changes + for _, record := range records { + changes = append(changes, &Route53Change{ + Change: route53.Change{ + Action: aws.String(route53.ChangeActionCreate), + ResourceRecordSet: record, + }, + }) + } - escapeAWSRecords(t, provider, "/hostedzone/zone-1.ext-dns-test-2.teapot.zalan.do.") - escapeAWSRecords(t, provider, "/hostedzone/zone-2.ext-dns-test-2.teapot.zalan.do.") - escapeAWSRecords(t, provider, "/hostedzone/zone-3.ext-dns-test-2.teapot.zalan.do.") + zones, err := provider.Zones(ctx) + require.NoError(t, err) + err = provider.submitChanges(ctx, changes, zones) + require.NoError(t, err) _, err = provider.Records(ctx) require.NoError(t, err) @@ -1346,28 +1760,6 @@ func listAWSRecords(t *testing.T, client Route53API, zone string) []*route53.Res return recordSets } -func clearAWSRecords(t *testing.T, provider *AWSProvider, zone string) { - recordSets := listAWSRecords(t, provider.client, zone) - - changes := make([]*route53.Change, 0, len(recordSets)) - for _, recordSet := range recordSets { - changes = append(changes, &route53.Change{ - Action: aws.String(route53.ChangeActionDelete), - ResourceRecordSet: recordSet, - }) - } - - if len(changes) != 0 { - _, err := provider.client.ChangeResourceRecordSetsWithContext(context.Background(), &route53.ChangeResourceRecordSetsInput{ - HostedZoneId: aws.String(zone), - ChangeBatch: &route53.ChangeBatch{ - Changes: changes, - }, - }) - require.NoError(t, err) - } -} - // Route53 stores wildcards escaped: http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DomainNameFormat.html?shortFooter=true#domain-name-format-asterisk func escapeAWSRecords(t *testing.T, provider *AWSProvider, zone string) { recordSets := listAWSRecords(t, provider.client, zone) @@ -1391,11 +1783,11 @@ func escapeAWSRecords(t *testing.T, provider *AWSProvider, zone string) { } } -func newAWSProvider(t *testing.T, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, zoneTypeFilter provider.ZoneTypeFilter, evaluateTargetHealth, dryRun bool, records []*endpoint.Endpoint) (*AWSProvider, *Route53APIStub) { +func newAWSProvider(t *testing.T, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, zoneTypeFilter provider.ZoneTypeFilter, evaluateTargetHealth, dryRun bool, records []*route53.ResourceRecordSet) (*AWSProvider, *Route53APIStub) { return newAWSProviderWithTagFilter(t, domainFilter, zoneIDFilter, zoneTypeFilter, provider.NewZoneTagFilter([]string{}), evaluateTargetHealth, dryRun, records) } -func newAWSProviderWithTagFilter(t *testing.T, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, zoneTypeFilter provider.ZoneTypeFilter, zoneTagFilter provider.ZoneTagFilter, evaluateTargetHealth, dryRun bool, records []*endpoint.Endpoint) (*AWSProvider, *Route53APIStub) { +func newAWSProviderWithTagFilter(t *testing.T, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, zoneTypeFilter provider.ZoneTypeFilter, zoneTagFilter provider.ZoneTagFilter, evaluateTargetHealth, dryRun bool, records []*route53.ResourceRecordSet) (*AWSProvider, *Route53APIStub) { client := NewRoute53APIStub(t) provider := &AWSProvider{ @@ -1439,7 +1831,7 @@ func newAWSProviderWithTagFilter(t *testing.T, domainFilter endpoint.DomainFilte setupZoneTags(provider.client.(*Route53APIStub)) - setupAWSRecords(t, provider, records) + setAWSRecords(t, provider, records) provider.dryRun = dryRun @@ -1494,7 +1886,7 @@ func containsRecordWithDNSName(records []*endpoint.Endpoint, dnsName string) boo } func TestRequiresDeleteCreate(t *testing.T) { - provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"foo.bar."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, []*endpoint.Endpoint{}) + provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"foo.bar."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, nil) oldRecordType := endpoint.NewEndpointWithTTL("recordType", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8") newRecordType := endpoint.NewEndpointWithTTL("recordType", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "bar") From 10a6a04e47dc743531449113369d2fca2ef6864c Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Fri, 12 May 2023 14:05:51 -0700 Subject: [PATCH 114/117] Refactor route53 test assertions Makes relevant tests verify the precise resulting route53 records --- provider/aws/aws_test.go | 299 ++++++++++++++++++++++++++++++--------- 1 file changed, 231 insertions(+), 68 deletions(-) diff --git a/provider/aws/aws_test.go b/provider/aws/aws_test.go index 84d63138c..7382b54f3 100644 --- a/provider/aws/aws_test.go +++ b/provider/aws/aws_test.go @@ -558,18 +558,63 @@ func TestAWSCreateRecords(t *testing.T) { require.NoError(t, provider.CreateRecords(context.Background(), records)) - records, err := provider.Records(context.Background()) - require.NoError(t, err) - - validateEndpoints(t, records, []*endpoint.Endpoint{ - endpoint.NewEndpointWithTTL("create-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4"), - endpoint.NewEndpointWithTTL("create-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8"), - endpoint.NewEndpointWithTTL("create-test-custom-ttl.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, customTTL, "172.17.0.1"), - endpoint.NewEndpointWithTTL("create-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "foo.example.com"), - endpoint.NewEndpointWithTTL("create-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true").WithProviderSpecific(providerSpecificAlias, "true"), - endpoint.NewEndpointWithTTL("create-test-cname-alias.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "alias-target.zone-2.ext-dns-test-2.teapot.zalan.do").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true").WithProviderSpecific(providerSpecificAlias, "true"), - endpoint.NewEndpointWithTTL("create-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8", "8.8.4.4"), - endpoint.NewEndpointWithTTL("create-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 mailhost1.example.com", "20 mailhost2.example.com"), + validateRecords(t, listAWSRecords(t, provider.client, "/hostedzone/zone-1.ext-dns-test-2.teapot.zalan.do."), []*route53.ResourceRecordSet{ + { + Name: aws.String("create-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + }, + { + Name: aws.String("create-test-custom-ttl.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(60), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("172.17.0.1")}}, + }, + { + Name: aws.String("create-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("foo.example.com")}}, + }, + { + Name: aws.String("create-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + AliasTarget: &route53.AliasTarget{ + DNSName: aws.String("foo.eu-central-1.elb.amazonaws.com."), + EvaluateTargetHealth: aws.Bool(true), + HostedZoneId: aws.String("Z215JYRZR1TBD5"), + }, + }, + { + Name: aws.String("create-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.8.8")}, {Value: aws.String("8.8.4.4")}}, + }, + { + Name: aws.String("create-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeMx), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("10 mailhost1.example.com")}, {Value: aws.String("20 mailhost2.example.com")}}, + }, + }) + validateRecords(t, listAWSRecords(t, provider.client, "/hostedzone/zone-2.ext-dns-test-2.teapot.zalan.do."), []*route53.ResourceRecordSet{ + { + Name: aws.String("create-test.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.8.8")}}, + }, + { + Name: aws.String("create-test-cname-alias.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + AliasTarget: &route53.AliasTarget{ + DNSName: aws.String("alias-target.zone-2.ext-dns-test-2.teapot.zalan.do."), + EvaluateTargetHealth: aws.Bool(true), + HostedZoneId: aws.String("zone-2.ext-dns-test-2.teapot.zalan.do."), + }, + }, }) } @@ -632,16 +677,45 @@ func TestAWSUpdateRecords(t *testing.T) { require.NoError(t, provider.UpdateRecords(context.Background(), updatedRecords, currentRecords)) - records, err := provider.Records(context.Background()) - require.NoError(t, err) - - validateEndpoints(t, records, []*endpoint.Endpoint{ - endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4"), - endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1"), - endpoint.NewEndpointWithTTL("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "foo.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "bar.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("create-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4", "4.3.2.1"), - endpoint.NewEndpointWithTTL("update-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 mailhost1.foo.elb.amazonaws.com", "20 mailhost2.foo.elb.amazonaws.com"), + validateRecords(t, listAWSRecords(t, provider.client, "/hostedzone/zone-1.ext-dns-test-2.teapot.zalan.do."), []*route53.ResourceRecordSet{ + { + Name: aws.String("update-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + }, + { + Name: aws.String("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("foo.elb.amazonaws.com")}}, + }, + { + Name: aws.String("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("bar.elb.amazonaws.com")}}, + }, + { + Name: aws.String("create-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}, {Value: aws.String("4.3.2.1")}}, + }, + { + Name: aws.String("update-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeMx), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("10 mailhost1.foo.elb.amazonaws.com")}, {Value: aws.String("20 mailhost2.foo.elb.amazonaws.com")}}, + }, + }) + validateRecords(t, listAWSRecords(t, provider.client, "/hostedzone/zone-2.ext-dns-test-2.teapot.zalan.do."), []*route53.ResourceRecordSet{ + { + Name: aws.String("update-test.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("4.3.2.1")}}, + }, }) } @@ -717,11 +791,8 @@ func TestAWSDeleteRecords(t *testing.T) { endpoint.NewEndpoint("delete-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, "10 mailhost1.foo.elb.amazonaws.com", "20 mailhost2.foo.elb.amazonaws.com"), })) - records, err := provider.Records(context.Background()) - - require.NoError(t, err) - - validateEndpoints(t, records, []*endpoint.Endpoint{}) + validateRecords(t, listAWSRecords(t, provider.client, "/hostedzone/zone-1.ext-dns-test-2.teapot.zalan.do."), []*route53.ResourceRecordSet{}) + validateRecords(t, listAWSRecords(t, provider.client, "/hostedzone/zone-2.ext-dns-test-2.teapot.zalan.do."), []*route53.ResourceRecordSet{}) } func TestAWSApplyChanges(t *testing.T) { @@ -934,29 +1005,134 @@ func TestAWSApplyChanges(t *testing.T) { assert.Equal(t, 1, counter.calls["ListHostedZonesPages"], tt.name) assert.Equal(t, tt.listRRSets, counter.calls["ListResourceRecordSetsPages"], tt.name) - records, err := provider.Records(ctx) - require.NoError(t, err, tt.name) - - validateEndpoints(t, records, []*endpoint.Endpoint{ - endpoint.NewEndpointWithTTL("create-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8"), - endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4"), - endpoint.NewEndpointWithTTL("create-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.4.4"), - endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1"), - endpoint.NewEndpointWithTTL("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "foo.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true").WithProviderSpecific(providerSpecificAlias, "true"), - endpoint.NewEndpointWithTTL("update-test-alias-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "my-internal-host.example.com"), - endpoint.NewEndpointWithTTL("create-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "foo.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "baz.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("create-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "foo.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("update-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "baz.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("create-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8", "8.8.4.4"), - endpoint.NewEndpointWithTTL("update-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4", "4.3.2.1"), - endpoint.NewEndpointWithTTL("weighted-to-simple.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4"), - endpoint.NewEndpointWithTTL("simple-to-weighted.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("simple-to-weighted").WithProviderSpecific(providerSpecificWeight, "10"), - endpoint.NewEndpointWithTTL("policy-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("policy-change").WithProviderSpecific(providerSpecificRegion, "us-east-1"), - endpoint.NewEndpointWithTTL("set-identifier-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("after").WithProviderSpecific(providerSpecificWeight, "10"), - endpoint.NewEndpointWithTTL("set-identifier-no-change.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("no-change").WithProviderSpecific(providerSpecificWeight, "20"), - endpoint.NewEndpointWithTTL("create-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 mailhost1.foo.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("update-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "20 mailhost3.foo.elb.amazonaws.com"), + validateRecords(t, listAWSRecords(t, provider.client, "/hostedzone/zone-1.ext-dns-test-2.teapot.zalan.do."), []*route53.ResourceRecordSet{ + { + Name: aws.String("create-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.8.8")}}, + }, + { + Name: aws.String("update-test.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + }, + { + Name: aws.String("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + AliasTarget: &route53.AliasTarget{ + DNSName: aws.String("foo.elb.amazonaws.com."), + EvaluateTargetHealth: aws.Bool(true), + HostedZoneId: aws.String("zone-1.ext-dns-test-2.teapot.zalan.do."), + }, + }, + { + Name: aws.String("update-test-alias-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("my-internal-host.example.com")}}, + }, + { + Name: aws.String("create-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("foo.elb.amazonaws.com")}}, + }, + { + Name: aws.String("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("baz.elb.amazonaws.com")}}, + }, + { + Name: aws.String("create-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("foo.elb.amazonaws.com")}}, + }, + { + Name: aws.String("update-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeCname), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("baz.elb.amazonaws.com")}}, + }, + { + Name: aws.String("weighted-to-simple.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + }, + { + Name: aws.String("simple-to-weighted.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + SetIdentifier: aws.String("simple-to-weighted"), + Weight: aws.Int64(10), + }, + { + Name: aws.String("policy-change.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + SetIdentifier: aws.String("policy-change"), + Region: aws.String("us-east-1"), + }, + { + Name: aws.String("set-identifier-change.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + SetIdentifier: aws.String("after"), + Weight: aws.Int64(10), + }, + { + Name: aws.String("set-identifier-no-change.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}}, + SetIdentifier: aws.String("no-change"), + Weight: aws.Int64(20), + }, + { + Name: aws.String("create-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeMx), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("10 mailhost1.foo.elb.amazonaws.com")}}, + }, + }) + validateRecords(t, listAWSRecords(t, provider.client, "/hostedzone/zone-2.ext-dns-test-2.teapot.zalan.do."), []*route53.ResourceRecordSet{ + { + Name: aws.String("create-test.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.4.4")}}, + }, + { + Name: aws.String("update-test.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("4.3.2.1")}}, + }, + { + Name: aws.String("create-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("8.8.8.8")}, {Value: aws.String("8.8.4.4")}}, + }, + { + Name: aws.String("update-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeA), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("1.2.3.4")}, {Value: aws.String("4.3.2.1")}}, + }, + { + Name: aws.String("update-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do."), + Type: aws.String(route53.RRTypeMx), + TTL: aws.Int64(recordTTL), + ResourceRecords: []*route53.ResourceRecord{{Value: aws.String("20 mailhost3.foo.elb.amazonaws.com")}}, + }, }) } } @@ -1093,24 +1269,11 @@ func TestAWSApplyChangesDryRun(t *testing.T) { require.NoError(t, provider.ApplyChanges(ctx, changes)) - records, err := provider.Records(ctx) - require.NoError(t, err) - - validateEndpoints(t, records, []*endpoint.Endpoint{ - endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8"), - endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8"), - endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.4.4"), - endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.4.4"), - endpoint.NewEndpointWithTTL("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.1.1.1"), - endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "bar.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "qux.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("update-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "bar.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "qux.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("update-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8", "8.8.4.4"), - endpoint.NewEndpointWithTTL("delete-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4", "4.3.2.1"), - endpoint.NewEndpointWithTTL("update-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "20 mail.foo.elb.amazonaws.com"), - endpoint.NewEndpointWithTTL("delete-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 mail.bar.elb.amazonaws.com"), - }) + validateRecords(t, + append( + listAWSRecords(t, provider.client, "/hostedzone/zone-1.ext-dns-test-2.teapot.zalan.do."), + listAWSRecords(t, provider.client, "/hostedzone/zone-2.ext-dns-test-2.teapot.zalan.do.")...), + originalRecords) } func TestAWSChangesByZones(t *testing.T) { From b17506cfa39107d9619b2763a931ee6fe7faca66 Mon Sep 17 00:00:00 2001 From: Antoine Bardoux Date: Tue, 16 May 2023 15:43:28 +0200 Subject: [PATCH 115/117] Remove unnecessary trailing newline --- source/pod.go | 1 - 1 file changed, 1 deletion(-) diff --git a/source/pod.go b/source/pod.go index 912879b95..e399a8906 100644 --- a/source/pod.go +++ b/source/pod.go @@ -93,7 +93,6 @@ func (ps *podSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error domainList := splitHostnameAnnotation(domainAnnotation) for _, domain := range domainList { addToEndpointMap(endpointMap, domain, suitableType(pod.Status.PodIP), pod.Status.PodIP) - } } From 83d58fae03f514a789230101e95e1096871d5d32 Mon Sep 17 00:00:00 2001 From: Jonas-Taha El Sesiy Date: Tue, 16 May 2023 08:46:54 -0700 Subject: [PATCH 116/117] Remove UpdateOld changeset for Azure The underlying SDK uses `recordSetsClient.CreateOrUpdate()` [doc](https://docs.microsoft.com/en-us/rest/api/dns/record-sets/create-or-update) which allows us to exclusively rely on UpdateNew for record set updates. As a result, we're no longer running the risk of zone update failures causing outages as outlined in #2918 Fix https://github.com/kubernetes-sigs/external-dns/issues/2918 --- provider/azure/azure.go | 4 ---- provider/azure/azure_private_dns.go | 4 ---- provider/azure/azure_privatedns_test.go | 3 --- provider/azure/azure_test.go | 5 ----- 4 files changed, 16 deletions(-) diff --git a/provider/azure/azure.go b/provider/azure/azure.go index b56a21320..bca9c841d 100644 --- a/provider/azure/azure.go +++ b/provider/azure/azure.go @@ -250,10 +250,6 @@ func (p *AzureProvider) mapChanges(zones []dns.Zone, changes *plan.Changes) (azu mapChange(deleted, change) } - for _, change := range changes.UpdateOld { - mapChange(deleted, change) - } - for _, change := range changes.Create { mapChange(updated, change) } diff --git a/provider/azure/azure_private_dns.go b/provider/azure/azure_private_dns.go index e3ab40c48..ee2e478bd 100644 --- a/provider/azure/azure_private_dns.go +++ b/provider/azure/azure_private_dns.go @@ -237,10 +237,6 @@ func (p *AzurePrivateDNSProvider) mapChanges(zones []privatedns.PrivateZone, cha mapChange(deleted, change) } - for _, change := range changes.UpdateOld { - mapChange(deleted, change) - } - for _, change := range changes.Create { mapChange(updated, change) } diff --git a/provider/azure/azure_privatedns_test.go b/provider/azure/azure_privatedns_test.go index e728659c4..061b0f4d6 100644 --- a/provider/azure/azure_privatedns_test.go +++ b/provider/azure/azure_privatedns_test.go @@ -342,11 +342,8 @@ func TestAzurePrivateDNSApplyChanges(t *testing.T) { testAzurePrivateDNSApplyChangesInternal(t, false, &recordsClient) validateAzureEndpoints(t, recordsClient.deletedEndpoints, []*endpoint.Endpoint{ - endpoint.NewEndpoint("old.example.com", endpoint.RecordTypeA, ""), - endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, ""), endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, ""), endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, ""), - endpoint.NewEndpoint("oldmail.example.com", endpoint.RecordTypeMX, ""), }) validateAzureEndpoints(t, recordsClient.updatedEndpoints, []*endpoint.Endpoint{ diff --git a/provider/azure/azure_test.go b/provider/azure/azure_test.go index 050ab3d92..ba831586c 100644 --- a/provider/azure/azure_test.go +++ b/provider/azure/azure_test.go @@ -349,11 +349,8 @@ func TestAzureApplyChanges(t *testing.T) { testAzureApplyChangesInternal(t, false, &recordsClient) validateAzureEndpoints(t, recordsClient.deletedEndpoints, []*endpoint.Endpoint{ - endpoint.NewEndpoint("old.example.com", endpoint.RecordTypeA, ""), - endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, ""), endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, ""), endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, ""), - endpoint.NewEndpoint("oldmail.example.com", endpoint.RecordTypeMX, ""), }) validateAzureEndpoints(t, recordsClient.updatedEndpoints, []*endpoint.Endpoint{ @@ -508,8 +505,6 @@ func TestAzureApplyChangesZoneName(t *testing.T) { testAzureApplyChangesInternalZoneName(t, false, &recordsClient) validateAzureEndpoints(t, recordsClient.deletedEndpoints, []*endpoint.Endpoint{ - endpoint.NewEndpoint("old.foo.example.com", endpoint.RecordTypeA, ""), - endpoint.NewEndpoint("oldcname.foo.example.com", endpoint.RecordTypeCNAME, ""), endpoint.NewEndpoint("deleted.foo.example.com", endpoint.RecordTypeA, ""), endpoint.NewEndpoint("deletedcname.foo.example.com", endpoint.RecordTypeCNAME, ""), }) From a8d0c4dc1d6c29ab9a889c582155b4be616f9a94 Mon Sep 17 00:00:00 2001 From: nitrocode <7775707+nitrocode@users.noreply.github.com> Date: Tue, 23 May 2023 17:08:09 -0500 Subject: [PATCH 117/117] chore(deps): bump libcrypto3 and libssl3 3.0.8-r4 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8590ad633..db7d8dcca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,7 +27,7 @@ COPY . . FROM alpine:3.18 -RUN apk update && apk add "libcrypto3>=3.0.8-r1" "libssl3>=3.0.8-r1" && rm -rf /var/cache/apt/* +RUN apk update && apk add "libcrypto3>=3.0.8-r4" "libssl3>=3.0.8-r4" && rm -rf /var/cache/apt/* COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt COPY --from=builder /sigs.k8s.io/external-dns/build/external-dns /bin/external-dns