From 44f319e6a058b5ac4f54ae139e229d337f77dd58 Mon Sep 17 00:00:00 2001 From: Toshikuni Fukaya Date: Wed, 25 Jul 2018 18:35:26 +0900 Subject: [PATCH 001/136] Support A record for multile IPs for a headless services. Non statefulset pods associating to a headless service have different IPs, but have a same hostname. In this case, external-dns registered only one A record due to attempting to register multiple A records for a same hostname for each IP. This patch now registers one A record having multiple IPs. --- source/service.go | 27 +++++++++++++++++---------- source/service_test.go | 38 ++++++++++++++++++-------------------- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/source/service.go b/source/service.go index cba3ce685..8d7831d30 100644 --- a/source/service.go +++ b/source/service.go @@ -156,6 +156,7 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri return endpoints } + targetsByHeadlessDomain := make(map[string][]string) for _, v := range pods.Items { headlessDomain := hostname if v.Spec.Hostname != "" { @@ -166,11 +167,7 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri log.Debugf("Generating matching endpoint %s with HostIP %s", headlessDomain, v.Status.HostIP) // To reduce traffice on the DNS API only add record for running Pods. Good Idea? if v.Status.Phase == v1.PodRunning { - if ttl.IsConfigured() { - endpoints = append(endpoints, endpoint.NewEndpointWithTTL(headlessDomain, endpoint.RecordTypeA, ttl, v.Status.HostIP)) - } else { - endpoints = append(endpoints, endpoint.NewEndpoint(headlessDomain, endpoint.RecordTypeA, v.Status.HostIP)) - } + targetsByHeadlessDomain[headlessDomain] = append(targetsByHeadlessDomain[headlessDomain], v.Status.HostIP) } else { log.Debugf("Pod %s is not in running phase", v.Spec.Hostname) } @@ -178,11 +175,7 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri log.Debugf("Generating matching endpoint %s with PodIP %s", headlessDomain, v.Status.PodIP) // To reduce traffice on the DNS API only add record for running Pods. Good Idea? if v.Status.Phase == v1.PodRunning { - if ttl.IsConfigured() { - endpoints = append(endpoints, endpoint.NewEndpointWithTTL(headlessDomain, endpoint.RecordTypeA, ttl, v.Status.PodIP)) - } else { - endpoints = append(endpoints, endpoint.NewEndpoint(headlessDomain, endpoint.RecordTypeA, v.Status.PodIP)) - } + targetsByHeadlessDomain[headlessDomain] = append(targetsByHeadlessDomain[headlessDomain], v.Status.PodIP) } else { log.Debugf("Pod %s is not in running phase", v.Spec.Hostname) } @@ -190,6 +183,20 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri } + headlessDomains := []string{} + for headlessDomain := range targetsByHeadlessDomain { + headlessDomains = append(headlessDomains, headlessDomain) + } + sort.Strings(headlessDomains) + for _, headlessDomain := range headlessDomains { + targets := targetsByHeadlessDomain[headlessDomain] + if ttl.IsConfigured() { + endpoints = append(endpoints, endpoint.NewEndpointWithTTL(headlessDomain, endpoint.RecordTypeA, ttl, targets...)) + } else { + endpoints = append(endpoints, endpoint.NewEndpoint(headlessDomain, endpoint.RecordTypeA, targets...)) + } + } + return endpoints } diff --git a/source/service_test.go b/source/service_test.go index 2e6a59b1f..9989cd1e7 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -1235,7 +1235,7 @@ func TestHeadlessServices(t *testing.T) { labels map[string]string annotations map[string]string clusterIP string - podIP string + podIPs []string selector map[string]string lbs []string podnames []string @@ -1257,7 +1257,7 @@ func TestHeadlessServices(t *testing.T) { hostnameAnnotationKey: "service.example.org", }, v1.ClusterIPNone, - "1.1.1.1", + []string{"1.1.1.1", "1.1.1.2"}, map[string]string{ "component": "foo", }, @@ -1267,7 +1267,7 @@ func TestHeadlessServices(t *testing.T) { []v1.PodPhase{v1.PodRunning, v1.PodRunning}, []*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.1"}}, + {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.2"}}, }, false, }, @@ -1285,7 +1285,7 @@ func TestHeadlessServices(t *testing.T) { ttlAnnotationKey: "1", }, v1.ClusterIPNone, - "1.1.1.1", + []string{"1.1.1.1", "1.1.1.2"}, map[string]string{ "component": "foo", }, @@ -1295,7 +1295,7 @@ func TestHeadlessServices(t *testing.T) { []v1.PodPhase{v1.PodRunning, v1.PodRunning}, []*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.1"}, RecordTTL: endpoint.TTL(1)}, + {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.2"}, RecordTTL: endpoint.TTL(1)}, }, false, }, @@ -1312,7 +1312,7 @@ func TestHeadlessServices(t *testing.T) { hostnameAnnotationKey: "service.example.org", }, v1.ClusterIPNone, - "1.1.1.1", + []string{"1.1.1.1", "1.1.1.2"}, map[string]string{ "component": "foo", }, @@ -1338,7 +1338,7 @@ func TestHeadlessServices(t *testing.T) { hostnameAnnotationKey: "service.example.org", }, v1.ClusterIPNone, - "1.1.1.1", + []string{"1.1.1.1", "1.1.1.2"}, map[string]string{ "component": "foo", }, @@ -1347,8 +1347,7 @@ func TestHeadlessServices(t *testing.T) { []string{"", ""}, []v1.PodPhase{v1.PodRunning, v1.PodRunning}, []*endpoint.Endpoint{ - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, + {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}}, }, false, }, @@ -1387,7 +1386,7 @@ func TestHeadlessServices(t *testing.T) { Annotations: tc.annotations, }, Status: v1.PodStatus{ - PodIP: tc.podIP, + PodIP: tc.podIPs[i], Phase: tc.phases[i], }, } @@ -1435,7 +1434,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { labels map[string]string annotations map[string]string clusterIP string - hostIP string + hostIPs []string selector map[string]string lbs []string podnames []string @@ -1457,7 +1456,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { hostnameAnnotationKey: "service.example.org", }, v1.ClusterIPNone, - "1.1.1.1", + []string{"1.1.1.1", "1.1.1.2"}, map[string]string{ "component": "foo", }, @@ -1467,7 +1466,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { []v1.PodPhase{v1.PodRunning, v1.PodRunning}, []*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.1"}}, + {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.2"}}, }, false, }, @@ -1485,7 +1484,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { ttlAnnotationKey: "1", }, v1.ClusterIPNone, - "1.1.1.1", + []string{"1.1.1.1", "1.1.1.2"}, map[string]string{ "component": "foo", }, @@ -1495,7 +1494,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { []v1.PodPhase{v1.PodRunning, v1.PodRunning}, []*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.1"}, RecordTTL: endpoint.TTL(1)}, + {DNSName: "foo-1.service.example.org", Targets: endpoint.Targets{"1.1.1.2"}, RecordTTL: endpoint.TTL(1)}, }, false, }, @@ -1512,7 +1511,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { hostnameAnnotationKey: "service.example.org", }, v1.ClusterIPNone, - "1.1.1.1", + []string{"1.1.1.1", "1.1.1.2"}, map[string]string{ "component": "foo", }, @@ -1538,7 +1537,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { hostnameAnnotationKey: "service.example.org", }, v1.ClusterIPNone, - "1.1.1.1", + []string{"1.1.1.1", "1.1.1.2"}, map[string]string{ "component": "foo", }, @@ -1547,8 +1546,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { []string{"", ""}, []v1.PodPhase{v1.PodRunning, v1.PodRunning}, []*endpoint.Endpoint{ - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, - {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1"}}, + {DNSName: "service.example.org", Targets: endpoint.Targets{"1.1.1.1", "1.1.1.2"}}, }, false, }, @@ -1587,7 +1585,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { Annotations: tc.annotations, }, Status: v1.PodStatus{ - HostIP: tc.hostIP, + HostIP: tc.hostIPs[i], Phase: tc.phases[i], }, } From 3454363d67e238e4f081ba95435ff41ec12b5f9d Mon Sep 17 00:00:00 2001 From: Anand Patel Date: Mon, 15 Oct 2018 16:50:52 -0400 Subject: [PATCH 002/136] allow hostname annotations to be ignored --- main.go | 1 + pkg/apis/externaldns/types.go | 3 + pkg/apis/externaldns/types_test.go | 107 ++--- pkg/apis/externaldns/validation/validation.go | 4 + .../externaldns/validation/validation_test.go | 8 + source/gateway.go | 44 +- source/gateway_test.go | 50 +++ source/ingress.go | 38 +- source/ingress_test.go | 38 +- source/service.go | 43 +- source/service_test.go | 384 +++++++++++++++--- source/store.go | 7 +- 12 files changed, 551 insertions(+), 176 deletions(-) diff --git a/main.go b/main.go index b32b5fd45..a3ee8a3a0 100644 --- a/main.go +++ b/main.go @@ -71,6 +71,7 @@ func main() { AnnotationFilter: cfg.AnnotationFilter, FQDNTemplate: cfg.FQDNTemplate, CombineFQDNAndAnnotation: cfg.CombineFQDNAndAnnotation, + IgnoreHostnameAnnotation: cfg.IgnoreHostnameAnnotation, Compatibility: cfg.Compatibility, PublishInternal: cfg.PublishInternal, PublishHostIP: cfg.PublishHostIP, diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index cad2bfd35..d518f99d9 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -45,6 +45,7 @@ type Config struct { AnnotationFilter string FQDNTemplate string CombineFQDNAndAnnotation bool + IgnoreHostnameAnnotation bool Compatibility string PublishInternal bool PublishHostIP bool @@ -118,6 +119,7 @@ var defaultConfig = &Config{ AnnotationFilter: "", FQDNTemplate: "", CombineFQDNAndAnnotation: false, + IgnoreHostnameAnnotation: false, Compatibility: "", PublishInternal: false, PublishHostIP: false, @@ -225,6 +227,7 @@ func (cfg *Config) ParseFlags(args []string) error { 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("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) app.Flag("compatibility", "Process annotation semantics from legacy implementations (optional, options: mate, molecule)").Default(defaultConfig.Compatibility).EnumVar(&cfg.Compatibility, "", "mate", "molecule") app.Flag("publish-internal-services", "Allow external-dns to publish DNS records for ClusterIP services (optional)").BoolVar(&cfg.PublishInternal) app.Flag("publish-host-ip", "Allow external-dns to publish host-ip for headless services (optional)").BoolVar(&cfg.PublishHostIP) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index e3094402b..221f1f92a 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -80,58 +80,59 @@ var ( } overriddenConfig = &Config{ - Master: "http://127.0.0.1:8080", - KubeConfig: "/some/path", - RequestTimeout: time.Second * 77, - IstioIngressGateway: "istio-other/istio-otheringressgateway", - Sources: []string{"service", "ingress", "connector"}, - Namespace: "namespace", - FQDNTemplate: "{{.Name}}.service.example.com", - Compatibility: "mate", - Provider: "google", - GoogleProject: "project", - DomainFilter: []string{"example.org", "company.com"}, - ZoneIDFilter: []string{"/hostedzone/ZTST1", "/hostedzone/ZTST2"}, - AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json", - AWSZoneType: "private", - AWSAssumeRole: "some-other-role", - AWSBatchChangeSize: 100, - AWSBatchChangeInterval: time.Second * 2, - AWSEvaluateTargetHealth: false, - AzureConfigFile: "azure.json", - AzureResourceGroup: "arg", - CloudflareProxied: true, - InfobloxGridHost: "127.0.0.1", - InfobloxWapiPort: 8443, - InfobloxWapiUsername: "infoblox", - InfobloxWapiPassword: "infoblox", - InfobloxWapiVersion: "2.6.1", - InfobloxSSLVerify: false, - OCIConfigFile: "oci.yaml", - InMemoryZones: []string{"example.org", "company.com"}, - PDNSServer: "http://ns.example.com:8081", - PDNSAPIKey: "some-secret-key", - PDNSTLSEnabled: true, - TLSCA: "/path/to/ca.crt", - TLSClientCert: "/path/to/cert.pem", - TLSClientCertKey: "/path/to/key.pem", - Policy: "upsert-only", - Registry: "noop", - TXTOwnerID: "owner-1", - TXTPrefix: "associated-txt-record", - TXTCacheInterval: 12 * time.Hour, - Interval: 10 * time.Minute, - Once: true, - DryRun: true, - LogFormat: "json", - MetricsAddress: "127.0.0.1:9099", - LogLevel: logrus.DebugLevel.String(), - ConnectorSourceServer: "localhost:8081", - ExoscaleEndpoint: "https://api.foo.ch/dns", - ExoscaleAPIKey: "1", - ExoscaleAPISecret: "2", - CRDSourceAPIVersion: "test.k8s.io/v1alpha1", - CRDSourceKind: "Endpoint", + Master: "http://127.0.0.1:8080", + KubeConfig: "/some/path", + RequestTimeout: time.Second * 77, + IstioIngressGateway: "istio-other/istio-otheringressgateway", + Sources: []string{"service", "ingress", "connector"}, + Namespace: "namespace", + FQDNTemplate: "{{.Name}}.service.example.com", + IgnoreHostnameAnnotation: true, + Compatibility: "mate", + Provider: "google", + GoogleProject: "project", + DomainFilter: []string{"example.org", "company.com"}, + ZoneIDFilter: []string{"/hostedzone/ZTST1", "/hostedzone/ZTST2"}, + AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json", + AWSZoneType: "private", + AWSAssumeRole: "some-other-role", + AWSBatchChangeSize: 100, + AWSBatchChangeInterval: time.Second * 2, + AWSEvaluateTargetHealth: false, + AzureConfigFile: "azure.json", + AzureResourceGroup: "arg", + CloudflareProxied: true, + InfobloxGridHost: "127.0.0.1", + InfobloxWapiPort: 8443, + InfobloxWapiUsername: "infoblox", + InfobloxWapiPassword: "infoblox", + InfobloxWapiVersion: "2.6.1", + InfobloxSSLVerify: false, + OCIConfigFile: "oci.yaml", + InMemoryZones: []string{"example.org", "company.com"}, + PDNSServer: "http://ns.example.com:8081", + PDNSAPIKey: "some-secret-key", + PDNSTLSEnabled: true, + TLSCA: "/path/to/ca.crt", + TLSClientCert: "/path/to/cert.pem", + TLSClientCertKey: "/path/to/key.pem", + Policy: "upsert-only", + Registry: "noop", + TXTOwnerID: "owner-1", + TXTPrefix: "associated-txt-record", + TXTCacheInterval: 12 * time.Hour, + Interval: 10 * time.Minute, + Once: true, + DryRun: true, + LogFormat: "json", + MetricsAddress: "127.0.0.1:9099", + LogLevel: logrus.DebugLevel.String(), + ConnectorSourceServer: "localhost:8081", + ExoscaleEndpoint: "https://api.foo.ch/dns", + ExoscaleAPIKey: "1", + ExoscaleAPISecret: "2", + CRDSourceAPIVersion: "test.k8s.io/v1alpha1", + CRDSourceKind: "Endpoint", } ) @@ -163,6 +164,7 @@ func TestParseFlags(t *testing.T) { "--source=connector", "--namespace=namespace", "--fqdn-template={{.Name}}.service.example.com", + "--ignore-hostname-annotation", "--compatibility=mate", "--provider=google", "--google-project=project", @@ -225,6 +227,7 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_SOURCE": "service\ningress\nconnector", "EXTERNAL_DNS_NAMESPACE": "namespace", "EXTERNAL_DNS_FQDN_TEMPLATE": "{{.Name}}.service.example.com", + "EXTERNAL_DNS_IGNORE_HOSTNAME_ANNOTATION": "1", "EXTERNAL_DNS_COMPATIBILITY": "mate", "EXTERNAL_DNS_PROVIDER": "google", "EXTERNAL_DNS_GOOGLE_PROJECT": "project", diff --git a/pkg/apis/externaldns/validation/validation.go b/pkg/apis/externaldns/validation/validation.go index cdbe6ef4e..174550cb4 100644 --- a/pkg/apis/externaldns/validation/validation.go +++ b/pkg/apis/externaldns/validation/validation.go @@ -65,5 +65,9 @@ func ValidateConfig(cfg *externaldns.Config) error { return errors.New("TTL specified for Dyn is negative") } } + + if cfg.IgnoreHostnameAnnotation && cfg.FQDNTemplate == "" { + return errors.New("FQDN Template must be set if ignoring annotations") + } return nil } diff --git a/pkg/apis/externaldns/validation/validation_test.go b/pkg/apis/externaldns/validation/validation_test.go index c50073ae4..da3d9e300 100644 --- a/pkg/apis/externaldns/validation/validation_test.go +++ b/pkg/apis/externaldns/validation/validation_test.go @@ -116,3 +116,11 @@ func TestValidateGoodDynConfig(t *testing.T) { assert.Nil(t, err, "Configuration should be valid, got this error instead", err) } } + +func TestValidateBadIgnoreHostnameAnnotationsConfig(t *testing.T) { + cfg := externaldns.NewConfig() + cfg.IgnoreHostnameAnnotation = true + cfg.FQDNTemplate = "" + + assert.Error(t, ValidateConfig(cfg)) +} diff --git a/source/gateway.go b/source/gateway.go index 0494f0a4a..09a77800f 100644 --- a/source/gateway.go +++ b/source/gateway.go @@ -38,14 +38,15 @@ import ( // The gateway implementation uses the spec.servers.hosts values for the hostnames. // Use targetAnnotationKey to explicitly set Endpoint. type gatewaySource struct { - kubeClient kubernetes.Interface - istioClient istiomodel.ConfigStore - istioNamespace string - istioIngressGatewayName string - namespace string - annotationFilter string - fqdnTemplate *template.Template - combineFQDNAnnotation bool + kubeClient kubernetes.Interface + istioClient istiomodel.ConfigStore + istioNamespace string + istioIngressGatewayName string + namespace string + annotationFilter string + fqdnTemplate *template.Template + combineFQDNAnnotation bool + ignoreHostnameAnnotation bool } // NewIstioGatewaySource creates a new gatewaySource with the given config. @@ -57,6 +58,7 @@ func NewIstioGatewaySource( annotationFilter string, fqdnTemplate string, combineFqdnAnnotation bool, + ignoreHostnameAnnotation bool, ) (Source, error) { var ( tmpl *template.Template @@ -77,14 +79,15 @@ func NewIstioGatewaySource( } return &gatewaySource{ - kubeClient: kubeClient, - istioClient: istioClient, - istioNamespace: istioNamespace, - istioIngressGatewayName: istioIngressGatewayName, - namespace: namespace, - annotationFilter: annotationFilter, - fqdnTemplate: tmpl, - combineFQDNAnnotation: combineFqdnAnnotation, + kubeClient: kubeClient, + istioClient: istioClient, + istioNamespace: istioNamespace, + istioIngressGatewayName: istioIngressGatewayName, + namespace: namespace, + annotationFilter: annotationFilter, + fqdnTemplate: tmpl, + combineFQDNAnnotation: combineFqdnAnnotation, + ignoreHostnameAnnotation: ignoreHostnameAnnotation, }, nil } @@ -265,9 +268,12 @@ func (sc *gatewaySource) endpointsFromGatewayConfig(config istiomodel.Config) ([ } } - hostnameList := getHostnamesFromAnnotations(config.Annotations) - for _, hostname := range hostnameList { - endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl)...) + // Skip endpoints if we do not want entries from annotations + if !sc.ignoreHostnameAnnotation { + hostnameList := getHostnamesFromAnnotations(config.Annotations) + for _, hostname := range hostnameList { + endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl)...) + } } return endpoints, nil diff --git a/source/gateway_test.go b/source/gateway_test.go index 145694293..c12a8389b 100644 --- a/source/gateway_test.go +++ b/source/gateway_test.go @@ -68,6 +68,7 @@ func (suite *GatewaySuite) SetupTest() { "", "{{.Name}}", false, + false, ) suite.NoError(err, "should initialize gateway source") @@ -141,6 +142,7 @@ func TestNewIstioGatewaySource(t *testing.T) { ti.annotationFilter, ti.fqdnTemplate, ti.combineFQDNAndAnnotation, + false, ) if ti.expectError { assert.Error(t, err) @@ -273,6 +275,7 @@ func testGatewayEndpoints(t *testing.T) { expectError bool fqdnTemplate string combineFQDNAndAnnotation bool + ignoreHostnameAnnotation bool }{ { title: "no gateway", @@ -913,6 +916,51 @@ func testGatewayEndpoints(t *testing.T) { expected: []*endpoint.Endpoint{}, fqdnTemplate: "{{.Name}}.ext-dns.test.com", }, + { + title: "ignore hostname annotations", + ignoreHostnameAnnotation: true, + targetNamespace: "", + ingressGateway: fakeIngressGateway{ + ips: []string{"8.8.8.8"}, + hostnames: []string{"lb.com"}, + }, + configItems: []fakeGatewayConfig{ + { + name: "fake1", + namespace: namespace, + annotations: map[string]string{ + hostnameAnnotationKey: "ignore.me", + }, + dnsnames: [][]string{{"example.org"}}, + }, + { + name: "fake2", + namespace: namespace, + annotations: map[string]string{ + hostnameAnnotationKey: "ignore.me.too", + }, + dnsnames: [][]string{{"new.org"}}, + }, + }, + expected: []*endpoint.Endpoint{ + { + DNSName: "example.org", + Targets: endpoint.Targets{"8.8.8.8"}, + }, + { + DNSName: "example.org", + Targets: endpoint.Targets{"lb.com"}, + }, + { + DNSName: "new.org", + Targets: endpoint.Targets{"8.8.8.8"}, + }, + { + DNSName: "new.org", + Targets: endpoint.Targets{"lb.com"}, + }, + }, + }, } { t.Run(ti.title, func(t *testing.T) { configs := make([]istiomodel.Config, 0) @@ -939,6 +987,7 @@ func testGatewayEndpoints(t *testing.T) { ti.annotationFilter, ti.fqdnTemplate, ti.combineFQDNAndAnnotation, + ti.ignoreHostnameAnnotation, ) require.NoError(t, err) @@ -972,6 +1021,7 @@ func newTestGatewaySource(ingress *v1.Service) (*gatewaySource, error) { "", "{{.Name}}", false, + false, ) if err != nil { return nil, err diff --git a/source/ingress.go b/source/ingress.go index a9ab76cfb..c78c87fd9 100644 --- a/source/ingress.go +++ b/source/ingress.go @@ -38,15 +38,16 @@ import ( // Use targetAnnotationKey to explicitly set Endpoint. (useful if the ingress // controller does not update, or to override with alternative endpoint) type ingressSource struct { - client kubernetes.Interface - namespace string - annotationFilter string - fqdnTemplate *template.Template - combineFQDNAnnotation bool + client kubernetes.Interface + namespace string + annotationFilter string + fqdnTemplate *template.Template + combineFQDNAnnotation bool + ignoreHostnameAnnotation bool } // NewIngressSource creates a new ingressSource with the given config. -func NewIngressSource(kubeClient kubernetes.Interface, namespace, annotationFilter string, fqdnTemplate string, combineFqdnAnnotation bool) (Source, error) { +func NewIngressSource(kubeClient kubernetes.Interface, namespace, annotationFilter string, fqdnTemplate string, combineFqdnAnnotation bool, ignoreHostnameAnnotation bool) (Source, error) { var ( tmpl *template.Template err error @@ -61,11 +62,12 @@ func NewIngressSource(kubeClient kubernetes.Interface, namespace, annotationFilt } return &ingressSource{ - client: kubeClient, - namespace: namespace, - annotationFilter: annotationFilter, - fqdnTemplate: tmpl, - combineFQDNAnnotation: combineFqdnAnnotation, + client: kubeClient, + namespace: namespace, + annotationFilter: annotationFilter, + fqdnTemplate: tmpl, + combineFQDNAnnotation: combineFqdnAnnotation, + ignoreHostnameAnnotation: ignoreHostnameAnnotation, }, nil } @@ -92,7 +94,7 @@ func (sc *ingressSource) Endpoints() ([]*endpoint.Endpoint, error) { continue } - ingEndpoints := endpointsFromIngress(&ing) + ingEndpoints := endpointsFromIngress(&ing, sc.ignoreHostnameAnnotation) // apply template if host is missing on ingress if (sc.combineFQDNAnnotation || len(ingEndpoints) == 0) && sc.fqdnTemplate != nil { @@ -194,7 +196,7 @@ func (sc *ingressSource) setResourceLabel(ingress v1beta1.Ingress, endpoints []* } // endpointsFromIngress extracts the endpoints from ingress object -func endpointsFromIngress(ing *v1beta1.Ingress) []*endpoint.Endpoint { +func endpointsFromIngress(ing *v1beta1.Ingress, ignoreHostnameAnnotation bool) []*endpoint.Endpoint { var endpoints []*endpoint.Endpoint ttl, err := getTTLFromAnnotations(ing.Annotations) @@ -224,11 +226,13 @@ func endpointsFromIngress(ing *v1beta1.Ingress) []*endpoint.Endpoint { } } - hostnameList := getHostnamesFromAnnotations(ing.Annotations) - for _, hostname := range hostnameList { - endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl)...) + // Skip endpoints if we do not want entries from annotations + if !ignoreHostnameAnnotation { + hostnameList := getHostnamesFromAnnotations(ing.Annotations) + for _, hostname := range hostnameList { + endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl)...) + } } - return endpoints } diff --git a/source/ingress_test.go b/source/ingress_test.go index 2b13287d6..13b77735e 100644 --- a/source/ingress_test.go +++ b/source/ingress_test.go @@ -50,6 +50,7 @@ func (suite *IngressSuite) SetupTest() { "", "{{.Name}}", false, + false, ) suite.NoError(err, "should initialize ingress source") @@ -123,6 +124,7 @@ func TestNewIngressSource(t *testing.T) { ti.annotationFilter, ti.fqdnTemplate, ti.combineFQDNAndAnnotation, + false, ) if ti.expectError { assert.Error(t, err) @@ -210,7 +212,7 @@ func testEndpointsFromIngress(t *testing.T) { } { t.Run(ti.title, func(t *testing.T) { realIngress := ti.ingress.Ingress() - validateEndpoints(t, endpointsFromIngress(realIngress), ti.expected) + validateEndpoints(t, endpointsFromIngress(realIngress, false), ti.expected) }) } } @@ -226,6 +228,7 @@ func testIngressEndpoints(t *testing.T) { expectError bool fqdnTemplate string combineFQDNAndAnnotation bool + ignoreHostnameAnnotation bool }{ { title: "no ingress", @@ -888,6 +891,38 @@ func testIngressEndpoints(t *testing.T) { expected: []*endpoint.Endpoint{}, fqdnTemplate: "{{.Name}}.ext-dns.test.com", }, + { + title: "ignore hostname annotation", + targetNamespace: "", + ignoreHostnameAnnotation: true, + ingressItems: []fakeIngress{ + { + name: "fake1", + namespace: namespace, + dnsnames: []string{"example.org"}, + ips: []string{"8.8.8.8"}, + }, + { + name: "fake2", + namespace: namespace, + annotations: map[string]string{ + hostnameAnnotationKey: "dns-through-hostname.com", + }, + dnsnames: []string{"new.org"}, + hostnames: []string{"lb.com"}, + }, + }, + expected: []*endpoint.Endpoint{ + { + DNSName: "example.org", + Targets: endpoint.Targets{"8.8.8.8"}, + }, + { + DNSName: "new.org", + Targets: endpoint.Targets{"lb.com"}, + }, + }, + }, } { t.Run(ti.title, func(t *testing.T) { ingresses := make([]*v1beta1.Ingress, 0) @@ -902,6 +937,7 @@ func testIngressEndpoints(t *testing.T) { ti.annotationFilter, ti.fqdnTemplate, ti.combineFQDNAndAnnotation, + ti.ignoreHostnameAnnotation, ) for _, ingress := range ingresses { _, err := fakeClient.Extensions().Ingresses(ingress.Namespace).Create(ingress) diff --git a/source/service.go b/source/service.go index 28cb1b9cd..a2e2c3e2a 100644 --- a/source/service.go +++ b/source/service.go @@ -48,16 +48,17 @@ type serviceSource struct { namespace string annotationFilter string // process Services with legacy annotations - compatibility string - fqdnTemplate *template.Template - combineFQDNAnnotation bool - publishInternal bool - publishHostIP bool - serviceTypeFilter map[string]struct{} + compatibility string + fqdnTemplate *template.Template + combineFQDNAnnotation bool + ignoreHostnameAnnotation bool + publishInternal bool + publishHostIP bool + serviceTypeFilter map[string]struct{} } // NewServiceSource creates a new serviceSource with the given config. -func NewServiceSource(kubeClient kubernetes.Interface, namespace, annotationFilter string, fqdnTemplate string, combineFqdnAnnotation bool, compatibility string, publishInternal bool, publishHostIP bool, serviceTypeFilter []string) (Source, error) { +func NewServiceSource(kubeClient kubernetes.Interface, namespace, annotationFilter string, fqdnTemplate string, combineFqdnAnnotation bool, compatibility string, publishInternal bool, publishHostIP bool, serviceTypeFilter []string, ignoreHostnameAnnotation bool) (Source, error) { var ( tmpl *template.Template err error @@ -79,15 +80,16 @@ func NewServiceSource(kubeClient kubernetes.Interface, namespace, annotationFilt } return &serviceSource{ - client: kubeClient, - namespace: namespace, - annotationFilter: annotationFilter, - compatibility: compatibility, - fqdnTemplate: tmpl, - combineFQDNAnnotation: combineFqdnAnnotation, - publishInternal: publishInternal, - publishHostIP: publishHostIP, - serviceTypeFilter: serviceTypes, + client: kubeClient, + namespace: namespace, + annotationFilter: annotationFilter, + compatibility: compatibility, + fqdnTemplate: tmpl, + combineFQDNAnnotation: combineFqdnAnnotation, + ignoreHostnameAnnotation: ignoreHostnameAnnotation, + publishInternal: publishInternal, + publishHostIP: publishHostIP, + serviceTypeFilter: serviceTypes, }, nil } @@ -230,9 +232,12 @@ func (sc *serviceSource) endpointsFromTemplate(svc *v1.Service, nodeTargets endp func (sc *serviceSource) endpoints(svc *v1.Service, nodeTargets endpoint.Targets) []*endpoint.Endpoint { var endpoints []*endpoint.Endpoint - hostnameList := getHostnamesFromAnnotations(svc.Annotations) - for _, hostname := range hostnameList { - endpoints = append(endpoints, sc.generateEndpoints(svc, hostname, nodeTargets)...) + // Skip endpoints if we do not want entries from annotations + if !sc.ignoreHostnameAnnotation { + hostnameList := getHostnamesFromAnnotations(svc.Annotations) + for _, hostname := range hostnameList { + endpoints = append(endpoints, sc.generateEndpoints(svc, hostname, nodeTargets)...) + } } return endpoints diff --git a/source/service_test.go b/source/service_test.go index edc4666ab..4036c5721 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -51,6 +51,7 @@ func (suite *ServiceSuite) SetupTest() { false, false, []string{}, + false, ) suite.fooWithTargets = &v1.Service{ Spec: v1.ServiceSpec{ @@ -142,6 +143,7 @@ func testServiceSourceNewServiceSource(t *testing.T) { false, false, ti.serviceTypesFilter, + false, ) if ti.expectError { @@ -165,6 +167,7 @@ func testServiceSourceEndpoints(t *testing.T) { compatibility string fqdnTemplate string combineFQDNAndAnnotation bool + ignoreHostnameAnnotation bool labels map[string]string annotations map[string]string clusterIP string @@ -183,6 +186,26 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, + map[string]string{}, + map[string]string{}, + "", + []string{"1.2.3.4"}, + []string{}, + []*endpoint.Endpoint{}, + false, + }, + { + "no annotated services return no endpoints when ignoreing annotations", + "", + "", + "testing", + "foo", + v1.ServiceTypeLoadBalancer, + "", + "", + false, + true, map[string]string{}, map[string]string{}, "", @@ -201,6 +224,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -213,6 +237,27 @@ func testServiceSourceEndpoints(t *testing.T) { }, false, }, + { + "hostname annotation on services is ignored", + "", + "", + "testing", + "foo", + v1.ServiceTypeLoadBalancer, + "", + "", + false, + true, + map[string]string{}, + map[string]string{ + hostnameAnnotationKey: "foo.example.org.", + }, + "", + []string{"1.2.3.4"}, + []string{}, + []*endpoint.Endpoint{}, + false, + }, { "annotated ClusterIp aren't processed without explicit authorization", "", @@ -223,6 +268,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -243,6 +289,29 @@ func testServiceSourceEndpoints(t *testing.T) { "", "{{.Name}}.fqdn.org,{{.Name}}.fqdn.com", false, + false, + map[string]string{}, + map[string]string{}, + "", + []string{"1.2.3.4"}, + []string{}, + []*endpoint.Endpoint{ + {DNSName: "foo.fqdn.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.fqdn.com", Targets: endpoint.Targets{"1.2.3.4"}}, + }, + false, + }, + { + "FQDN template with multiple hostnames return an endpoint with target IP when ignoreing annotations", + "", + "", + "testing", + "foo", + v1.ServiceTypeLoadBalancer, + "", + "{{.Name}}.fqdn.org,{{.Name}}.fqdn.com", + false, + true, map[string]string{}, map[string]string{}, "", @@ -264,6 +333,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "{{.Name}}.fqdn.org,{{.Name}}.fqdn.com", true, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org., bar.example.org.", @@ -279,6 +349,30 @@ func testServiceSourceEndpoints(t *testing.T) { }, false, }, + { + "FQDN template and annotation both with multiple hostnames while ignoring annotations will only return FQDN endpoints", + "", + "", + "testing", + "foo", + v1.ServiceTypeLoadBalancer, + "", + "{{.Name}}.fqdn.org,{{.Name}}.fqdn.com", + true, + true, + map[string]string{}, + map[string]string{ + hostnameAnnotationKey: "foo.example.org., bar.example.org.", + }, + "", + []string{"1.2.3.4"}, + []string{}, + []*endpoint.Endpoint{ + {DNSName: "foo.fqdn.org", Targets: endpoint.Targets{"1.2.3.4"}}, + {DNSName: "foo.fqdn.com", Targets: endpoint.Targets{"1.2.3.4"}}, + }, + false, + }, { "annotated services with multiple hostnames return an endpoint with target IP", "", @@ -289,6 +383,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org., bar.example.org.", @@ -312,6 +407,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org, bar.example.org", @@ -335,6 +431,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -357,6 +454,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org", // Trailing dot is omitted @@ -380,6 +478,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ controllerAnnotationKey: controllerAnnotationValue, @@ -403,6 +502,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "{{.Name}}.ext-dns.test.com", false, + false, map[string]string{}, map[string]string{ controllerAnnotationKey: "some-other-tool", @@ -424,6 +524,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -446,6 +547,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -466,6 +568,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -488,6 +591,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -511,6 +615,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -532,6 +637,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -553,6 +659,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -576,6 +683,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -597,6 +705,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -617,6 +726,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -639,6 +749,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ "zalando.org/dnsname": "foo.example.org.", @@ -659,6 +770,7 @@ func testServiceSourceEndpoints(t *testing.T) { "mate", "", false, + false, map[string]string{}, map[string]string{ "zalando.org/dnsname": "foo.example.org.", @@ -681,6 +793,7 @@ func testServiceSourceEndpoints(t *testing.T) { "molecule", "", false, + false, map[string]string{ "dns": "route53", }, @@ -706,6 +819,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "{{.Name}}.bar.example.com", false, + false, map[string]string{}, map[string]string{}, "", @@ -727,6 +841,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "{{.Name}}.bar.example.com", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -750,6 +865,7 @@ func testServiceSourceEndpoints(t *testing.T) { "mate", "{{.Name}}.bar.example.com", false, + false, map[string]string{}, map[string]string{ "zalando.org/dnsname": "mate.example.org.", @@ -772,6 +888,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "{{.Calibre}}.bar.example.com", false, + false, map[string]string{}, map[string]string{}, "", @@ -790,6 +907,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -812,6 +930,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -835,6 +954,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -858,6 +978,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -881,6 +1002,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -903,6 +1025,7 @@ func testServiceSourceEndpoints(t *testing.T) { "", "", false, + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -960,6 +1083,7 @@ func testServiceSourceEndpoints(t *testing.T) { false, false, tc.serviceTypesFilter, + tc.ignoreHostnameAnnotation, ) require.NoError(t, err) @@ -979,20 +1103,21 @@ func testServiceSourceEndpoints(t *testing.T) { // testServiceSourceEndpoints tests that various services generate the correct endpoints. func TestClusterIpServices(t *testing.T) { for _, tc := range []struct { - title string - targetNamespace string - annotationFilter string - svcNamespace string - svcName string - svcType v1.ServiceType - compatibility string - fqdnTemplate string - labels map[string]string - annotations map[string]string - clusterIP string - lbs []string - expected []*endpoint.Endpoint - expectError bool + title string + targetNamespace string + annotationFilter string + svcNamespace string + svcName string + svcType v1.ServiceType + compatibility string + fqdnTemplate string + ignoreHostnameAnnotation bool + labels map[string]string + annotations map[string]string + clusterIP string + lbs []string + expected []*endpoint.Endpoint + expectError bool }{ { "annotated ClusterIp services return an endpoint with Cluster IP", @@ -1003,6 +1128,7 @@ func TestClusterIpServices(t *testing.T) { v1.ServiceTypeClusterIP, "", "", + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -1014,6 +1140,25 @@ func TestClusterIpServices(t *testing.T) { }, false, }, + { + "hostname annotated ClusterIp services are ignored", + "", + "", + "testing", + "foo", + v1.ServiceTypeClusterIP, + "", + "", + true, + map[string]string{}, + map[string]string{ + hostnameAnnotationKey: "foo.example.org.", + }, + "1.2.3.4", + []string{}, + []*endpoint.Endpoint{}, + false, + }, { "non-annotated ClusterIp services with set fqdnTemplate return an endpoint with target IP", "", @@ -1023,6 +1168,7 @@ func TestClusterIpServices(t *testing.T) { v1.ServiceTypeClusterIP, "", "{{.Name}}.bar.example.com", + false, map[string]string{}, map[string]string{}, "4.5.6.7", @@ -1041,6 +1187,7 @@ func TestClusterIpServices(t *testing.T) { v1.ServiceTypeClusterIP, "", "", + false, map[string]string{}, map[string]string{}, v1.ClusterIPNone, @@ -1095,6 +1242,7 @@ func TestClusterIpServices(t *testing.T) { true, false, []string{}, + tc.ignoreHostnameAnnotation, ) require.NoError(t, err) @@ -1114,20 +1262,21 @@ func TestClusterIpServices(t *testing.T) { // testNodePortServices tests that various services generate the correct endpoints. func TestNodePortServices(t *testing.T) { for _, tc := range []struct { - title string - targetNamespace string - annotationFilter string - svcNamespace string - svcName string - svcType v1.ServiceType - compatibility string - fqdnTemplate string - labels map[string]string - annotations map[string]string - lbs []string - expected []*endpoint.Endpoint - expectError bool - nodes []*v1.Node + title string + targetNamespace string + annotationFilter string + svcNamespace string + svcName string + svcType v1.ServiceType + compatibility string + fqdnTemplate string + ignoreHostnameAnnotation bool + labels map[string]string + annotations map[string]string + lbs []string + expected []*endpoint.Endpoint + expectError bool + nodes []*v1.Node }{ { "annotated NodePort services return an endpoint with IP addresses of the cluster's nodes", @@ -1138,6 +1287,7 @@ func TestNodePortServices(t *testing.T) { v1.ServiceTypeNodePort, "", "", + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -1170,6 +1320,45 @@ func TestNodePortServices(t *testing.T) { }, }}, }, + { + "hostname annotated NodePort services are ignored", + "", + "", + "testing", + "foo", + v1.ServiceTypeNodePort, + "", + "", + true, + map[string]string{}, + map[string]string{ + hostnameAnnotationKey: "foo.example.org.", + }, + nil, + []*endpoint.Endpoint{}, + false, + []*v1.Node{{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node1", + }, + Status: v1.NodeStatus{ + Addresses: []v1.NodeAddress{ + {Type: v1.NodeExternalIP, Address: "54.10.11.1"}, + {Type: v1.NodeInternalIP, Address: "10.0.1.1"}, + }, + }, + }, { + ObjectMeta: metav1.ObjectMeta{ + Name: "node2", + }, + Status: v1.NodeStatus{ + Addresses: []v1.NodeAddress{ + {Type: v1.NodeExternalIP, Address: "54.10.11.2"}, + {Type: v1.NodeInternalIP, Address: "10.0.1.2"}, + }, + }, + }}, + }, { "non-annotated NodePort services with set fqdnTemplate return an endpoint with target IP", "", @@ -1179,6 +1368,7 @@ func TestNodePortServices(t *testing.T) { v1.ServiceTypeNodePort, "", "{{.Name}}.bar.example.com", + false, map[string]string{}, map[string]string{}, nil, @@ -1218,6 +1408,7 @@ func TestNodePortServices(t *testing.T) { v1.ServiceTypeNodePort, "", "", + false, map[string]string{}, map[string]string{ hostnameAnnotationKey: "foo.example.org.", @@ -1292,6 +1483,7 @@ func TestNodePortServices(t *testing.T) { true, false, []string{}, + tc.ignoreHostnameAnnotation, ) require.NoError(t, err) @@ -1311,24 +1503,25 @@ func TestNodePortServices(t *testing.T) { // TestHeadlessServices tests that headless services generate the correct endpoints. func TestHeadlessServices(t *testing.T) { for _, tc := range []struct { - title string - targetNamespace string - svcNamespace string - svcName string - svcType v1.ServiceType - compatibility string - fqdnTemplate string - labels map[string]string - annotations map[string]string - clusterIP string - podIP string - selector map[string]string - lbs []string - podnames []string - hostnames []string - phases []v1.PodPhase - expected []*endpoint.Endpoint - expectError bool + title string + targetNamespace string + svcNamespace string + svcName string + svcType v1.ServiceType + compatibility string + fqdnTemplate string + ignoreHostnameAnnotation bool + labels map[string]string + annotations map[string]string + clusterIP string + podIP string + selector map[string]string + lbs []string + podnames []string + hostnames []string + phases []v1.PodPhase + expected []*endpoint.Endpoint + expectError bool }{ { "annotated Headless services return endpoints for each selected Pod", @@ -1338,6 +1531,7 @@ func TestHeadlessServices(t *testing.T) { v1.ServiceTypeClusterIP, "", "", + false, map[string]string{"component": "foo"}, map[string]string{ hostnameAnnotationKey: "service.example.org", @@ -1357,6 +1551,31 @@ func TestHeadlessServices(t *testing.T) { }, false, }, + { + "hostname annotated Headless services are ignored", + "", + "testing", + "foo", + v1.ServiceTypeClusterIP, + "", + "", + true, + map[string]string{"component": "foo"}, + map[string]string{ + hostnameAnnotationKey: "service.example.org", + }, + v1.ClusterIPNone, + "1.1.1.1", + map[string]string{ + "component": "foo", + }, + []string{}, + []string{"foo-0", "foo-1"}, + []string{"foo-0", "foo-1"}, + []v1.PodPhase{v1.PodRunning, v1.PodRunning}, + []*endpoint.Endpoint{}, + false, + }, { "annotated Headless services return endpoints with TTL for each selected Pod", "", @@ -1365,6 +1584,7 @@ func TestHeadlessServices(t *testing.T) { v1.ServiceTypeClusterIP, "", "", + false, map[string]string{"component": "foo"}, map[string]string{ hostnameAnnotationKey: "service.example.org", @@ -1393,6 +1613,7 @@ func TestHeadlessServices(t *testing.T) { v1.ServiceTypeClusterIP, "", "", + false, map[string]string{"component": "foo"}, map[string]string{ hostnameAnnotationKey: "service.example.org", @@ -1419,6 +1640,7 @@ func TestHeadlessServices(t *testing.T) { v1.ServiceTypeClusterIP, "", "", + false, map[string]string{"component": "foo"}, map[string]string{ hostnameAnnotationKey: "service.example.org", @@ -1493,6 +1715,7 @@ func TestHeadlessServices(t *testing.T) { true, false, []string{}, + tc.ignoreHostnameAnnotation, ) require.NoError(t, err) @@ -1512,24 +1735,25 @@ func TestHeadlessServices(t *testing.T) { // TestHeadlessServices tests that headless services generate the correct endpoints. func TestHeadlessServicesHostIP(t *testing.T) { for _, tc := range []struct { - title string - targetNamespace string - svcNamespace string - svcName string - svcType v1.ServiceType - compatibility string - fqdnTemplate string - labels map[string]string - annotations map[string]string - clusterIP string - hostIP string - selector map[string]string - lbs []string - podnames []string - hostnames []string - phases []v1.PodPhase - expected []*endpoint.Endpoint - expectError bool + title string + targetNamespace string + svcNamespace string + svcName string + svcType v1.ServiceType + compatibility string + fqdnTemplate string + ignoreHostnameAnnotation bool + labels map[string]string + annotations map[string]string + clusterIP string + hostIP string + selector map[string]string + lbs []string + podnames []string + hostnames []string + phases []v1.PodPhase + expected []*endpoint.Endpoint + expectError bool }{ { "annotated Headless services return endpoints for each selected Pod", @@ -1539,6 +1763,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { v1.ServiceTypeClusterIP, "", "", + false, map[string]string{"component": "foo"}, map[string]string{ hostnameAnnotationKey: "service.example.org", @@ -1558,6 +1783,31 @@ func TestHeadlessServicesHostIP(t *testing.T) { }, false, }, + { + "hostname annotated Headless services are ignored", + "", + "testing", + "foo", + v1.ServiceTypeClusterIP, + "", + "", + true, + map[string]string{"component": "foo"}, + map[string]string{ + hostnameAnnotationKey: "service.example.org", + }, + v1.ClusterIPNone, + "1.1.1.1", + map[string]string{ + "component": "foo", + }, + []string{}, + []string{"foo-0", "foo-1"}, + []string{"foo-0", "foo-1"}, + []v1.PodPhase{v1.PodRunning, v1.PodRunning}, + []*endpoint.Endpoint{}, + false, + }, { "annotated Headless services return endpoints with TTL for each selected Pod", "", @@ -1566,6 +1816,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { v1.ServiceTypeClusterIP, "", "", + false, map[string]string{"component": "foo"}, map[string]string{ hostnameAnnotationKey: "service.example.org", @@ -1594,6 +1845,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { v1.ServiceTypeClusterIP, "", "", + false, map[string]string{"component": "foo"}, map[string]string{ hostnameAnnotationKey: "service.example.org", @@ -1620,6 +1872,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { v1.ServiceTypeClusterIP, "", "", + false, map[string]string{"component": "foo"}, map[string]string{ hostnameAnnotationKey: "service.example.org", @@ -1694,6 +1947,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { true, true, []string{}, + tc.ignoreHostnameAnnotation, ) require.NoError(t, err) @@ -1734,7 +1988,7 @@ func BenchmarkServiceEndpoints(b *testing.B) { _, err := kubernetes.CoreV1().Services(service.Namespace).Create(service) require.NoError(b, err) - client, err := NewServiceSource(kubernetes, v1.NamespaceAll, "", "", false, "", false, false, []string{}) + client, err := NewServiceSource(kubernetes, v1.NamespaceAll, "", "", false, "", false, false, []string{}, false) require.NoError(b, err) for i := 0; i < b.N; i++ { diff --git a/source/store.go b/source/store.go index 10ab64eb7..01d3ef76f 100644 --- a/source/store.go +++ b/source/store.go @@ -42,6 +42,7 @@ type Config struct { AnnotationFilter string FQDNTemplate string CombineFQDNAndAnnotation bool + IgnoreHostnameAnnotation bool Compatibility string PublishInternal bool PublishHostIP bool @@ -112,13 +113,13 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err if err != nil { return nil, err } - return NewServiceSource(client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.Compatibility, cfg.PublishInternal, cfg.PublishHostIP, cfg.ServiceTypeFilter) + return NewServiceSource(client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.Compatibility, cfg.PublishInternal, cfg.PublishHostIP, cfg.ServiceTypeFilter, cfg.IgnoreHostnameAnnotation) case "ingress": client, err := p.KubeClient() if err != nil { return nil, err } - return NewIngressSource(client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation) + return NewIngressSource(client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation) case "istio-gateway": kubernetesClient, err := p.KubeClient() if err != nil { @@ -128,7 +129,7 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err if err != nil { return nil, err } - return NewIstioGatewaySource(kubernetesClient, istioClient, cfg.IstioIngressGateway, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation) + return NewIstioGatewaySource(kubernetesClient, istioClient, cfg.IstioIngressGateway, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation) case "fake": return NewFakeSource(cfg.FQDNTemplate) case "connector": From 06f315cae8d61e302e174df86af0412750989f56 Mon Sep 17 00:00:00 2001 From: Przemyslaw Bak Date: Mon, 5 Nov 2018 23:49:25 +0000 Subject: [PATCH 003/136] Tiny clarification about two available deployment methods. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index a411ac638..e02fbf216 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,10 @@ Note that all flags can be replaced with environment variables; for instance, `--dry-run` could be replaced with `EXTERNAL_DNS_DRY_RUN=1`, or `--registry txt` could be replaced with `EXTERNAL_DNS_REGISTRY=txt`. +## There are two ways of running ExternalDNS: +* Deploying to a Cluster +* Running Locally + ## Deploying to a Cluster The following tutorials are provided: From e0e7a9defd4286e4aa53d51f81280baa25ca320b Mon Sep 17 00:00:00 2001 From: Erik Swets Date: Fri, 6 Apr 2018 12:12:11 +0200 Subject: [PATCH 004/136] Allow setting Cloudflare proxying by annotation --- docs/contributing/crd-source.md | 6 +- .../contributing/crd-source/crd-manifest.yaml | 9 ++- docs/tutorials/cloudflare.md | 4 ++ endpoint/endpoint.go | 21 ++++++- internal/testutils/endpoint.go | 19 ++---- internal/testutils/endpoint_test.go | 22 ++++--- provider/aws.go | 6 +- provider/aws_test.go | 16 +++-- provider/cloudflare.go | 62 +++++++++++++------ provider/cloudflare_test.go | 30 +++++++++ source/ingress.go | 1 - source/service.go | 30 +++++---- source/source.go | 25 ++++++-- 13 files changed, 179 insertions(+), 72 deletions(-) diff --git a/docs/contributing/crd-source.md b/docs/contributing/crd-source.md index 06fbea80a..ca43503d3 100644 --- a/docs/contributing/crd-source.md +++ b/docs/contributing/crd-source.md @@ -14,7 +14,11 @@ Here is typical example of [CRD API type](https://github.com/kubernetes-incubato ```go type TTL int64 type Targets []string -type ProviderSpecific map[string]string +type ProviderSpecificProperty struct { + Name string + Value string +} +type ProviderSpecific []ProviderSpecificProperty type Endpoint struct { // The hostname of the DNS record diff --git a/docs/contributing/crd-source/crd-manifest.yaml b/docs/contributing/crd-source/crd-manifest.yaml index 258404575..00b52f34c 100644 --- a/docs/contributing/crd-source/crd-manifest.yaml +++ b/docs/contributing/crd-source/crd-manifest.yaml @@ -33,7 +33,14 @@ spec: labels: type: object providerSpecific: - type: object + items: + properties: + name: + type: string + value: + type: string + type: object + type: array recordTTL: format: int64 type: integer diff --git a/docs/tutorials/cloudflare.md b/docs/tutorials/cloudflare.md index 308cb8186..2a16d1a35 100644 --- a/docs/tutorials/cloudflare.md +++ b/docs/tutorials/cloudflare.md @@ -196,3 +196,7 @@ Now that we have verified that ExternalDNS will automatically manage Cloudflare $ kubectl delete service -f nginx.yaml $ kubectl delete service -f externaldns.yaml ``` + +## Setting cloudflare-proxied on a per-ingress basis + +Using the `external-dns.alpha.kubernetes.io/cloudflare-proxied: "true"` annotation on your ingress, you can specify if the proxy feature of Cloudflare should be enabled for that record. This setting will override the global `--cloudflare-proxied` setting. diff --git a/endpoint/endpoint.go b/endpoint/endpoint.go index ca77f36e2..2d292fb0d 100644 --- a/endpoint/endpoint.go +++ b/endpoint/endpoint.go @@ -109,8 +109,14 @@ func (t Targets) IsLess(o Targets) bool { return false } +// ProviderSpecificProperty holds the name and value of a configuration which is specific to individual DNS providers +type ProviderSpecificProperty struct { + Name string `json:"name,omitempty"` + Value string `json:"value,omitempty"` +} + // ProviderSpecific holds configuration which is specific to individual DNS providers -type ProviderSpecific map[string]string +type ProviderSpecific []ProviderSpecificProperty // Endpoint is a high-level way of a connection between a service and an IP type Endpoint struct { @@ -160,10 +166,21 @@ func (e *Endpoint) WithProviderSpecific(key, value string) *Endpoint { if e.ProviderSpecific == nil { e.ProviderSpecific = ProviderSpecific{} } - e.ProviderSpecific[key] = value + + e.ProviderSpecific = append(e.ProviderSpecific, ProviderSpecificProperty{Name: key, Value: value}) return e } +// GetProviderSpecificProperty returns a ProviderSpecificProperty if the property exists. +func (e *Endpoint) GetProviderSpecificProperty(key string) (ProviderSpecificProperty, bool) { + for _, providerSpecific := range e.ProviderSpecific { + if providerSpecific.Name == key { + return providerSpecific, true + } + } + return ProviderSpecificProperty{}, false +} + func (e *Endpoint) String() string { return fmt.Sprintf("%s %d IN %s %s %s", e.DNSName, e.RecordTTL, e.RecordType, e.Targets, e.ProviderSpecific) } diff --git a/internal/testutils/endpoint.go b/internal/testutils/endpoint.go index f13a6b4c7..d804c75ea 100644 --- a/internal/testutils/endpoint.go +++ b/internal/testutils/endpoint.go @@ -17,6 +17,7 @@ limitations under the License. package testutils import ( + "reflect" "sort" "github.com/kubernetes-incubator/external-dns/endpoint" @@ -49,7 +50,7 @@ func SameEndpoint(a, b *endpoint.Endpoint) bool { return a.DNSName == b.DNSName && a.Targets.Same(b.Targets) && a.RecordType == b.RecordType && a.Labels[endpoint.OwnerLabelKey] == b.Labels[endpoint.OwnerLabelKey] && a.RecordTTL == b.RecordTTL && a.Labels[endpoint.ResourceLabelKey] == b.Labels[endpoint.ResourceLabelKey] && - SameMap(a.ProviderSpecific, b.ProviderSpecific) + SameProverSpecific(a.ProviderSpecific, b.ProviderSpecific) } // SameEndpoints compares two slices of endpoints regardless of order @@ -81,17 +82,7 @@ func SamePlanChanges(a, b map[string][]*endpoint.Endpoint) bool { SameEndpoints(a["UpdateOld"], b["UpdateOld"]) && SameEndpoints(a["UpdateNew"], b["UpdateNew"]) } -// SameMap verifies that two maps contain the same string/string key/value pairs -func SameMap(a, b map[string]string) bool { - if len(a) != len(b) { - return false - } - - for k, v := range a { - if v != b[k] { - return false - } - } - - return true +// SameProverSpecific verifies that two maps contain the same string/string key/value pairs +func SameProverSpecific(a, b endpoint.ProviderSpecific) bool { + return reflect.DeepEqual(a, b) } diff --git a/internal/testutils/endpoint_test.go b/internal/testutils/endpoint_test.go index f14ae655c..2f1204fd6 100644 --- a/internal/testutils/endpoint_test.go +++ b/internal/testutils/endpoint_test.go @@ -56,9 +56,11 @@ func ExampleSameEndpoints() { RecordTTL: endpoint.TTL(60), }, { - DNSName: "example.org", - Targets: endpoint.Targets{"load-balancer.org"}, - ProviderSpecific: endpoint.ProviderSpecific{"foo": "bar"}, + DNSName: "example.org", + Targets: endpoint.Targets{"load-balancer.org"}, + ProviderSpecific: endpoint.ProviderSpecific{ + endpoint.ProviderSpecificProperty{Name: "foo", Value: "bar"}, + }, }, } sort.Sort(byAllFields(eps)) @@ -66,11 +68,11 @@ func ExampleSameEndpoints() { fmt.Println(ep) } // Output: - // abc.com 0 IN A 1.2.3.4 map[] - // abc.com 0 IN TXT something map[] - // bbc.com 0 IN CNAME foo.com map[] - // cbc.com 60 IN CNAME foo.com map[] - // example.org 0 IN load-balancer.org map[] - // example.org 0 IN load-balancer.org map[foo:bar] - // example.org 0 IN TXT load-balancer.org map[] + // abc.com 0 IN A 1.2.3.4 [] + // abc.com 0 IN TXT something [] + // bbc.com 0 IN CNAME foo.com [] + // cbc.com 60 IN CNAME foo.com [] + // example.org 0 IN load-balancer.org [] + // example.org 0 IN load-balancer.org [{foo bar}] + // example.org 0 IN TXT load-balancer.org [] } diff --git a/provider/aws.go b/provider/aws.go index 7ec5e79aa..33fd0b5cc 100644 --- a/provider/aws.go +++ b/provider/aws.go @@ -371,8 +371,8 @@ func (p *AWSProvider) newChange(action string, endpoint *endpoint.Endpoint) *rou if isAWSLoadBalancer(endpoint) { evalTargetHealth := p.evaluateTargetHealth - if _, ok := endpoint.ProviderSpecific[providerSpecificEvaluateTargetHealth]; ok { - evalTargetHealth = endpoint.ProviderSpecific[providerSpecificEvaluateTargetHealth] == "true" + if prop, ok := endpoint.GetProviderSpecificProperty(providerSpecificEvaluateTargetHealth); ok { + evalTargetHealth = prop.Value == "true" } change.ResourceRecordSet.Type = aws.String(route53.RRTypeA) @@ -549,7 +549,7 @@ func isAWSLoadBalancer(ep *endpoint.Endpoint) bool { // isAWSAlias determines if a given hostname belongs to an AWS Alias record by doing an reverse lookup. func isAWSAlias(ep *endpoint.Endpoint, addrs []*endpoint.Endpoint) string { - if val, exists := ep.ProviderSpecific["alias"]; ep.RecordType == endpoint.RecordTypeCNAME && exists && val == "true" { + if prop, exists := ep.GetProviderSpecificProperty("alias"); ep.RecordType == endpoint.RecordTypeCNAME && exists && prop.Value == "true" { for _, addr := range addrs { if addr.DNSName == ep.Targets[0] { if hostedZone := canonicalHostedZone(addr.Targets[0]); hostedZone != "" { diff --git a/provider/aws_test.go b/provider/aws_test.go index 6852b6df2..d1a37cc89 100644 --- a/provider/aws_test.go +++ b/provider/aws_test.go @@ -781,7 +781,10 @@ func TestAWSCreateRecordsWithALIAS(t *testing.T) { Targets: endpoint.Targets{"foo.eu-central-1.elb.amazonaws.com"}, RecordType: endpoint.RecordTypeCNAME, ProviderSpecific: endpoint.ProviderSpecific{ - providerSpecificEvaluateTargetHealth: key, + endpoint.ProviderSpecificProperty{ + Name: providerSpecificEvaluateTargetHealth, + Value: key, + }, }, }, } @@ -832,9 +835,14 @@ func TestAWSisAWSAlias(t *testing.T) { {"foo.example.org", endpoint.RecordTypeCNAME, "true", ""}, } { ep := &endpoint.Endpoint{ - Targets: endpoint.Targets{tc.target}, - RecordType: tc.recordType, - ProviderSpecific: map[string]string{"alias": tc.alias}, + Targets: endpoint.Targets{tc.target}, + RecordType: tc.recordType, + ProviderSpecific: endpoint.ProviderSpecific{ + endpoint.ProviderSpecificProperty{ + Name: "alias", + Value: tc.alias, + }, + }, } addrs := []*endpoint.Endpoint{ { diff --git a/provider/cloudflare.go b/provider/cloudflare.go index 75e24f414..ffb6bf81c 100644 --- a/provider/cloudflare.go +++ b/provider/cloudflare.go @@ -19,6 +19,7 @@ package provider import ( "fmt" "os" + "strconv" "strings" cloudflare "github.com/cloudflare/cloudflare-go" @@ -26,6 +27,7 @@ import ( "github.com/kubernetes-incubator/external-dns/endpoint" "github.com/kubernetes-incubator/external-dns/plan" + "github.com/kubernetes-incubator/external-dns/source" ) const ( @@ -93,10 +95,10 @@ func (z zoneService) DeleteDNSRecord(zoneID, recordID string) error { type CloudFlareProvider struct { Client cloudFlareDNS // only consider hosted zones managing domains ending in this suffix - domainFilter DomainFilter - zoneIDFilter ZoneIDFilter - proxied bool - DryRun bool + domainFilter DomainFilter + zoneIDFilter ZoneIDFilter + proxiedByDefault bool + DryRun bool } // cloudFlareChange differentiates between ChangActions @@ -106,7 +108,7 @@ type cloudFlareChange struct { } // NewCloudFlareProvider initializes a new CloudFlare DNS based Provider. -func NewCloudFlareProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, proxied bool, dryRun bool) (*CloudFlareProvider, error) { +func NewCloudFlareProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, proxiedByDefault bool, dryRun bool) (*CloudFlareProvider, error) { // initialize via API email and API key and returns new API object config, err := cloudflare.New(os.Getenv("CF_API_KEY"), os.Getenv("CF_API_EMAIL")) if err != nil { @@ -114,11 +116,11 @@ func NewCloudFlareProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, } provider := &CloudFlareProvider{ //Client: config, - Client: zoneService{config}, - domainFilter: domainFilter, - zoneIDFilter: zoneIDFilter, - proxied: proxied, - DryRun: dryRun, + Client: zoneService{config}, + domainFilter: domainFilter, + zoneIDFilter: zoneIDFilter, + proxiedByDefault: proxiedByDefault, + DryRun: dryRun, } return provider, nil } @@ -173,11 +175,13 @@ func (p *CloudFlareProvider) Records() ([]*endpoint.Endpoint, error) { // ApplyChanges applies a given set of changes in a given zone. func (p *CloudFlareProvider) ApplyChanges(changes *plan.Changes) error { + proxiedByDefault := p.proxiedByDefault + combinedChanges := make([]*cloudFlareChange, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete)) - combinedChanges = append(combinedChanges, newCloudFlareChanges(cloudFlareCreate, changes.Create, p.proxied)...) - combinedChanges = append(combinedChanges, newCloudFlareChanges(cloudFlareUpdate, changes.UpdateNew, p.proxied)...) - combinedChanges = append(combinedChanges, newCloudFlareChanges(cloudFlareDelete, changes.Delete, p.proxied)...) + combinedChanges = append(combinedChanges, newCloudFlareChanges(cloudFlareCreate, changes.Create, proxiedByDefault)...) + combinedChanges = append(combinedChanges, newCloudFlareChanges(cloudFlareUpdate, changes.UpdateNew, proxiedByDefault)...) + combinedChanges = append(combinedChanges, newCloudFlareChanges(cloudFlareDelete, changes.Delete, proxiedByDefault)...) return p.submitChanges(combinedChanges) } @@ -270,21 +274,20 @@ func (p *CloudFlareProvider) getRecordID(records []cloudflare.DNSRecord, record } // newCloudFlareChanges returns a collection of Changes based on the given records and action. -func newCloudFlareChanges(action string, endpoints []*endpoint.Endpoint, proxied bool) []*cloudFlareChange { +func newCloudFlareChanges(action string, endpoints []*endpoint.Endpoint, proxiedByDefault bool) []*cloudFlareChange { changes := make([]*cloudFlareChange, 0, len(endpoints)) for _, endpoint := range endpoints { - changes = append(changes, newCloudFlareChange(action, endpoint, proxied)) + changes = append(changes, newCloudFlareChange(action, endpoint, proxiedByDefault)) } return changes } -func newCloudFlareChange(action string, endpoint *endpoint.Endpoint, proxied bool) *cloudFlareChange { +func newCloudFlareChange(action string, endpoint *endpoint.Endpoint, proxiedByDefault bool) *cloudFlareChange { ttl := defaultCloudFlareRecordTTL - if proxied && (cloudFlareTypeNotSupported[endpoint.RecordType] || strings.Contains(endpoint.DNSName, "*")) { - proxied = false - } + proxied := shouldBeProxied(endpoint, proxiedByDefault) + if endpoint.RecordTTL.IsConfigured() { ttl = int(endpoint.RecordTTL) } @@ -300,3 +303,24 @@ func newCloudFlareChange(action string, endpoint *endpoint.Endpoint, proxied boo }, } } + +func shouldBeProxied(endpoint *endpoint.Endpoint, proxiedByDefault bool) bool { + proxied := proxiedByDefault + + for _, v := range endpoint.ProviderSpecific { + if v.Name == source.CloudflareProxiedKey { + b, err := strconv.ParseBool(v.Value) + if err != nil { + log.Errorf("Failed to parse annotation [%s]: %v", source.CloudflareProxiedKey, err) + } else { + proxied = b + } + break + } + } + + if cloudFlareTypeNotSupported[endpoint.RecordType] || strings.Contains(endpoint.DNSName, "*") { + proxied = false + } + return proxied +} diff --git a/provider/cloudflare_test.go b/provider/cloudflare_test.go index cfa54ca21..00389e9ce 100644 --- a/provider/cloudflare_test.go +++ b/provider/cloudflare_test.go @@ -368,6 +368,36 @@ func TestNewCloudFlareChangeNoProxied(t *testing.T) { assert.False(t, change.ResourceRecordSet.Proxied) } +func TestNewCloudFlareProxiedAnnotationTrue(t *testing.T) { + change := newCloudFlareChange(cloudFlareCreate, &endpoint.Endpoint{DNSName: "new", RecordType: "A", Targets: endpoint.Targets{"target"}, ProviderSpecific: endpoint.ProviderSpecific{ + endpoint.ProviderSpecificProperty{ + Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied", + Value: "true", + }, + }}, false) + assert.True(t, change.ResourceRecordSet.Proxied) +} + +func TestNewCloudFlareProxiedAnnotationFalse(t *testing.T) { + change := newCloudFlareChange(cloudFlareCreate, &endpoint.Endpoint{DNSName: "new", RecordType: "A", Targets: endpoint.Targets{"target"}, ProviderSpecific: endpoint.ProviderSpecific{ + endpoint.ProviderSpecificProperty{ + Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied", + Value: "false", + }, + }}, true) + assert.False(t, change.ResourceRecordSet.Proxied) +} + +func TestNewCloudFlareProxiedAnnotationIllegalValue(t *testing.T) { + change := newCloudFlareChange(cloudFlareCreate, &endpoint.Endpoint{DNSName: "new", RecordType: "A", Targets: endpoint.Targets{"target"}, ProviderSpecific: endpoint.ProviderSpecific{ + endpoint.ProviderSpecificProperty{ + Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied", + Value: "asdaslkjndaslkdjals", + }, + }}, false) + assert.False(t, change.ResourceRecordSet.Proxied) +} + func TestNewCloudFlareChangeProxiable(t *testing.T) { var cloudFlareTypes = []struct { recordType string diff --git a/source/ingress.go b/source/ingress.go index cbda3cd92..056b8dd6f 100644 --- a/source/ingress.go +++ b/source/ingress.go @@ -232,7 +232,6 @@ func endpointsFromIngress(ing *v1beta1.Ingress) []*endpoint.Endpoint { for _, hostname := range hostnameList { endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific)...) } - return endpoints } diff --git a/source/service.go b/source/service.go index 28cb1b9cd..1242763fe 100644 --- a/source/service.go +++ b/source/service.go @@ -218,9 +218,10 @@ func (sc *serviceSource) endpointsFromTemplate(svc *v1.Service, nodeTargets endp return nil, fmt.Errorf("failed to apply template on service %s: %v", svc.String(), err) } + providerSpecific := getProviderSpecificAnnotations(svc.Annotations) hostnameList := strings.Split(strings.Replace(buf.String(), " ", "", -1), ",") for _, hostname := range hostnameList { - endpoints = append(endpoints, sc.generateEndpoints(svc, hostname, nodeTargets)...) + endpoints = append(endpoints, sc.generateEndpoints(svc, hostname, nodeTargets, providerSpecific)...) } return endpoints, nil @@ -230,9 +231,10 @@ func (sc *serviceSource) endpointsFromTemplate(svc *v1.Service, nodeTargets endp func (sc *serviceSource) endpoints(svc *v1.Service, nodeTargets endpoint.Targets) []*endpoint.Endpoint { var endpoints []*endpoint.Endpoint + providerSpecific := getProviderSpecificAnnotations(svc.Annotations) hostnameList := getHostnamesFromAnnotations(svc.Annotations) for _, hostname := range hostnameList { - endpoints = append(endpoints, sc.generateEndpoints(svc, hostname, nodeTargets)...) + endpoints = append(endpoints, sc.generateEndpoints(svc, hostname, nodeTargets, providerSpecific)...) } return endpoints @@ -288,7 +290,7 @@ func (sc *serviceSource) setResourceLabel(service v1.Service, endpoints []*endpo } } -func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, nodeTargets endpoint.Targets) []*endpoint.Endpoint { +func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, nodeTargets endpoint.Targets, providerSpecific endpoint.ProviderSpecific) []*endpoint.Endpoint { hostname = strings.TrimSuffix(hostname, ".") ttl, err := getTTLFromAnnotations(svc.Annotations) if err != nil { @@ -296,19 +298,21 @@ func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, nod } epA := &endpoint.Endpoint{ - RecordTTL: ttl, - RecordType: endpoint.RecordTypeA, - Labels: endpoint.NewLabels(), - Targets: make(endpoint.Targets, 0, defaultTargetsCapacity), - DNSName: hostname, + RecordTTL: ttl, + RecordType: endpoint.RecordTypeA, + Labels: endpoint.NewLabels(), + Targets: make(endpoint.Targets, 0, defaultTargetsCapacity), + DNSName: hostname, + ProviderSpecific: providerSpecific, } epCNAME := &endpoint.Endpoint{ - RecordTTL: ttl, - RecordType: endpoint.RecordTypeCNAME, - Labels: endpoint.NewLabels(), - Targets: make(endpoint.Targets, 0, defaultTargetsCapacity), - DNSName: hostname, + RecordTTL: ttl, + RecordType: endpoint.RecordTypeCNAME, + Labels: endpoint.NewLabels(), + Targets: make(endpoint.Targets, 0, defaultTargetsCapacity), + DNSName: hostname, + ProviderSpecific: providerSpecific, } var endpoints []*endpoint.Endpoint diff --git a/source/source.go b/source/source.go index 338397dfc..c4e856fb9 100644 --- a/source/source.go +++ b/source/source.go @@ -41,6 +41,12 @@ const ( controllerAnnotationValue = "dns-controller" ) +// Provider-specific annotations +const ( + // The annotation used for determining if traffic will go through Cloudflare + CloudflareProxiedKey = "external-dns.alpha.kubernetes.io/cloudflare-proxied" +) + const ( ttlMinimum = 1 ttlMaximum = math.MaxUint32 @@ -72,7 +78,6 @@ func getHostnamesFromAnnotations(annotations map[string]string) []string { if !exists { return nil } - return strings.Split(strings.Replace(hostnameAnnotation, " ", "", -1), ",") } @@ -82,10 +87,22 @@ func getAliasFromAnnotations(annotations map[string]string) bool { } func getProviderSpecificAnnotations(annotations map[string]string) endpoint.ProviderSpecific { - if getAliasFromAnnotations(annotations) { - return map[string]string{"alias": "true"} + providerSpecificAnnotations := endpoint.ProviderSpecific{} + + v, exists := annotations[CloudflareProxiedKey] + if exists { + providerSpecificAnnotations = append(providerSpecificAnnotations, endpoint.ProviderSpecificProperty{ + Name: CloudflareProxiedKey, + Value: v, + }) } - return map[string]string{} + if getAliasFromAnnotations(annotations) { + providerSpecificAnnotations = append(providerSpecificAnnotations, endpoint.ProviderSpecificProperty{ + Name: "alias", + Value: "true", + }) + } + return providerSpecificAnnotations } // getTargetsFromTargetAnnotation gets endpoints from optional "target" annotation. From 398b49b3d46afe429644cb0b2d9c3439e5529ece Mon Sep 17 00:00:00 2001 From: Martin Linkhorst Date: Wed, 28 Nov 2018 15:02:05 +0100 Subject: [PATCH 005/136] feat(controller): expose managed resources and records as metrics --- controller/controller.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/controller/controller.go b/controller/controller.go index fec621bc8..769c6553a 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -40,11 +40,29 @@ var ( Help: "Number of Source errors.", }, ) + sourceEndpointsTotal = prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: "external_dns", + Subsystem: "source", + Name: "endpoints_total", + Help: "Number of Endpoints in all sources", + }, + ) + registryEndpointsTotal = prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: "external_dns", + Subsystem: "registry", + Name: "endpoints_total", + Help: "Number of Endpoints in the registry", + }, + ) ) func init() { prometheus.MustRegister(registryErrors) prometheus.MustRegister(sourceErrors) + prometheus.MustRegister(sourceEndpointsTotal) + prometheus.MustRegister(registryEndpointsTotal) } // Controller is responsible for orchestrating the different components. @@ -69,12 +87,14 @@ func (c *Controller) RunOnce() error { registryErrors.Inc() return err } + registryEndpointsTotal.Set(float64(len(records))) endpoints, err := c.Source.Endpoints() if err != nil { sourceErrors.Inc() return err } + sourceEndpointsTotal.Set(float64(len(endpoints))) plan := &plan.Plan{ Policies: []plan.Policy{c.Policy}, From 78c63c3187d6fdb95803a7eae0a272d7d4f88463 Mon Sep 17 00:00:00 2001 From: Marques Johansson Date: Fri, 30 Nov 2018 14:56:51 -0500 Subject: [PATCH 006/136] update the FAQ list of supported DNS providers (#796) --- docs/faq.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/faq.md b/docs/faq.md index d5a1666ab..927a5b9a4 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -40,6 +40,11 @@ Currently, the following providers are supported: - Dyn - OpenStack Designate - PowerDNS +- CoreDNS +- Exoscale +- Oracle Cloud Infrastructure DNS +- Linode DNS +- RFC2136 As stated in the README, we are currently looking for stable maintainers for those providers, to ensure that bugfixes and new features will be available for all of those. From f25f90db0ed66910ab60416fc8d26b9159e83d9c Mon Sep 17 00:00:00 2001 From: Davis Phillips Date: Fri, 30 Nov 2018 13:57:06 -0600 Subject: [PATCH 007/136] adding config for bind for tsig (#790) * adding config for bind for tsig * add indentation as requested --- docs/tutorials/rfc2136.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/tutorials/rfc2136.md b/docs/tutorials/rfc2136.md index 5b0bf7304..1ff0f6160 100644 --- a/docs/tutorials/rfc2136.md +++ b/docs/tutorials/rfc2136.md @@ -12,6 +12,39 @@ key "externaldns-key" { ``` - `Warning!` Bind server configuration should enable for this key AFXR zone transfer protocol. It is used for listing DNS records. +```text +# cat /etc/named.conf +... +include "/etc/rndc.key"; + +controls { + inet 123.123.123.123 port 953 allow { 10.x.y.151; } keys { "externaldns-key"; }; +}; +options { + include "/etc/named/options.conf"; +}; + +include "/etc/named/zones.conf"; +... + +# cat /etc/named/options.conf +... +dnssec-enable yes; +dnssec-validation yes; +... + +# cat /etc/named/zones.conf +... +zone "example.com" { + type master; + file "/var/named/dynamic/db.example.com"; + update-policy { + grant externaldns-key zonesub ANY; + }; +}; +... +``` + ## RFC2136 provider configuration: - Example fragment of real configuration of ExternalDNS service pod. @@ -31,4 +64,4 @@ key "externaldns-key" { - `rfc2136-tsig-keyname` - this is string parameter with secret key name it is should `MATCH!` with server key name. In example it is `externaldns-key`. - \ No newline at end of file + From 7b3c5dd8ad63a4ae19df20008fbb7fc88c1dc674 Mon Sep 17 00:00:00 2001 From: Sanyu Melwani <1154057+sanyu@users.noreply.github.com> Date: Tue, 4 Dec 2018 12:22:02 +1100 Subject: [PATCH 008/136] Use SOAP API to retrieve all records with 1 request --- Gopkg.lock | 9 ++ Gopkg.toml | 4 + provider/dyn.go | 192 ++++++++++++++++++++++++------------------- provider/dyn_test.go | 37 --------- 4 files changed, 119 insertions(+), 123 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index a692aa987..d5b1ec160 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -585,6 +585,14 @@ pruneopts = "" revision = "a6e9df898b1336106c743392c48ee0b71f5c4efa" +[[projects]] + branch = "master" + digest = "1:bb2ccb2d56cbafdec58af0f473f45304e19876f09fa671960ca87802b656a9c0" + name = "github.com/sanyu/dynectsoap" + packages = ["dynectsoap"] + pruneopts = "" + revision = "b83de5edc4e022f22903eeb3b428d2f39fb740e5" + [[projects]] digest = "1:7f569d906bdd20d906b606415b7d794f798f91a62fcfb6a4daa6d50690fb7a3f" name = "github.com/satori/go.uuid" @@ -1149,6 +1157,7 @@ "github.com/pkg/errors", "github.com/prometheus/client_golang/prometheus", "github.com/prometheus/client_golang/prometheus/promhttp", + "github.com/sanyu/dynectsoap/dynectsoap", "github.com/sirupsen/logrus", "github.com/stretchr/testify/assert", "github.com/stretchr/testify/mock", diff --git a/Gopkg.toml b/Gopkg.toml index 2887deaab..17cc1e3cc 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -115,3 +115,7 @@ ignored = ["github.com/kubernetes/repo-infra/kazel"] [[constraint]] name = "github.com/miekg/dns" version = "1.0.8" + +[[constraint]] + name = "github.com/sanyu/dynectsoap" + branch = "master" diff --git a/provider/dyn.go b/provider/dyn.go index 36eefdbfb..fd0280250 100644 --- a/provider/dyn.go +++ b/provider/dyn.go @@ -26,6 +26,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/nesv/go-dynect/dynect" + "github.com/sanyu/dynectsoap/dynectsoap" "github.com/kubernetes-incubator/external-dns/endpoint" "github.com/kubernetes-incubator/external-dns/plan" @@ -34,9 +35,6 @@ import ( const ( // 10 minutes default timeout if not configured using flags dynDefaultTTL = 600 - // can store 20000 entries globally, that's about 4MB of memory - // may be made configurable in the future but 20K records seems like enough for a few zones - cacheMaxSize = 20000 // when rate limit is hit retry up to 5 times after sleep 1m between retries dynMaxRetriesOnErrRateLimited = 5 @@ -51,50 +49,10 @@ const ( restAPIPrefix = "/REST/" ) -// A simple non-thread-safe cache with TTL. The TTL of the records is used here to -// This cache is used to save on requests to DynAPI -type cache struct { - contents map[string]*entry -} - -type entry struct { - expires int64 - ep *endpoint.Endpoint -} - -func (c *cache) Put(link string, ep *endpoint.Endpoint) { - // flush the whole cache on overflow - if len(c.contents) >= cacheMaxSize { - log.Debugf("Flushing cache") - c.contents = make(map[string]*entry) - } - - c.contents[link] = &entry{ - ep: ep, - expires: unixNow() + int64(ep.RecordTTL), - } -} - func unixNow() int64 { return int64(time.Now().Unix()) } -func (c *cache) Get(link string) *endpoint.Endpoint { - result, ok := c.contents[link] - if !ok { - return nil - } - - now := unixNow() - - if result.expires < now { - delete(c.contents, link) - return nil - } - - return result.ep -} - // DynConfig hold connection parameters to dyn.com and internal state type DynConfig struct { DomainFilter DomainFilter @@ -145,7 +103,6 @@ func (snap *ZoneSnapshot) StoreRecordsForSerial(zone string, serial int, records // DynProvider is the actual interface impl. type dynProviderState struct { DynConfig - Cache *cache LastLoginErrorTime int64 ZoneSnapshot *ZoneSnapshot @@ -186,9 +143,6 @@ type ZonePublishResponse struct { func NewDynProvider(config DynConfig) (Provider, error) { return &dynProviderState{ DynConfig: config, - Cache: &cache{ - contents: make(map[string]*entry), - }, ZoneSnapshot: &ZoneSnapshot{ endpoints: map[string][]*endpoint.Endpoint{}, serials: map[string]int{}, @@ -315,40 +269,48 @@ func apiRetryLoop(f func() error) error { return err } -// recordLinkToEndpoint makes an Endpoint given a resource link optinally making a remote call if a cached entry is expired -func (d *dynProviderState) recordLinkToEndpoint(client *dynect.Client, recordLink string) (*endpoint.Endpoint, error) { - result := d.Cache.Get(recordLink) - if result != nil { - log.Infof("Using cached endpoint for %s: %+v", recordLink, result) - return result, nil +func (d *dynProviderState) allRecordsToEndpoints(records *dynectsoap.GetAllRecordsResponseType) []*endpoint.Endpoint { + result := []*endpoint.Endpoint{} + //Convert each record to an endpoint + + //Process A Records + for _, rec := range records.Data.A_records { + ep := &endpoint.Endpoint{ + DNSName: rec.Fqdn, + RecordTTL: endpoint.TTL(rec.Ttl), + RecordType: rec.Record_type, + Targets: endpoint.Targets{rec.Rdata.Address}, + } + log.Debugf("A record: %v", *ep) + result = append(result, ep) } - rec := dynect.RecordResponse{} - - err := apiRetryLoop(func() error { - return client.Do("GET", recordLink, nil, &rec) - }) - - if err != nil { - return nil, err + //Process CNAME Records + for _, rec := range records.Data.Cname_records { + ep := &endpoint.Endpoint{ + DNSName: rec.Fqdn, + RecordTTL: endpoint.TTL(rec.Ttl), + RecordType: rec.Record_type, + Targets: endpoint.Targets{strings.TrimSuffix(rec.Rdata.Cname, ".")}, + } + log.Debugf("CNAME record: %v", *ep) + result = append(result, ep) } - // ignore all records but the types supported by external- - target := extractTarget(rec.Data.RecordType, &rec.Data.RData) - if target == "" { - return nil, nil + //Process TXT Records + for _, rec := range records.Data.Txt_records { + ep := &endpoint.Endpoint{ + DNSName: rec.Fqdn, + RecordTTL: endpoint.TTL(rec.Ttl), + RecordType: rec.Record_type, + Targets: endpoint.Targets{rec.Rdata.Txtdata}, + } + log.Debugf("TXT record: %v", *ep) + result = append(result, ep) } - result = &endpoint.Endpoint{ - DNSName: rec.Data.FQDN, - RecordTTL: endpoint.TTL(rec.Data.TTL), - RecordType: rec.Data.RecordType, - Targets: endpoint.Targets{target}, - } + return result - log.Debugf("Fetched new endpoint for %s: %+v", recordLink, result) - d.Cache.Put(recordLink, result) - return result, nil } func errorOrValue(err error, value interface{}) interface{} { @@ -387,6 +349,72 @@ func (d *dynProviderState) fetchZoneSerial(client *dynect.Client, zone string) ( return resp.Data.Serial, nil } +//Use SOAP to fetch all records with a single call +func (d *dynProviderState) fetchAllRecordsInZone(zone string) (*dynectsoap.GetAllRecordsResponseType, error) { + var err error + client := dynectsoap.NewClient("https://api2.dynect.net/SOAP/") + service := dynectsoap.NewDynect(client) + + sessionRequest := dynectsoap.SessionLoginRequestType{ + Customer_name: d.CustomerName, + User_name: d.Username, + Password: d.Password, + Fault_incompat: 0, + } + resp := dynectsoap.SessionLoginResponseType{} + err = apiRetryLoop(func() error { + return service.Do(&sessionRequest, &resp) + }) + + if err != nil { + return nil, err + } + + token := resp.Data.Token + + logoutRequest := dynectsoap.SessionLogoutRequestType{ + Token: token, + Fault_incompat: 0, + } + logoutResponse := dynectsoap.SessionLogoutResponseType{} + defer service.Do(&logoutRequest, &logoutResponse) + + req := dynectsoap.GetAllRecordsRequestType{ + Token: token, + Zone: zone, + Fault_incompat: 0, + } + records := dynectsoap.GetAllRecordsResponseType{} + err = apiRetryLoop(func() error { + return service.Do(&req, &records) + }) + + if err != nil { + return nil, err + } + log.Debugf("Got all Records, status is %s", records.Status) + + if strings.ToLower(records.Status) == "incomplete" { + jobRequest := dynectsoap.GetJobRequestType{ + Token: token, + Job_id: records.Job_id, + Fault_incompat: 0, + } + + jobResults := dynectsoap.GetJobResponseType{} + err = apiRetryLoop(func() error { + return service.GetJobRetry(&jobRequest, &jobResults) + }) + if err != nil { + return nil, err + } + return jobResults.Data.(*dynectsoap.GetAllRecordsResponseType), nil + } + + return &records, nil + +} + // fetchAllRecordLinksInZone list all records in a zone with a single call. Records not matched by the // DomainFilter are ignored. The response is a list of links that can be fed to dynect.Client.Do() // directly @@ -611,22 +639,14 @@ func (d *dynProviderState) Records() ([]*endpoint.Endpoint, error) { continue } - recordLinks, err := d.fetchAllRecordLinksInZone(client, zone) + //Fetch All Records + records, err := d.fetchAllRecordsInZone(zone) if err != nil { return nil, err } + relevantRecords = d.allRecordsToEndpoints(records) - log.Infof("Found %d relevant records found in zone %s: %+v", len(recordLinks), zone, recordLinks) - for _, link := range recordLinks { - ep, err := d.recordLinkToEndpoint(client, link) - if err != nil { - return nil, err - } - - if ep != nil { - relevantRecords = append(relevantRecords, ep) - } - } + log.Debugf("Relevant records %+v", relevantRecords) d.ZoneSnapshot.StoreRecordsForSerial(zone, serial, relevantRecords) log.Infof("Stored %d records for %s@%d", len(relevantRecords), zone, serial) diff --git a/provider/dyn_test.go b/provider/dyn_test.go index 4beda817a..e93bbd5ce 100644 --- a/provider/dyn_test.go +++ b/provider/dyn_test.go @@ -20,7 +20,6 @@ import ( "errors" "fmt" "testing" - "time" "github.com/nesv/go-dynect/dynect" "github.com/stretchr/testify/assert" @@ -264,42 +263,6 @@ func TestDyn_fixMissingTTL(t *testing.T) { assert.Equal(t, "1992", fixMissingTTL(endpoint.TTL(111), 1992)) } -func TestDyn_cachePut(t *testing.T) { - c := cache{ - contents: make(map[string]*entry), - } - - c.Put("link", &endpoint.Endpoint{ - DNSName: "name", - Targets: endpoint.Targets{"target"}, - RecordTTL: endpoint.TTL(10000), - RecordType: "A", - }) - - found := c.Get("link") - assert.NotNil(t, found) -} - -func TestDyn_cachePutExpired(t *testing.T) { - c := cache{ - contents: make(map[string]*entry), - } - - c.Put("link", &endpoint.Endpoint{ - DNSName: "name", - Targets: endpoint.Targets{"target"}, - RecordTTL: endpoint.TTL(0), - RecordType: "A", - }) - - time.Sleep(2 * time.Second) - - found := c.Get("link") - assert.Nil(t, found) - - assert.Nil(t, c.Get("no-such-records")) -} - func TestDyn_Snapshot(t *testing.T) { snap := ZoneSnapshot{ serials: map[string]int{}, From 36b443f853690737775f88fec7d41e09fe83879d Mon Sep 17 00:00:00 2001 From: Matteo Dell'Aquila Date: Tue, 4 Dec 2018 16:02:32 +0100 Subject: [PATCH 009/136] fix json syntax error - typing error (#765) there was an unexpected comma in json used as custom configuration file --- docs/tutorials/azure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/azure.md b/docs/tutorials/azure.md index 42778721a..cdd45e7f6 100644 --- a/docs/tutorials/azure.md +++ b/docs/tutorials/azure.md @@ -48,7 +48,7 @@ The preferred way to inject the configuration file is by using a Kubernetes secr "subscriptionId": "01234abc-de56-ff78-abc1-234567890def", "resourceGroup": "MyDnsResourceGroup", "aadClientId": "01234abc-de56-ff78-abc1-234567890def", - "aadClientSecret": "uKiuXeiwui4jo9quae9o", + "aadClientSecret": "uKiuXeiwui4jo9quae9o" } ``` From 159d10983338448bc5851d1caca5a4b98ec03dce Mon Sep 17 00:00:00 2001 From: xunpan Date: Fri, 7 Dec 2018 02:45:47 -0500 Subject: [PATCH 010/136] 2 issues: - coredns support more than 1 targets - delete with prefix to make sure the record is cleaned --- provider/coredns.go | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/provider/coredns.go b/provider/coredns.go index 38403ae61..4e78bdbf4 100644 --- a/provider/coredns.go +++ b/provider/coredns.go @@ -153,7 +153,7 @@ func (c etcdClient) DeleteService(key string) error { ctx, cancel := context.WithTimeout(c.ctx, etcdTimeout) defer cancel() - _, err := c.client.Delete(ctx, key) + _, err := c.client.Delete(ctx, key, etcdcv3.WithPrefix()) return err } @@ -317,22 +317,26 @@ func (p coreDNSProvider) ApplyChanges(changes *plan.Changes) error { if ep.RecordType == endpoint.RecordTypeTXT { continue } - prefix := ep.Labels[randomPrefixLabel] - if prefix == "" { - prefix = fmt.Sprintf("%08x", rand.Int31()) + + for _, target := range ep.Targets { + prefix := ep.Labels[randomPrefixLabel] + if prefix == "" { + prefix = fmt.Sprintf("%08x", rand.Int31()) + } + + service := Service{ + Host: target, + Text: ep.Labels["originalText"], + Key: etcdKeyFor(prefix + "." + dnsName), + TargetStrip: strings.Count(prefix, ".") + 1, + TTL: uint32(ep.RecordTTL), + } + services = append(services, service) } - service := Service{ - Host: ep.Targets[0], - Text: ep.Labels["originalText"], - Key: etcdKeyFor(prefix + "." + dnsName), - TargetStrip: strings.Count(prefix, ".") + 1, - TTL: uint32(ep.RecordTTL), - } - services = append(services, service) } index := 0 for _, ep := range group { - if ep.RecordType != "TXT" { + if ep.RecordType != endpoint.RecordTypeTXT { continue } if index >= len(services) { From 65e13af9b7025cea2fc131d87c0bd17595101ff0 Mon Sep 17 00:00:00 2001 From: Cesar Wong Date: Mon, 3 Dec 2018 18:16:57 -0500 Subject: [PATCH 011/136] Add zone tag filter for AWS --- main.go | 2 + pkg/apis/externaldns/types.go | 3 ++ pkg/apis/externaldns/types_test.go | 4 ++ provider/aws.go | 36 +++++++++++++++ provider/aws_test.go | 72 +++++++++++++++++++++++++++--- provider/zone_tag_filter.go | 57 +++++++++++++++++++++++ provider/zone_tag_filter_test.go | 62 +++++++++++++++++++++++++ 7 files changed, 229 insertions(+), 7 deletions(-) create mode 100644 provider/zone_tag_filter.go create mode 100644 provider/zone_tag_filter_test.go diff --git a/main.go b/main.go index b32b5fd45..56d4e5777 100644 --- a/main.go +++ b/main.go @@ -99,6 +99,7 @@ func main() { domainFilter := provider.NewDomainFilter(cfg.DomainFilter) zoneIDFilter := provider.NewZoneIDFilter(cfg.ZoneIDFilter) zoneTypeFilter := provider.NewZoneTypeFilter(cfg.AWSZoneType) + zoneTagFilter := provider.NewZoneTagFilter(cfg.AWSZoneTagFilter) var p provider.Provider switch cfg.Provider { @@ -110,6 +111,7 @@ func main() { DomainFilter: domainFilter, ZoneIDFilter: zoneIDFilter, ZoneTypeFilter: zoneTypeFilter, + ZoneTagFilter: zoneTagFilter, BatchChangeSize: cfg.AWSBatchChangeSize, BatchChangeInterval: cfg.AWSBatchChangeInterval, EvaluateTargetHealth: cfg.AWSEvaluateTargetHealth, diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 3e67a8bf9..5e50ea374 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -56,6 +56,7 @@ type Config struct { AlibabaCloudConfigFile string AlibabaCloudZoneType string AWSZoneType string + AWSZoneTagFilter []string AWSAssumeRole string AWSBatchChangeSize int AWSBatchChangeInterval time.Duration @@ -127,6 +128,7 @@ var defaultConfig = &Config{ DomainFilter: []string{}, AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json", AWSZoneType: "", + AWSZoneTagFilter: []string{}, AWSAssumeRole: "", AWSBatchChangeSize: 4000, AWSBatchChangeInterval: time.Second, @@ -241,6 +243,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("alibaba-cloud-config-file", "When using the Alibaba Cloud provider, specify the Alibaba Cloud configuration file (required when --provider=alibabacloud").Default(defaultConfig.AlibabaCloudConfigFile).StringVar(&cfg.AlibabaCloudConfigFile) app.Flag("alibaba-cloud-zone-type", "When using the Alibaba Cloud provider, filter for zones of this type (optional, options: public, private)").Default(defaultConfig.AlibabaCloudZoneType).EnumVar(&cfg.AlibabaCloudZoneType, "", "public", "private") app.Flag("aws-zone-type", "When using the AWS provider, filter for zones of this type (optional, options: public, private)").Default(defaultConfig.AWSZoneType).EnumVar(&cfg.AWSZoneType, "", "public", "private") + app.Flag("aws-zone-tags", "When using the AWS provider, filter for zones with these tags").Default("").StringsVar(&cfg.AWSZoneTagFilter) app.Flag("aws-assume-role", "When using the AWS provider, assume this IAM role. Useful for hosted zones in another AWS account. Specify the full ARN, e.g. `arn:aws:iam::123455567:role/external-dns` (optional)").Default(defaultConfig.AWSAssumeRole).StringVar(&cfg.AWSAssumeRole) app.Flag("aws-batch-change-size", "When using the AWS provider, set the maximum number of changes that will be applied in each batch.").Default(strconv.Itoa(defaultConfig.AWSBatchChangeSize)).IntVar(&cfg.AWSBatchChangeSize) app.Flag("aws-batch-change-interval", "When using the AWS provider, set the interval between batch changes.").Default(defaultConfig.AWSBatchChangeInterval.String()).DurationVar(&cfg.AWSBatchChangeInterval) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index 87740640f..bdee0a22d 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -43,6 +43,7 @@ var ( ZoneIDFilter: []string{""}, AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json", AWSZoneType: "", + AWSZoneTagFilter: []string{""}, AWSAssumeRole: "", AWSBatchChangeSize: 4000, AWSBatchChangeInterval: time.Second, @@ -94,6 +95,7 @@ var ( ZoneIDFilter: []string{"/hostedzone/ZTST1", "/hostedzone/ZTST2"}, AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json", AWSZoneType: "private", + AWSZoneTagFilter: []string{"tag=foo"}, AWSAssumeRole: "some-other-role", AWSBatchChangeSize: 100, AWSBatchChangeInterval: time.Second * 2, @@ -189,6 +191,7 @@ func TestParseFlags(t *testing.T) { "--zone-id-filter=/hostedzone/ZTST1", "--zone-id-filter=/hostedzone/ZTST2", "--aws-zone-type=private", + "--aws-zone-tags=tag=foo", "--aws-assume-role=some-other-role", "--aws-batch-change-size=100", "--aws-batch-change-interval=2s", @@ -248,6 +251,7 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_TLS_CLIENT_CERT_KEY": "/path/to/key.pem", "EXTERNAL_DNS_ZONE_ID_FILTER": "/hostedzone/ZTST1\n/hostedzone/ZTST2", "EXTERNAL_DNS_AWS_ZONE_TYPE": "private", + "EXTERNAL_DNS_AWS_ZONE_TAGS": "tag=foo", "EXTERNAL_DNS_AWS_ASSUME_ROLE": "some-other-role", "EXTERNAL_DNS_AWS_BATCH_CHANGE_SIZE": "100", "EXTERNAL_DNS_AWS_BATCH_CHANGE_INTERVAL": "2s", diff --git a/provider/aws.go b/provider/aws.go index 7ec5e79aa..ec14c8d35 100644 --- a/provider/aws.go +++ b/provider/aws.go @@ -85,6 +85,7 @@ type Route53API interface { ChangeResourceRecordSets(*route53.ChangeResourceRecordSetsInput) (*route53.ChangeResourceRecordSetsOutput, error) CreateHostedZone(*route53.CreateHostedZoneInput) (*route53.CreateHostedZoneOutput, error) ListHostedZonesPages(input *route53.ListHostedZonesInput, fn func(resp *route53.ListHostedZonesOutput, lastPage bool) (shouldContinue bool)) error + ListTagsForResource(input *route53.ListTagsForResourceInput) (*route53.ListTagsForResourceOutput, error) } // AWSProvider is an implementation of Provider for AWS Route53. @@ -100,6 +101,8 @@ type AWSProvider struct { zoneIDFilter ZoneIDFilter // filter hosted zones by type (e.g. private or public) zoneTypeFilter ZoneTypeFilter + // filter hosted zones by tags + zoneTagFilter ZoneTagFilter } // AWSConfig contains configuration to create a new AWS provider. @@ -107,6 +110,7 @@ type AWSConfig struct { DomainFilter DomainFilter ZoneIDFilter ZoneIDFilter ZoneTypeFilter ZoneTypeFilter + ZoneTagFilter ZoneTagFilter BatchChangeSize int BatchChangeInterval time.Duration EvaluateTargetHealth bool @@ -145,6 +149,7 @@ func NewAWSProvider(awsConfig AWSConfig) (*AWSProvider, error) { domainFilter: awsConfig.DomainFilter, zoneIDFilter: awsConfig.ZoneIDFilter, zoneTypeFilter: awsConfig.ZoneTypeFilter, + zoneTagFilter: awsConfig.ZoneTagFilter, batchChangeSize: awsConfig.BatchChangeSize, batchChangeInterval: awsConfig.BatchChangeInterval, evaluateTargetHealth: awsConfig.EvaluateTargetHealth, @@ -158,6 +163,7 @@ func NewAWSProvider(awsConfig AWSConfig) (*AWSProvider, error) { func (p *AWSProvider) Zones() (map[string]*route53.HostedZone, error) { zones := make(map[string]*route53.HostedZone) + var tagErr error f := func(resp *route53.ListHostedZonesOutput, lastPage bool) (shouldContinue bool) { for _, zone := range resp.HostedZones { if !p.zoneIDFilter.Match(aws.StringValue(zone.Id)) { @@ -172,6 +178,18 @@ func (p *AWSProvider) Zones() (map[string]*route53.HostedZone, error) { continue } + // Only fetch tags if a tag filter was specified + if !p.zoneTagFilter.IsEmpty() { + tags, err := p.tagsForZone(*zone.Id) + if err != nil { + tagErr = err + return false + } + if !p.zoneTagFilter.Match(tags) { + continue + } + } + zones[aws.StringValue(zone.Id)] = zone } @@ -182,6 +200,9 @@ func (p *AWSProvider) Zones() (map[string]*route53.HostedZone, error) { if err != nil { return nil, err } + if tagErr != nil { + return nil, tagErr + } for _, zone := range zones { log.Debugf("Considering zone: %s (domain: %s)", aws.StringValue(zone.Id), aws.StringValue(zone.Name)) @@ -412,6 +433,21 @@ func (p *AWSProvider) newChange(action string, endpoint *endpoint.Endpoint) *rou return change } +func (p *AWSProvider) tagsForZone(zoneID string) (map[string]string, error) { + response, err := p.client.ListTagsForResource(&route53.ListTagsForResourceInput{ + ResourceType: aws.String("hostedzone"), + ResourceId: aws.String(zoneID), + }) + if err != nil { + return nil, err + } + tagMap := map[string]string{} + for _, tag := range response.ResourceTagSet.Tags { + tagMap[*tag.Key] = *tag.Value + } + return tagMap, nil +} + func batchChangeSet(cs []*route53.Change, batchSize int) [][]*route53.Change { if len(cs) <= batchSize { return [][]*route53.Change{cs} diff --git a/provider/aws_test.go b/provider/aws_test.go index 6852b6df2..8ffbc51ba 100644 --- a/provider/aws_test.go +++ b/provider/aws_test.go @@ -50,6 +50,7 @@ var _ Route53API = &Route53APIStub{} type Route53APIStub struct { zones map[string]*route53.HostedZone recordSets map[string]map[string][]*route53.ResourceRecordSet + zoneTags map[string][]*route53.Tag m dynamicMock } @@ -66,6 +67,7 @@ func NewRoute53APIStub() *Route53APIStub { return &Route53APIStub{ zones: make(map[string]*route53.HostedZone), recordSets: make(map[string]map[string][]*route53.ResourceRecordSet), + zoneTags: make(map[string][]*route53.Tag), } } @@ -95,6 +97,20 @@ func wildcardEscape(s string) string { return s } +func (r *Route53APIStub) ListTagsForResource(input *route53.ListTagsForResourceInput) (*route53.ListTagsForResourceOutput, error) { + if aws.StringValue(input.ResourceType) == "hostedzone" { + tags := r.zoneTags[aws.StringValue(input.ResourceId)] + return &route53.ListTagsForResourceOutput{ + ResourceTagSet: &route53.ResourceTagSet{ + ResourceId: input.ResourceId, + ResourceType: input.ResourceType, + Tags: tags, + }, + }, nil + } + return &route53.ListTagsForResourceOutput{}, nil +} + func (r *Route53APIStub) ChangeResourceRecordSets(input *route53.ChangeResourceRecordSetsInput) (*route53.ChangeResourceRecordSetsOutput, error) { if r.m.isMocked("ChangeResourceRecordSets", input) { return r.m.ChangeResourceRecordSets(input) @@ -231,15 +247,17 @@ func TestAWSZones(t *testing.T) { msg string zoneIDFilter ZoneIDFilter zoneTypeFilter ZoneTypeFilter + zoneTagFilter ZoneTagFilter expectedZones map[string]*route53.HostedZone }{ - {"no filter", NewZoneIDFilter([]string{}), NewZoneTypeFilter(""), allZones}, - {"public filter", NewZoneIDFilter([]string{}), NewZoneTypeFilter("public"), publicZones}, - {"private filter", NewZoneIDFilter([]string{}), NewZoneTypeFilter("private"), privateZones}, - {"unknown filter", NewZoneIDFilter([]string{}), NewZoneTypeFilter("unknown"), noZones}, - {"zone id filter", NewZoneIDFilter([]string{"/hostedzone/zone-3.ext-dns-test-2.teapot.zalan.do."}), NewZoneTypeFilter(""), privateZones}, + {"no filter", NewZoneIDFilter([]string{}), NewZoneTypeFilter(""), NewZoneTagFilter([]string{}), allZones}, + {"public filter", NewZoneIDFilter([]string{}), NewZoneTypeFilter("public"), NewZoneTagFilter([]string{}), publicZones}, + {"private filter", NewZoneIDFilter([]string{}), NewZoneTypeFilter("private"), NewZoneTagFilter([]string{}), privateZones}, + {"unknown filter", NewZoneIDFilter([]string{}), NewZoneTypeFilter("unknown"), NewZoneTagFilter([]string{}), noZones}, + {"zone id filter", NewZoneIDFilter([]string{"/hostedzone/zone-3.ext-dns-test-2.teapot.zalan.do."}), NewZoneTypeFilter(""), NewZoneTagFilter([]string{}), privateZones}, + {"tag filter", NewZoneIDFilter([]string{}), NewZoneTypeFilter(""), NewZoneTagFilter([]string{"zone=3"}), privateZones}, } { - provider, _ := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), ti.zoneIDFilter, ti.zoneTypeFilter, defaultEvaluateTargetHealth, false, []*endpoint.Endpoint{}) + provider, _ := newAWSProviderWithTagFilter(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), ti.zoneIDFilter, ti.zoneTypeFilter, ti.zoneTagFilter, defaultEvaluateTargetHealth, false, []*endpoint.Endpoint{}) zones, err := provider.Zones() require.NoError(t, err) @@ -1027,8 +1045,11 @@ func escapeAWSRecords(t *testing.T, provider *AWSProvider, zone string) { require.NoError(t, err) } } - func newAWSProvider(t *testing.T, domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, zoneTypeFilter ZoneTypeFilter, evaluateTargetHealth, dryRun bool, records []*endpoint.Endpoint) (*AWSProvider, *Route53APIStub) { + return newAWSProviderWithTagFilter(t, domainFilter, zoneIDFilter, zoneTypeFilter, NewZoneTagFilter([]string{}), evaluateTargetHealth, dryRun, records) +} + +func newAWSProviderWithTagFilter(t *testing.T, domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, zoneTypeFilter ZoneTypeFilter, zoneTagFilter ZoneTagFilter, evaluateTargetHealth, dryRun bool, records []*endpoint.Endpoint) (*AWSProvider, *Route53APIStub) { client := NewRoute53APIStub() provider := &AWSProvider{ @@ -1039,6 +1060,7 @@ func newAWSProvider(t *testing.T, domainFilter DomainFilter, zoneIDFilter ZoneID domainFilter: domainFilter, zoneIDFilter: zoneIDFilter, zoneTypeFilter: zoneTypeFilter, + zoneTagFilter: zoneTagFilter, dryRun: false, } @@ -1067,6 +1089,8 @@ func newAWSProvider(t *testing.T, domainFilter DomainFilter, zoneIDFilter ZoneID Config: &route53.HostedZoneConfig{PrivateZone: aws.Bool(false)}, }) + setupZoneTags(provider.client.(*Route53APIStub)) + setupAWSRecords(t, provider, records) provider.dryRun = dryRun @@ -1074,6 +1098,40 @@ func newAWSProvider(t *testing.T, domainFilter DomainFilter, zoneIDFilter ZoneID return provider, client } +func setupZoneTags(client *Route53APIStub) { + addZoneTags(client.zoneTags, "/hostedzone/zone-1.ext-dns-test-2.teapot.zalan.do.", map[string]string{ + "zone-1-tag-1": "tag-1-value", + "domain": "test-2", + "zone": "1", + }) + addZoneTags(client.zoneTags, "/hostedzone/zone-2.ext-dns-test-2.teapot.zalan.do.", map[string]string{ + "zone-2-tag-1": "tag-1-value", + "domain": "test-2", + "zone": "2", + }) + addZoneTags(client.zoneTags, "/hostedzone/zone-3.ext-dns-test-2.teapot.zalan.do.", map[string]string{ + "zone-3-tag-1": "tag-1-value", + "domain": "test-2", + "zone": "3", + }) + addZoneTags(client.zoneTags, "/hostedzone/zone-4.ext-dns-test-2.teapot.zalan.do.", map[string]string{ + "zone-4-tag-1": "tag-1-value", + "domain": "test-3", + "zone": "4", + }) +} + +func addZoneTags(tagMap map[string][]*route53.Tag, zoneID string, tags map[string]string) { + tagList := make([]*route53.Tag, 0, len(tags)) + for k, v := range tags { + tagList = append(tagList, &route53.Tag{ + Key: aws.String(k), + Value: aws.String(v), + }) + } + tagMap[zoneID] = tagList +} + func validateRecords(t *testing.T, records []*route53.ResourceRecordSet, expected []*route53.ResourceRecordSet) { assert.Equal(t, expected, records) } diff --git a/provider/zone_tag_filter.go b/provider/zone_tag_filter.go new file mode 100644 index 000000000..c40ab06e9 --- /dev/null +++ b/provider/zone_tag_filter.go @@ -0,0 +1,57 @@ +/* +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 provider + +import ( + "strings" +) + +// ZoneTagFilter holds a list of zone tags to filter by +type ZoneTagFilter struct { + zoneTags []string +} + +// NewZoneTagFilter returns a new ZoneTagFilter given a list of zone tags +func NewZoneTagFilter(tags []string) ZoneTagFilter { + if len(tags) == 1 && len(tags[0]) == 0 { + tags = []string{} + } + return ZoneTagFilter{zoneTags: tags} +} + +// Match checks whether a zone's set of tags matches the provided tag values +func (f ZoneTagFilter) Match(tagsMap map[string]string) bool { + for _, tagFilter := range f.zoneTags { + filterParts := strings.SplitN(tagFilter, "=", 2) + switch len(filterParts) { + case 1: + if _, hasTag := tagsMap[filterParts[0]]; !hasTag { + return false + } + case 2: + if value, hasTag := tagsMap[filterParts[0]]; !hasTag || value != filterParts[1] { + return false + } + } + } + return true +} + +// IsEmpty returns true if there are no tags for the filter +func (f ZoneTagFilter) IsEmpty() bool { + return len(f.zoneTags) == 0 +} diff --git a/provider/zone_tag_filter_test.go b/provider/zone_tag_filter_test.go new file mode 100644 index 000000000..9574e68eb --- /dev/null +++ b/provider/zone_tag_filter_test.go @@ -0,0 +1,62 @@ +/* +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 provider + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestZoneTagFilterMatch(t *testing.T) { + for _, tc := range []struct { + name string + zoneTagFilter []string + zoneTags map[string]string + matches bool + }{ + { + "single tag no match", []string{"tag1=value1"}, map[string]string{"tag0": "value0"}, false, + }, + { + "single tag matches", []string{"tag1=value1"}, map[string]string{"tag1": "value1"}, true, + }, + { + "multiple tags no value match", []string{"tag1=value1"}, map[string]string{"tag0": "value0", "tag1": "value2"}, false, + }, + { + "multiple tags matches", []string{"tag1=value1"}, map[string]string{"tag0": "value0", "tag1": "value1"}, true, + }, + { + "tag name no match", []string{"tag1"}, map[string]string{"tag0": "value0"}, false, + }, + { + "tag name matches", []string{"tag1"}, map[string]string{"tag1": "value1"}, true, + }, + { + "multiple filter no match", []string{"tag1=value1", "tag2=value2"}, map[string]string{"tag1": "value1"}, false, + }, + { + "multiple filter matches", []string{"tag1=value1", "tag2=value2"}, map[string]string{"tag2": "value2", "tag1": "value1", "tag3": "value3"}, true, + }, + } { + zoneTagFilter := NewZoneTagFilter(tc.zoneTagFilter) + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.matches, zoneTagFilter.Match(tc.zoneTags)) + }) + } +} From 2a6d9ae8981f7a232c38f65d9c0b31abc20013d4 Mon Sep 17 00:00:00 2001 From: Sanyu Melwani <1154057+sanyu@users.noreply.github.com> Date: Mon, 10 Dec 2018 17:26:18 +1100 Subject: [PATCH 012/136] Removed extractTarget --- provider/dyn.go | 21 --------------------- provider/dyn_test.go | 16 ---------------- 2 files changed, 37 deletions(-) diff --git a/provider/dyn.go b/provider/dyn.go index fd0280250..e34999803 100644 --- a/provider/dyn.go +++ b/provider/dyn.go @@ -231,27 +231,6 @@ func merge(updateOld, updateNew []*endpoint.Endpoint) []*endpoint.Endpoint { return result } -// extractTarget populates the correct field given a record type. -// See dynect.DataBlock comments for details. Empty response means nothing -// was populated - basically an error -func extractTarget(recType string, data *dynect.DataBlock) string { - result := "" - if recType == endpoint.RecordTypeA { - result = data.Address - } - - if recType == endpoint.RecordTypeCNAME { - result = data.CName - result = strings.TrimSuffix(result, ".") - } - - if recType == endpoint.RecordTypeTXT { - result = data.TxtData - } - - return result -} - func apiRetryLoop(f func() error) error { var err error for i := 0; i < dynMaxRetriesOnErrRateLimited; i++ { diff --git a/provider/dyn_test.go b/provider/dyn_test.go index e93bbd5ce..f2c2fa6d6 100644 --- a/provider/dyn_test.go +++ b/provider/dyn_test.go @@ -168,22 +168,6 @@ func TestDynMerge_NoUpdateIfTTLUnchanged(t *testing.T) { assert.Equal(t, 0, len(merged)) } -func TestDyn_extractTarget(t *testing.T) { - tests := []struct { - recordType string - block *dynect.DataBlock - target string - }{ - {"A", &dynect.DataBlock{Address: "address"}, "address"}, - {"CNAME", &dynect.DataBlock{CName: "name."}, "name"}, // note trailing dot is trimmed for CNAMEs - {"TXT", &dynect.DataBlock{TxtData: "text."}, "text."}, - } - - for _, tc := range tests { - assert.Equal(t, tc.target, extractTarget(tc.recordType, tc.block)) - } -} - func TestDyn_endpointToRecord(t *testing.T) { tests := []struct { ep *endpoint.Endpoint From 7747db2351728a4c4fed6519f6f87071f8a61cd1 Mon Sep 17 00:00:00 2001 From: THEBAULT Julien Date: Wed, 5 Dec 2018 20:19:52 +0100 Subject: [PATCH 013/136] Update coredns tutorial with RBAC manifest (see #791) --- docs/tutorials/coredns.md | 79 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 4 deletions(-) diff --git a/docs/tutorials/coredns.md b/docs/tutorials/coredns.md index 12a357dc0..a8554d456 100644 --- a/docs/tutorials/coredns.md +++ b/docs/tutorials/coredns.md @@ -86,8 +86,10 @@ helm install --name my-coredns --values values.yaml stable/coredns ## Installing ExternalDNS ### Install external ExternalDNS ETCD_URLS is configured to etcd client service address. -``` -$ cat external-dns.yaml + +#### Manifest (for clusters without RBAC enabled) + +```yaml apiVersion: apps/v1 kind: Deployment metadata: @@ -97,7 +99,7 @@ spec: strategy: type: Recreate selector: - matchLabels: + matchLabels: app: external-dns template: metadata: @@ -114,7 +116,76 @@ spec: env: - name: ETCD_URLS value: http://10.105.68.165:2379 -$ kubectl apply -f external-dns.yaml +``` + +#### Manifest (for clusters with RBAC enabled) + +```yaml +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: external-dns +rules: +- apiGroups: [""] + resources: ["services"] + verbs: ["get","watch","list"] +- apiGroups: [""] + resources: ["pods"] + verbs: ["get","watch","list"] +- apiGroups: ["extensions"] + resources: ["ingresses"] + verbs: ["get","watch","list"] +- apiGroups: [""] + resources: ["nodes"] + verbs: ["list"] +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: external-dns-viewer +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: external-dns +subjects: +- kind: ServiceAccount + name: external-dns + namespace: kube-system +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: external-dns + namespace: kube-system +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: external-dns + namespace: kube-system +spec: + strategy: + type: Recreate + selector: + matchLabels: + app: external-dns + template: + metadata: + labels: + app: external-dns + spec: + serviceAccountName: external-dns + containers: + - name: external-dns + image: registry.opensource.zalan.do/teapot/external-dns:latest + args: + - --source=ingress + - --provider=coredns + - --log-level=debug # debug only + env: + - name: ETCD_URLS + value: http://10.105.68.165:2379 ``` ## Enable the ingress controller From fc245c088d759caa22bc3c6014eced928d436b3c Mon Sep 17 00:00:00 2001 From: xunpan Date: Fri, 7 Dec 2018 21:52:06 -0500 Subject: [PATCH 014/136] avoid unnecessary updating for CRD resource with test updated --- source/crd.go | 5 +++++ source/crd_test.go | 46 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/source/crd.go b/source/crd.go index 79b308ad4..4c5a2a698 100644 --- a/source/crd.go +++ b/source/crd.go @@ -118,6 +118,11 @@ func (cs *crdSource) Endpoints() ([]*endpoint.Endpoint, error) { for _, dnsEndpoint := range result.Items { endpoints = append(endpoints, dnsEndpoint.Spec.Endpoints...) + + if dnsEndpoint.Status.ObservedGeneration == dnsEndpoint.Generation { + continue + } + dnsEndpoint.Status.ObservedGeneration = dnsEndpoint.Generation // Update the ObservedGeneration _, err = cs.UpdateStatus(&dnsEndpoint) diff --git a/source/crd_test.go b/source/crd_test.go index a069f2114..928a35685 100644 --- a/source/crd_test.go +++ b/source/crd_test.go @@ -18,6 +18,7 @@ package source import ( "bytes" + "encoding/json" "fmt" "io" "io/ioutil" @@ -54,20 +55,21 @@ func objBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser { return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj)))) } -func startCRDServerToServeTargets(endpoints []*endpoint.Endpoint, apiVersion, kind, namespace, name string) rest.Interface { +func startCRDServerToServeTargets(endpoints []*endpoint.Endpoint, apiVersion, kind, namespace, name string, t *testing.T) rest.Interface { groupVersion, _ := schema.ParseGroupVersion(apiVersion) scheme := runtime.NewScheme() addKnownTypes(scheme, groupVersion) dnsEndpointList := endpoint.DNSEndpointList{} - dnsEndpoint := endpoint.DNSEndpoint{ + dnsEndpoint := &endpoint.DNSEndpoint{ TypeMeta: metav1.TypeMeta{ APIVersion: apiVersion, Kind: kind, }, ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, + Name: name, + Namespace: namespace, + Generation: 1, }, Spec: endpoint.DNSEndpointSpec{ Endpoints: endpoints, @@ -88,10 +90,18 @@ func startCRDServerToServeTargets(endpoints []*endpoint.Endpoint, apiVersion, ki case p == "/apis/"+apiVersion+"/"+strings.ToLower(kind)+"s" && m == http.MethodGet: fallthrough case p == "/apis/"+apiVersion+"/namespaces/"+namespace+"/"+strings.ToLower(kind)+"s" && m == http.MethodGet: - dnsEndpointList.Items = append(dnsEndpointList.Items, dnsEndpoint) + dnsEndpointList.Items = dnsEndpointList.Items[:0] + dnsEndpointList.Items = append(dnsEndpointList.Items, *dnsEndpoint) return &http.Response{StatusCode: http.StatusOK, Header: defaultHeader(), Body: objBody(codec, &dnsEndpointList)}, nil case strings.HasPrefix(p, "/apis/"+apiVersion+"/namespaces/") && strings.HasSuffix(p, strings.ToLower(kind)+"s") && m == http.MethodGet: return &http.Response{StatusCode: http.StatusOK, Header: defaultHeader(), Body: objBody(codec, &dnsEndpointList)}, nil + case p == "/apis/"+apiVersion+"/namespaces/"+namespace+"/"+strings.ToLower(kind)+"s/"+name+"/status" && m == http.MethodPut: + decoder := json.NewDecoder(req.Body) + + var body endpoint.DNSEndpoint + decoder.Decode(&body) + dnsEndpoint.Status.ObservedGeneration = body.Status.ObservedGeneration + return &http.Response{StatusCode: http.StatusOK, Header: defaultHeader(), Body: objBody(codec, dnsEndpoint)}, nil default: return nil, fmt.Errorf("unexpected request: %#v\n%#v", req.URL, req) } @@ -200,6 +210,8 @@ func testCRDSourceEndpoints(t *testing.T) { apiVersion: "test.k8s.io/v1alpha1", registeredKind: "DNSEndpoint", kind: "DNSEndpoint", + namespace: "foo", + registeredNamespace: "foo", endpoints: []*endpoint.Endpoint{ {DNSName: "abc.example.org", Targets: endpoint.Targets{"1.2.3.4"}, @@ -216,6 +228,8 @@ func testCRDSourceEndpoints(t *testing.T) { apiVersion: "test.k8s.io/v1alpha1", registeredKind: "DNSEndpoint", kind: "DNSEndpoint", + namespace: "foo", + registeredNamespace: "foo", endpoints: []*endpoint.Endpoint{ {DNSName: "abc.example.org", Targets: endpoint.Targets{"1.2.3.4"}, @@ -233,7 +247,7 @@ func testCRDSourceEndpoints(t *testing.T) { }, } { t.Run(ti.title, func(t *testing.T) { - restClient := startCRDServerToServeTargets(ti.endpoints, ti.registeredAPIVersion, ti.registeredKind, ti.registeredNamespace, "") + restClient := startCRDServerToServeTargets(ti.endpoints, ti.registeredAPIVersion, ti.registeredKind, ti.registeredNamespace, "test", t) groupVersion, err := schema.ParseGroupVersion(ti.apiVersion) require.NoError(t, err) @@ -253,8 +267,28 @@ func testCRDSourceEndpoints(t *testing.T) { return } + if err == nil { + validateCRDResource(t, cs, ti.expectError) + } + // Validate received endpoints against expected endpoints. validateEndpoints(t, receivedEndpoints, ti.endpoints) }) } } + +func validateCRDResource(t *testing.T, src Source, expectError bool) { + cs := src.(*crdSource) + result, err := cs.List(&metav1.ListOptions{}) + if expectError { + require.Errorf(t, err, "Received err %v", err) + } else { + require.NoErrorf(t, err, "Received err %v", err) + } + + for _, dnsEndpoint := range result.Items { + if dnsEndpoint.Status.ObservedGeneration != dnsEndpoint.Generation { + require.Errorf(t, err, "Unexpected CRD resource result: ObservedGenerations <%v> is not equal to Generation<%v>", dnsEndpoint.Status.ObservedGeneration, dnsEndpoint.Generation) + } + } +} From 374bb9235a968b4f2def87178a0facfde5cc09de Mon Sep 17 00:00:00 2001 From: Adrian Rangel Date: Wed, 19 Dec 2018 02:08:20 -0600 Subject: [PATCH 015/136] fix commands to cleanup --- docs/tutorials/cloudflare.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorials/cloudflare.md b/docs/tutorials/cloudflare.md index 308cb8186..72c39260a 100644 --- a/docs/tutorials/cloudflare.md +++ b/docs/tutorials/cloudflare.md @@ -193,6 +193,6 @@ This should show the external IP address of the service as the A record for your Now that we have verified that ExternalDNS will automatically manage Cloudflare DNS records, we can delete the tutorial's example: ``` -$ kubectl delete service -f nginx.yaml -$ kubectl delete service -f externaldns.yaml +$ kubectl delete -f nginx.yaml +$ kubectl delete -f externaldns.yaml ``` From cea58909f0c118f19754c9bd059b932bc19c5f75 Mon Sep 17 00:00:00 2001 From: Wade Lee Date: Thu, 20 Dec 2018 16:31:55 +0800 Subject: [PATCH 016/136] Update coredns.md Make the DNS service IP consistent with `my-coredns-coredns` in example --- docs/tutorials/coredns.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorials/coredns.md b/docs/tutorials/coredns.md index a8554d456..e20699c89 100644 --- a/docs/tutorials/coredns.md +++ b/docs/tutorials/coredns.md @@ -228,8 +228,8 @@ nginx nginx.example.org 10.0.2.15 80 2m $ kubectl run -it --rm --restart=Never --image=infoblox/dnstools:latest dnstools If you don't see a command prompt, try pressing enter. -dnstools# dig @10.102.213.122 nginx.example.org +short -dnstools# dig @10.102.213.122 nginx.example.org +short +dnstools# dig @10.100.4.143 nginx.example.org +short +dnstools# dig @10.100.4.143 nginx.example.org +short 10.0.2.15 dnstools# ``` From a19cc0a3d0b74a2d0975e3ae886a56eb25723f9e Mon Sep 17 00:00:00 2001 From: Zach Yam Date: Thu, 20 Dec 2018 23:30:52 +0100 Subject: [PATCH 017/136] Add metrics info to FAQ --- docs/faq.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/faq.md b/docs/faq.md index d5a1666ab..99a622ea7 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -28,7 +28,7 @@ ExternalDNS can solve this for you as well. ### Which DNS providers are supported? -Currently, the following providers are supported: +Currently, the following providers are supported: - Google CloudDNS - AWS Route 53 @@ -155,6 +155,18 @@ CNAMEs cannot co-exist with other records, therefore you can use the `--txt-pref You need to add either https://www.googleapis.com/auth/ndev.clouddns.readwrite or https://www.googleapis.com/auth/cloud-platform on your instance group's scope. +### What metrics can I get from ExternalDNS and what do they mean? + +ExternalDNS exposes 2 types of metrics: Sources and Registry errors. + +`Source`s are mostly Kubernetes API objects. Examples of `source` errors may be connection errors to the Kubernetes API server itself or missing RBAC permissions. It can also stem from incompatible configuration in the objects itself like invalid characters, processing a broken fqdnTemplate etc. + +`Registry` errors are mostly Provider errors, unless there's some coding flaw in the registry package. Provider errors often arise due to accessing their APIs due to network or missing cloud-provider permissions when reading records. When applying a changeset, errors arise when the changeset applied is incompatible with the current state, in which case ExternalDNS can get stuck forever. + +In case of an increased error count, you could correlate them with the `http_request_duration_seconds{handler="instrumented_http"}` metric which should show increased numbers for status codes 4xx (permissions, configuration, invalid changeset) or 5xx (apiserver down). + +You can use the host label in the metric to figure out if the request was against the Kubernetes API server (Source errors) or the DNS provider API (Registry/Provider errors). + ### How can I run ExternalDNS under a specific GCP Service Account, e.g. to access DNS records in other projects? Have a look at https://github.com/linki/mate/blob/v0.6.2/examples/google/README.md#permissions From 5aee5ad345beba79ab138954281b288daf701ea3 Mon Sep 17 00:00:00 2001 From: Pascal Kutscha Date: Sat, 22 Dec 2018 21:27:48 +0100 Subject: [PATCH 018/136] Update cloudflare.md --- docs/tutorials/cloudflare.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/cloudflare.md b/docs/tutorials/cloudflare.md index 308cb8186..c84e4f432 100644 --- a/docs/tutorials/cloudflare.md +++ b/docs/tutorials/cloudflare.md @@ -16,7 +16,7 @@ Snippet from [Cloudflare - Getting Started](https://api.cloudflare.com/#getting- >Cloudflare's API exposes the entire Cloudflare infrastructure via a standardized programmatic interface. Using Cloudflare's API, you can do just about anything you can do on cloudflare.com via the customer dashboard. ->The Cloudflare API is a RESTful API based on HTTPS requests and JSON responses. If you are registered with Cloudflare, you can obtain your API key from the bottom of the "My Account" page, found here: [Go to My account](https://www.cloudflare.com/a/account). +>The Cloudflare API is a RESTful API based on HTTPS requests and JSON responses. If you are registered with Cloudflare, you can obtain your API key from the bottom of the "My Account" page, found here: [Go to My account](https://dash.cloudflare.com/profile). The environment vars `CF_API_KEY` and `CF_API_EMAIL` will be needed to run ExternalDNS with Cloudflare. From d0de07c084d5021ee15adbc88116fa8776e10cad Mon Sep 17 00:00:00 2001 From: Denis Biondic Date: Mon, 24 Dec 2018 16:44:06 +0100 Subject: [PATCH 019/136] docs(azure): better security granuality concerning external dns service principal --- docs/tutorials/azure.md | 59 +++++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/docs/tutorials/azure.md b/docs/tutorials/azure.md index cdd45e7f6..51f6d77fc 100644 --- a/docs/tutorials/azure.md +++ b/docs/tutorials/azure.md @@ -61,13 +61,18 @@ The `resourceGroup` is the Resource Group created in a previous step. The `aadClientID` and `aaClientSecret` are assoiated with the Service Principal, that you need to create next. ### Creating service principal -A Service Principal with a minimum access level of contribute to the resource group containing the Azure DNS zone(s) is necessary for ExternalDNS to be able to edit DNS records. This is an Azure CLI example on how to query the Azure API for the information required for the Resource Group and DNS zone you would have already created in previous steps. +A Service Principal with a minimum access level of `contributor` to the DNS zone(s) and `reader` to the resource group containing the Azure DNS zone(s) is necessary for ExternalDNS to be able to edit DNS records. However, other more permissive access levels will work too (e.g. `contributor` to the resource group or the whole subscription). +This is an Azure CLI example on how to query the Azure API for the information required for the Resource Group and DNS zone you would have already created in previous steps. + +``` bash +> az login ``` ->az login -... -# find the relevant subscription and set the az context. id = subscriptionId value in the azure.json. ->az account list + +Find the relevant subscription and make sure it is selected (the same subscriptionId should be set into azure.json) + +``` bash +> az account list { "cloudName": "AzureCloud", "id": "", @@ -79,16 +84,15 @@ A Service Principal with a minimum access level of contribute to the resource gr "name": "name", "type": "user" } ->az account set -s id -... ->az group show --name externaldns -{ - "id": "/subscriptions/id/resourceGroups/externaldns", - ... -} -# use the id from the previous step in the scopes argument ->az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/id/resourceGroups/externaldns" -n ExternalDnsServicePrincipal +# select the subscription +> az account set -s +... +``` +Create the service principal + +``` bash +> az ad sp create-for-rbac -n ExternalDnsServicePrincipal { "appId": "appId GUID", <-- aadClientId value ... @@ -97,6 +101,33 @@ A Service Principal with a minimum access level of contribute to the resource gr } ``` +Assign the rights for the service principal + +``` +# find out the resource ids of the resource group where the dns zone is deployed, and the dns zone itself +> az group show --name externaldns +{ + "id": "/subscriptions/id/resourceGroups/externaldns", + ... +} + +> az network dns zone show --name example.com -g externaldns +{ + "id": "/subscriptions/.../resourceGroups/externaldns/providers/Microsoft.Network/dnszones/example.com", + ... +} +``` +``` +# assign the rights to the created service principal, using the resource ids from previous step + +# 1. as a reader to the resource group +> az role assignment create --role "Reader" --assignee --scope + +# 2. as a contributor to DNS Zone itself +> az role assignment create --role "Contributor" --assignee --scope + +``` + Now you can create a file named 'azure.json' with values gathered above and with the structure of the example above. Use this file to create a Kubernetes secret: ``` From 3c646a39a1c6ee657c4271be9d9c718d750b19b9 Mon Sep 17 00:00:00 2001 From: Justin SB Date: Thu, 27 Dec 2018 12:34:57 -0500 Subject: [PATCH 020/136] Implement Stringer for planTableRow Makes for clearer log messages. --- plan/plan.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plan/plan.go b/plan/plan.go index 21bf5b677..cdbdf1837 100644 --- a/plan/plan.go +++ b/plan/plan.go @@ -17,6 +17,7 @@ limitations under the License. package plan import ( + "fmt" "strings" "github.com/kubernetes-incubator/external-dns/endpoint" @@ -78,6 +79,10 @@ type planTableRow struct { candidates []*endpoint.Endpoint } +func (t planTableRow) String() string { + return fmt.Sprintf("planTableRow{current=%v, candidates=%v}", t.current, t.candidates) +} + func (t planTable) addCurrent(e *endpoint.Endpoint) { dnsName := sanitizeDNSName(e.DNSName) if _, ok := t.rows[dnsName]; !ok { From 173ef2ea4b2f169f6d88985f5377923e55fba0d2 Mon Sep 17 00:00:00 2001 From: Justin SB Date: Thu, 27 Dec 2018 12:40:14 -0500 Subject: [PATCH 021/136] Normalize DNS names during planning Ensure that we don't consider names with and without a trailing dot differently at this stage. --- plan/plan.go | 16 ++++++++++------ plan/plan_test.go | 30 +++++++++++++++++------------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/plan/plan.go b/plan/plan.go index 21bf5b677..f3cdc6c19 100644 --- a/plan/plan.go +++ b/plan/plan.go @@ -79,7 +79,7 @@ type planTableRow struct { } func (t planTable) addCurrent(e *endpoint.Endpoint) { - dnsName := sanitizeDNSName(e.DNSName) + dnsName := normalizeDNSName(e.DNSName) if _, ok := t.rows[dnsName]; !ok { t.rows[dnsName] = &planTableRow{} } @@ -87,7 +87,7 @@ func (t planTable) addCurrent(e *endpoint.Endpoint) { } func (t planTable) addCandidate(e *endpoint.Endpoint) { - dnsName := sanitizeDNSName(e.DNSName) + dnsName := normalizeDNSName(e.DNSName) if _, ok := t.rows[dnsName]; !ok { t.rows[dnsName] = &planTableRow{} } @@ -204,8 +204,12 @@ func filterRecordsForPlan(records []*endpoint.Endpoint) []*endpoint.Endpoint { return filtered } -// sanitizeDNSName checks if the DNS name is correct -// for now it only removes space and lower case -func sanitizeDNSName(dnsName string) string { - return strings.TrimSpace(strings.ToLower(dnsName)) +// normalizeDNSName converts a DNS name to a canonical form, so that we can use string equality +// it: removes space, converts to lower case, ensures there is a trailing dot +func normalizeDNSName(dnsName string) string { + s := strings.TrimSpace(strings.ToLower(dnsName)) + if !strings.HasSuffix(s, ".") { + s += "." + } + return s } diff --git a/plan/plan_test.go b/plan/plan_test.go index e36b343a6..daeb8caa4 100644 --- a/plan/plan_test.go +++ b/plan/plan_test.go @@ -385,54 +385,58 @@ func validateEntries(t *testing.T, entries, expected []*endpoint.Endpoint) { } } -func TestSanitizeDNSName(t *testing.T) { +func TestNormalizeDNSName(t *testing.T) { records := []struct { dnsName string expect string }{ { "3AAAA.FOO.BAR.COM ", - "3aaaa.foo.bar.com", + "3aaaa.foo.bar.com.", }, { - " example.foo.com", - "example.foo.com", + " example.foo.com.", + "example.foo.com.", }, { "example123.foo.com ", - "example123.foo.com", + "example123.foo.com.", }, { "foo", - "foo", + "foo.", }, { "123foo.bar", - "123foo.bar", + "123foo.bar.", }, { "foo.com", - "foo.com", + "foo.com.", + }, + { + "foo.com.", + "foo.com.", }, { "foo123.COM", - "foo123.com", + "foo123.com.", }, { "my-exaMple3.FOO.BAR.COM", - "my-example3.foo.bar.com", + "my-example3.foo.bar.com.", }, { " my-example1214.FOO-1235.BAR-foo.COM ", - "my-example1214.foo-1235.bar-foo.com", + "my-example1214.foo-1235.bar-foo.com.", }, { "my-example-my-example-1214.FOO-1235.BAR-foo.COM", - "my-example-my-example-1214.foo-1235.bar-foo.com", + "my-example-my-example-1214.foo-1235.bar-foo.com.", }, } for _, r := range records { - gotName := sanitizeDNSName(r.dnsName) + gotName := normalizeDNSName(r.dnsName) assert.Equal(t, r.expect, gotName) } } From bb6c5f18057cde75de1ad52d5c96fd75d61e38f0 Mon Sep 17 00:00:00 2001 From: Ivan Filippov Date: Thu, 3 Jan 2019 05:01:50 -0700 Subject: [PATCH 022/136] RFC2136 seems to require one IP Target per RRSET instead of multiple IPs per RRSET. --- provider/rfc2136.go | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/provider/rfc2136.go b/provider/rfc2136.go index 7f9856bad..153f5e8e0 100644 --- a/provider/rfc2136.go +++ b/provider/rfc2136.go @@ -240,25 +240,26 @@ func (r rfc2136Provider) UpdateRecord(ep *endpoint.Endpoint) error { func (r rfc2136Provider) AddRecord(ep *endpoint.Endpoint) error { log.Debugf("AddRecord.ep=%s", ep) + for _, target := range ep.Targets { + newRR := fmt.Sprintf("%s %d %s %s", ep.DNSName, ep.RecordTTL, ep.RecordType, target) + log.Debugf("Adding RR: %s", newRR) - newRR := fmt.Sprintf("%s %d %s %s", ep.DNSName, ep.RecordTTL, ep.RecordType, ep.Targets) - log.Debugf("Adding RR: %s", newRR) + rr, err := dns.NewRR(newRR) + if err != nil { + return fmt.Errorf("failed to build RR: %v", err) + } - rr, err := dns.NewRR(newRR) - if err != nil { - return fmt.Errorf("failed to build RR: %v", err) - } + rrs := make([]dns.RR, 1) + rrs[0] = rr - rrs := make([]dns.RR, 1) - rrs[0] = rr + m := new(dns.Msg) + m.SetUpdate(r.zoneName) + m.Insert(rrs) - m := new(dns.Msg) - m.SetUpdate(r.zoneName) - m.Insert(rrs) - - err = r.actions.SendMessage(m) - if err != nil { - return fmt.Errorf("RFC2136 query failed: %v", err) + err = r.actions.SendMessage(m) + if err != nil { + return fmt.Errorf("RFC2136 query failed: %v", err) + } } return nil From d93f6ec24e7701668281ff877a3a0778b7fc0402 Mon Sep 17 00:00:00 2001 From: Lachlan Cooper Date: Fri, 4 Jan 2019 10:49:20 +1100 Subject: [PATCH 023/136] Fix typos in rfc2136 provider The rfc2136Actions interface was misspelled. Signed-off-by: Lachlan Cooper --- provider/rfc2136.go | 6 +++--- provider/rfc2136_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/provider/rfc2136.go b/provider/rfc2136.go index 7f9856bad..572f2e3c0 100644 --- a/provider/rfc2136.go +++ b/provider/rfc2136.go @@ -43,7 +43,7 @@ type rfc2136Provider struct { // only consider hosted zones managing domains ending in this suffix domainFilter DomainFilter dryRun bool - actions rfc1236Actions + actions rfc2136Actions } var ( @@ -56,13 +56,13 @@ var ( } ) -type rfc1236Actions interface { +type rfc2136Actions interface { SendMessage(msg *dns.Msg) error IncomeTransfer(m *dns.Msg, a string) (env chan *dns.Envelope, err error) } // NewRfc2136Provider is a factory function for OpenStack rfc2136 providers -func NewRfc2136Provider(host string, port int, zoneName string, insecure bool, keyName string, secret string, secretAlg string, axfr bool, domainFilter DomainFilter, dryRun bool, actions rfc1236Actions) (Provider, error) { +func NewRfc2136Provider(host string, port int, zoneName string, insecure bool, keyName string, secret string, secretAlg string, axfr bool, domainFilter DomainFilter, dryRun bool, actions rfc2136Actions) (Provider, error) { secretAlgChecked, ok := tsigAlgs[secretAlg] if !ok { return nil, errors.Errorf("%s is not supported TSIG algorithm", secretAlg) diff --git a/provider/rfc2136_test.go b/provider/rfc2136_test.go index 63d10abb2..cb9ef8a2b 100644 --- a/provider/rfc2136_test.go +++ b/provider/rfc2136_test.go @@ -93,7 +93,7 @@ func createRfc2136StubProvider(stub *rfc2136Stub) (Provider, error) { return NewRfc2136Provider("", 0, "", false, "key", "secret", "hmac-sha512", true, DomainFilter{}, false, stub) } -func TestRfc1236GetRecords(t *testing.T) { +func TestRfc2136GetRecords(t *testing.T) { stub := newStub() err := stub.setOutput([]string{ "v4.barfoo.com 3600 TXT test1", From 736a01633e0212c234a35d765dac1ba3eb153722 Mon Sep 17 00:00:00 2001 From: Lachlan Cooper Date: Fri, 4 Jan 2019 11:02:29 +1100 Subject: [PATCH 024/136] Fix dry-run mode in rfc2136 provider In dry-run mode we need to return early to avoid sending messages. Fixes #816. Signed-off-by: Lachlan Cooper --- provider/rfc2136.go | 1 + 1 file changed, 1 insertion(+) diff --git a/provider/rfc2136.go b/provider/rfc2136.go index 7f9856bad..469566d34 100644 --- a/provider/rfc2136.go +++ b/provider/rfc2136.go @@ -293,6 +293,7 @@ func (r rfc2136Provider) RemoveRecord(ep *endpoint.Endpoint) error { func (r rfc2136Provider) SendMessage(msg *dns.Msg) error { if r.dryRun { log.Debugf("SendMessage.skipped") + return nil } log.Debugf("SendMessage") From 391b536c13627b9c4bad19d14087c9f045e4bc3c Mon Sep 17 00:00:00 2001 From: Adam Medzinski Date: Fri, 4 Jan 2019 14:55:48 +0100 Subject: [PATCH 025/136] Change default AWSBatchChangeSize to 1000 AWS API ChangeResourceRecordSets method only allows 1000 ResourceRecord elements in one call, so the previous value was not very useful. --- pkg/apis/externaldns/types.go | 2 +- pkg/apis/externaldns/types_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 5e50ea374..5de4aca5c 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -130,7 +130,7 @@ var defaultConfig = &Config{ AWSZoneType: "", AWSZoneTagFilter: []string{}, AWSAssumeRole: "", - AWSBatchChangeSize: 4000, + AWSBatchChangeSize: 1000, AWSBatchChangeInterval: time.Second, AWSEvaluateTargetHealth: true, AzureConfigFile: "/etc/kubernetes/azure.json", diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index bdee0a22d..8a1f1854f 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -45,7 +45,7 @@ var ( AWSZoneType: "", AWSZoneTagFilter: []string{""}, AWSAssumeRole: "", - AWSBatchChangeSize: 4000, + AWSBatchChangeSize: 1000, AWSBatchChangeInterval: time.Second, AWSEvaluateTargetHealth: true, AzureConfigFile: "/etc/kubernetes/azure.json", From 9cc0fbf3e1ff57a161f089230ff972ce543aa524 Mon Sep 17 00:00:00 2001 From: Zach Seils Date: Fri, 4 Jan 2019 21:37:00 +0000 Subject: [PATCH 026/136] Correct Google Cloud DNS (ref: https://cloud.google.com/dns/) naming in docs --- README.md | 4 ++-- docs/contributing/getting-started.md | 2 +- docs/contributing/sources-and-providers.md | 4 ++-- docs/faq.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 25c2b5536..d2058e41b 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with DNS prov ## What It Does -Inspired by [Kubernetes DNS](https://github.com/kubernetes/dns), Kubernetes' cluster-internal DNS server, ExternalDNS makes Kubernetes resources discoverable via public DNS servers. Like KubeDNS, it retrieves a list of resources (Services, Ingresses, etc.) from the [Kubernetes API](https://kubernetes.io/docs/api/) to determine a desired list of DNS records. *Unlike* KubeDNS, however, it's not a DNS server itself, but merely configures other DNS providers accordingly—e.g. [AWS Route 53](https://aws.amazon.com/route53/) or [Google CloudDNS](https://cloud.google.com/dns/docs/). +Inspired by [Kubernetes DNS](https://github.com/kubernetes/dns), Kubernetes' cluster-internal DNS server, ExternalDNS makes Kubernetes resources discoverable via public DNS servers. Like KubeDNS, it retrieves a list of resources (Services, Ingresses, etc.) from the [Kubernetes API](https://kubernetes.io/docs/api/) to determine a desired list of DNS records. *Unlike* KubeDNS, however, it's not a DNS server itself, but merely configures other DNS providers accordingly—e.g. [AWS Route 53](https://aws.amazon.com/route53/) or [Google Cloud DNS](https://cloud.google.com/dns/docs/). In a broader sense, ExternalDNS allows you to control DNS records dynamically via Kubernetes resources in a DNS provider-agnostic way. @@ -24,7 +24,7 @@ To see ExternalDNS in action, have a look at this [video](https://www.youtube.co ## The Latest Release: v0.5 ExternalDNS' current release is `v0.5`. This version allows you to keep selected zones (via `--domain-filter`) synchronized with Ingresses and Services of `type=LoadBalancer` in various cloud providers: -* [Google CloudDNS](https://cloud.google.com/dns/docs/) +* [Google Cloud DNS](https://cloud.google.com/dns/docs/) * [AWS Route 53](https://aws.amazon.com/route53/) * [AWS Service Discovery](https://docs.aws.amazon.com/Route53/latest/APIReference/overview-service-discovery.html) * [AzureDNS](https://azure.microsoft.com/en-us/services/dns) diff --git a/docs/contributing/getting-started.md b/docs/contributing/getting-started.md index 8ce2325c6..9fe764c37 100644 --- a/docs/contributing/getting-started.md +++ b/docs/contributing/getting-started.md @@ -14,7 +14,7 @@ This list of endpoints is passed to the [Plan](../../plan) which determines the Once the difference has been figured out the list of intended changes is passed to a `Registry` which live in the [registry](../../registry) package. The registry is a wrapper and access point to DNS provider. Registry implements the ownership concept by marking owned records and filtering out records not owned by ExternalDNS before passing them to DNS provider. -The [provider](../../provider) is the adapter to the DNS provider, e.g. Google CloudDNS. It implements two methods: `ApplyChanges` to apply a set of changes filtered by `Registry` and `Records` to retrieve the current list of records from the DNS provider. +The [provider](../../provider) is the adapter to the DNS provider, e.g. Google Cloud DNS. It implements two methods: `ApplyChanges` to apply a set of changes filtered by `Registry` and `Records` to retrieve the current list of records from the DNS provider. The orchestration between the different components is controlled by the [controller](../../controller). diff --git a/docs/contributing/sources-and-providers.md b/docs/contributing/sources-and-providers.md index e5d1cd995..3d51b3598 100644 --- a/docs/contributing/sources-and-providers.md +++ b/docs/contributing/sources-and-providers.md @@ -29,7 +29,7 @@ All sources live in package `source`. ### Providers Providers are an abstraction over any kind of sink for desired Endpoints, e.g.: -* storing them in Google CloudDNS +* storing them in Google Cloud DNS * printing them to stdout for testing purposes * fanning out to multiple nested providers @@ -46,7 +46,7 @@ The interface tries to be generic and assumes a flat list of records for both fu All providers live in package `provider`. -* `GoogleProvider`: returns and creates DNS records in Google CloudDNS +* `GoogleProvider`: returns and creates DNS records in Google Cloud DNS * `AWSProvider`: returns and creates DNS records in AWS Route 53 * `AzureProvider`: returns and creates DNS records in Azure DNS * `InMemoryProvider`: Keeps a list of records in local memory diff --git a/docs/faq.md b/docs/faq.md index 927a5b9a4..38b72aac3 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -30,7 +30,7 @@ ExternalDNS can solve this for you as well. Currently, the following providers are supported: -- Google CloudDNS +- Google Cloud DNS - AWS Route 53 - AzureDNS - CloudFlare From 123cc398f1985544148819d7a75970b56c0d1535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nick=20J=C3=BCttner?= Date: Mon, 7 Jan 2019 14:13:26 +0100 Subject: [PATCH 027/136] add security file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nick Jüttner --- SECURITY_CONTACTS | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 SECURITY_CONTACTS diff --git a/SECURITY_CONTACTS b/SECURITY_CONTACTS new file mode 100644 index 000000000..16ccfb1fe --- /dev/null +++ b/SECURITY_CONTACTS @@ -0,0 +1,16 @@ +# Defined below are the security contacts for this repo. +# +# They are the contact point for the Product Security Team to reach out +# to for triaging and handling of incoming issues. +# +# The below names agree to abide by the +# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy) +# and will be removed and replaced if they violate that agreement. +# +# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE +# INSTRUCTIONS AT https://kubernetes.io/security/ + +linki +njuettner +hjacobs +raffo From ca0d3aa59ccb21b764071f6bf257de280f23a2cb Mon Sep 17 00:00:00 2001 From: Joakim Olsson Date: Mon, 7 Jan 2019 15:37:22 +0100 Subject: [PATCH 028/136] Add support for eu-north-1 --- provider/aws.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/provider/aws.go b/provider/aws.go index ec14c8d35..fb328cbd2 100644 --- a/provider/aws.go +++ b/provider/aws.go @@ -58,6 +58,7 @@ var ( "eu-west-1.elb.amazonaws.com": "Z32O12XQLNTSW2", "eu-west-2.elb.amazonaws.com": "ZHURV8PSTC4K8", "eu-west-3.elb.amazonaws.com": "Z3Q77PNBQS71R4", + "eu-north-1.elb.amazonaws.com": "Z23TAZ6LKFMNIO", "sa-east-1.elb.amazonaws.com": "Z2P70J7HTTTPLU", // Network Load Balancers "elb.us-east-2.amazonaws.com": "ZLMOA37VPKANP", @@ -74,6 +75,7 @@ var ( "elb.eu-west-1.amazonaws.com": "Z2IFOLAFXWLO4F", "elb.eu-west-2.amazonaws.com": "ZD4D7Y8KGAS4G", "elb.eu-west-3.amazonaws.com": "Z1CMS0P5QUZ6D5", + "elb.eu-north-1.amazonaws.com": "Z1UDT6IFJ4EJM", "elb.sa-east-1.amazonaws.com": "ZTK26PT1VY4CU", } ) From 451bfa879f13ee1b70a11c26d21ddb9cc508f19e Mon Sep 17 00:00:00 2001 From: Zach Yam Date: Mon, 7 Jan 2019 15:11:00 -0500 Subject: [PATCH 029/136] Clarify registry error info --- docs/faq.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 99a622ea7..8c01b966d 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -159,9 +159,9 @@ You need to add either https://www.googleapis.com/auth/ndev.clouddns.readwrite o ExternalDNS exposes 2 types of metrics: Sources and Registry errors. -`Source`s are mostly Kubernetes API objects. Examples of `source` errors may be connection errors to the Kubernetes API server itself or missing RBAC permissions. It can also stem from incompatible configuration in the objects itself like invalid characters, processing a broken fqdnTemplate etc. +`Source`s are mostly Kubernetes API objects. Examples of `source` errors may be connection errors to the Kubernetes API server itself or missing RBAC permissions. It can also stem from incompatible configuration in the objects itself like invalid characters, processing a broken fqdnTemplate, etc. -`Registry` errors are mostly Provider errors, unless there's some coding flaw in the registry package. Provider errors often arise due to accessing their APIs due to network or missing cloud-provider permissions when reading records. When applying a changeset, errors arise when the changeset applied is incompatible with the current state, in which case ExternalDNS can get stuck forever. +`Registry` errors are mostly Provider errors, unless there's some coding flaw in the registry package. Provider errors often arise due to accessing their APIs due to network or missing cloud-provider permissions when reading records. When applying a changeset, errors will arise if the changeset applied is incompatible with the current state. In case of an increased error count, you could correlate them with the `http_request_duration_seconds{handler="instrumented_http"}` metric which should show increased numbers for status codes 4xx (permissions, configuration, invalid changeset) or 5xx (apiserver down). From f2ed58f00da4086725b54b1bd17ab4803a610cba Mon Sep 17 00:00:00 2001 From: xianlubird Date: Wed, 9 Jan 2019 11:06:36 +0800 Subject: [PATCH 030/136] Fix private zone dns record does not work --- provider/alibaba_cloud.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/provider/alibaba_cloud.go b/provider/alibaba_cloud.go index 79663577d..1bfcd3566 100644 --- a/provider/alibaba_cloud.go +++ b/provider/alibaba_cloud.go @@ -42,6 +42,7 @@ const ( defaultAlibabaCloudPrivateZoneRecordTTL = 60 defaultAlibabaCloudPageSize = 50 nullHostAlibabaCloud = "@" + pVTZDoamin = "pvtz.aliyuncs.com" ) // AlibabaCloudDNSAPI is a minimal implementation of DNS API that we actually use, used primarily for unit testing. @@ -708,6 +709,7 @@ func (p *AlibabaCloudProvider) splitDNSName(endpoint *endpoint.Endpoint) (rr str func (p *AlibabaCloudProvider) matchVPC(zoneID string) bool { request := pvtz.CreateDescribeZoneInfoRequest() request.ZoneId = zoneID + request.Domain = pVTZDoamin response, err := p.getPvtzClient().DescribeZoneInfo(request) if err != nil { log.Errorf("Failed to describe zone info %s in Alibaba Cloud DNS: %v", zoneID, err) @@ -730,7 +732,7 @@ func (p *AlibabaCloudProvider) privateZones() ([]pvtz.Zone, error) { request := pvtz.CreateDescribeZonesRequest() request.PageSize = requests.NewInteger(defaultAlibabaCloudPageSize) request.PageNumber = "1" - + request.Domain = pVTZDoamin for { response, err := p.getPvtzClient().DescribeZones(request) if err != nil { @@ -738,7 +740,7 @@ func (p *AlibabaCloudProvider) privateZones() ([]pvtz.Zone, error) { return nil, err } for _, zone := range response.Zones.Zone { - log.Debugf("Zone: %++v", zone) + log.Infof("PrivateZones zone: %++v", zone) if !p.zoneIDFilter.Match(zone.ZoneId) { continue @@ -784,6 +786,7 @@ func (p *AlibabaCloudProvider) getPrivateZones() (map[string]*alibabaPrivateZone request.ZoneId = zone.ZoneId request.PageSize = requests.NewInteger(defaultAlibabaCloudPageSize) request.PageNumber = "1" + request.Domain = pVTZDoamin var records []pvtz.Record for { @@ -884,6 +887,7 @@ func (p *AlibabaCloudProvider) createPrivateZoneRecord(zones map[string]*alibaba request.ZoneId = zone.ZoneId request.Type = endpoint.RecordType request.Rr = rr + request.Domain = pVTZDoamin ttl := int(endpoint.RecordTTL) if ttl != 0 { @@ -927,6 +931,7 @@ func (p *AlibabaCloudProvider) deletePrivateZoneRecord(recordID int) error { request := pvtz.CreateDeleteZoneRecordRequest() request.RecordId = requests.NewInteger(recordID) + request.Domain = pVTZDoamin response, err := p.getPvtzClient().DeleteZoneRecord(request) if err == nil { @@ -998,6 +1003,7 @@ func (p *AlibabaCloudProvider) updatePrivateZoneRecord(record pvtz.Record, endpo request.Rr = record.Rr request.Type = record.Type request.Value = record.Value + request.Domain = pVTZDoamin ttl := int(endpoint.RecordTTL) if ttl != 0 { request.Ttl = requests.NewInteger(ttl) From 6927af40673110634169c0434daa5df16f47c1ba Mon Sep 17 00:00:00 2001 From: Sheng Lao Date: Sat, 12 Jan 2019 00:06:43 +0800 Subject: [PATCH 031/136] Add apiVersion to ingress.yaml, and Delete the duplicated line in dnstools --- docs/tutorials/coredns.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/coredns.md b/docs/tutorials/coredns.md index e20699c89..de8b16fca 100644 --- a/docs/tutorials/coredns.md +++ b/docs/tutorials/coredns.md @@ -197,6 +197,7 @@ minikube addons enable ingress ## Testing ingress example ``` $ cat ingress.yaml +apiVersion: extensions/v1beta1 kind: Ingress metadata: name: nginx @@ -229,7 +230,6 @@ nginx nginx.example.org 10.0.2.15 80 2m $ kubectl run -it --rm --restart=Never --image=infoblox/dnstools:latest dnstools If you don't see a command prompt, try pressing enter. dnstools# dig @10.100.4.143 nginx.example.org +short -dnstools# dig @10.100.4.143 nginx.example.org +short 10.0.2.15 dnstools# ``` From a5441bddea0a04a430cde2565644d6a2a4032d92 Mon Sep 17 00:00:00 2001 From: Erik Swets Date: Fri, 11 Jan 2019 20:15:54 +0100 Subject: [PATCH 032/136] Support updating ProviderSpecific property. --- Gopkg.lock | 328 ++++++----------------------------------- Gopkg.toml | 4 + plan/plan.go | 7 +- plan/plan_test.go | 71 +++++++-- provider/cloudflare.go | 2 +- 5 files changed, 120 insertions(+), 292 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 7ae69152a..29912ae53 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,66 +2,53 @@ [[projects]] - digest = "1:ae9d0182a5cf7dbb025a8fc5821234cc1f26ca342fc41d951a99f71b9adc1b87" name = "cloud.google.com/go" packages = [ "compute/metadata", - "internal", + "internal" ] - pruneopts = "" revision = "3b1ae45394a234c385be014e9a488f2bb6eef821" [[projects]] - digest = "1:fd38e3b8c27cab6561a7d2e8557205c3ca5c57cbb6d3a79e10f22e73e84fd776" name = "github.com/Azure/azure-sdk-for-go" packages = ["arm/dns"] - pruneopts = "" revision = "2629e2dfcfeab50896230140542c3b9d89b35ae1" version = "v10.0.4-beta" [[projects]] - digest = "1:f719ef698feb8da2923ebda9a8d553b977320b02182f3797e185202e588a94b1" name = "github.com/Azure/go-autorest" packages = [ "autorest", "autorest/adal", "autorest/azure", "autorest/date", - "autorest/to", + "autorest/to" ] - pruneopts = "" revision = "aa2a4534ab680e938d933870f58f23f77e0e208e" version = "v10.9.0" [[projects]] - digest = "1:7dc69d1597e4773ec5f64e5c078d55f0f011bb05ec0435346d0649ad978a23fd" name = "github.com/alecthomas/kingpin" packages = ["."] - pruneopts = "" revision = "1087e65c9441605df944fb12c33f0fe7072d18ca" version = "v2.2.5" [[projects]] branch = "master" - digest = "1:a74730e052a45a3fab1d310fdef2ec17ae3d6af16228421e238320846f2aaec8" name = "github.com/alecthomas/template" packages = [ ".", - "parse", + "parse" ] - pruneopts = "" revision = "a0175ee3bccc567396460bf5acd36800cb10c49c" [[projects]] branch = "master" - digest = "1:8483994d21404c8a1d489f6be756e25bfccd3b45d65821f25695577791a08e68" name = "github.com/alecthomas/units" packages = ["."] - pruneopts = "" revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" [[projects]] - digest = "1:d2dc5d0ccc137594ea6fb3870964967b96b43daac19b8093930c7b02873fd5ca" name = "github.com/aliyun/alibaba-cloud-sdk-go" packages = [ "sdk", @@ -74,14 +61,12 @@ "sdk/responses", "sdk/utils", "services/alidns", - "services/pvtz", + "services/pvtz" ] - pruneopts = "" revision = "cad214d7d71fba7883fcf3b7e550ba782c15b400" version = "1.27.7" [[projects]] - digest = "1:1c82dd6a02941a3c4f23a32eca182717ab79691939e97d6b971466b780f06eea" name = "github.com/aws/aws-sdk-go" packages = [ "aws", @@ -113,30 +98,24 @@ "private/protocol/xml/xmlutil", "service/route53", "service/servicediscovery", - "service/sts", + "service/sts" ] - pruneopts = "" revision = "9b0098a71f6d4d473a26ec8ad3c2feaac6eb1da6" version = "v1.13.32" [[projects]] branch = "master" - digest = "1:0c5485088ce274fac2e931c1b979f2619345097b39d91af3239977114adf0320" name = "github.com/beorn7/perks" packages = ["quantile"] - pruneopts = "" revision = "4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9" [[projects]] - digest = "1:85fd00554a6ed5b33687684b76635d532c74141508b5bce2843d85e8a3c9dc91" name = "github.com/cloudflare/cloudflare-go" packages = ["."] - pruneopts = "" revision = "4c6994ac3877fbb627766edadc67f4e816e8c890" version = "v0.7.4" [[projects]] - digest = "1:eaeede87b418b97f9dee473f8940fd9b65ca5cdac0503350c7c8f8965ea3cf4d" name = "github.com/coreos/etcd" packages = [ "auth/authpb", @@ -144,108 +123,84 @@ "etcdserver/api/v3rpc/rpctypes", "etcdserver/etcdserverpb", "mvcc/mvccpb", - "pkg/types", + "pkg/types" ] - pruneopts = "" revision = "1b3ac99e8a431b381e633802cc42fe70e663baf5" version = "v3.2.15" [[projects]] - digest = "1:56c130d885a4aacae1dd9c7b71cfe39912c7ebc1ff7d2b46083c8812996dc43b" name = "github.com/davecgh/go-spew" packages = ["spew"] - pruneopts = "" revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" [[projects]] branch = "master" - digest = "1:dc166ce7345c060c2153561130d6d49ac580c804226ac675e368d533b36eb169" name = "github.com/denverdino/aliyungo" packages = [ "metadata", - "util", + "util" ] - pruneopts = "" revision = "69560d9530f5265ba00ffad2520d7ef01c2cddd4" [[projects]] - digest = "1:6098222470fe0172157ce9bbef5d2200df4edde17ee649c5d6e48330e4afa4c6" name = "github.com/dgrijalva/jwt-go" packages = ["."] - pruneopts = "" revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e" version = "v3.2.0" [[projects]] - digest = "1:32d1941b093bb945de75b0276348494be318d34f3df39c4413d61e002c800bc6" name = "github.com/digitalocean/godo" packages = [ ".", - "context", + "context" ] - pruneopts = "" revision = "77ea48de76a7b31b234d854f15d003c68bb2fb90" version = "v1.1.1" [[projects]] - digest = "1:5ffd39844bdd1259a6227d544f582c6686ce43c8c44399a46052fe3bd2bed93c" name = "github.com/dnsimple/dnsimple-go" packages = ["dnsimple"] - pruneopts = "" revision = "d1105abc03b313d7b8d9b04364f6bd053b346e59" version = "v0.14.0" [[projects]] - digest = "1:e17d18b233f506404061c27ac4a08624dad38dcd0d49f9cfdae67a7772a4fb8c" name = "github.com/exoscale/egoscale" packages = ["."] - pruneopts = "" revision = "c6d915cb993f1a54f604acefc0fc15cf6578a87a" version = "v0.11.0" [[projects]] branch = "master" - digest = "1:ae7fb2062735e966ab69d14d2a091f3778b0d676dc8d1f01d092bcb0fb8ed45b" name = "github.com/ffledgling/pdns-go" packages = ["."] - pruneopts = "" revision = "524e7daccd99651cdb56426eb15b7d61f9597a5c" [[projects]] - digest = "1:b13707423743d41665fd23f0c36b2f37bb49c30e94adb813319c44188a51ba22" name = "github.com/ghodss/yaml" packages = ["."] - pruneopts = "" revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7" version = "v1.0.0" [[projects]] - digest = "1:a00483fe4106b86fb1187a92b5cf6915c85f294ed4c129ccbe7cb1f1a06abd46" name = "github.com/go-ini/ini" packages = ["."] - pruneopts = "" revision = "32e4c1e6bc4e7d0d8451aa6b75200d19e37a536a" version = "v1.32.0" [[projects]] - digest = "1:8e67153fc0a9fb0d6c9707e36cf80e217a012364307b222eb4ba6828f7e881e6" name = "github.com/go-resty/resty" packages = ["."] - pruneopts = "" revision = "97a15579492cd5f35632499f315d7a8df94160a1" version = "v1.8.0" [[projects]] - digest = "1:54d5c6a784a9de9c836fc070d51d0689c3e99ee6d24dba8a36f0762039dae830" name = "github.com/gogo/googleapis" packages = ["google/rpc"] - pruneopts = "" revision = "8558fb44d2f1fc223118afc694129d2c2d2924d1" version = "v1.1.0" [[projects]] - digest = "1:6e73003ecd35f4487a5e88270d3ca0a81bc80dc88053ac7e4dcfec5fba30d918" name = "github.com/gogo/protobuf" packages = [ "gogoproto", @@ -253,23 +208,19 @@ "proto", "protoc-gen-gogo/descriptor", "sortkeys", - "types", + "types" ] - pruneopts = "" revision = "636bf0302bc95575d69441b25a2603156ffdddf1" version = "v1.1.1" [[projects]] branch = "master" - digest = "1:b12aff239810a9fa71e901a712a52f9da4c6e536852e943be693dec1d4519dfd" name = "github.com/golang/glog" packages = ["."] - pruneopts = "" revision = "3fa5b9870d1d29f6d7907b29f1ae8c6eeb403829" source = "github.com/kubermatic/glog-logrus" [[projects]] - digest = "1:3dd078fda7500c341bc26cfbc6c6a34614f295a2457149fc1045cab767cbcf18" name = "github.com/golang/protobuf" packages = [ "jsonpb", @@ -279,50 +230,51 @@ "ptypes/any", "ptypes/duration", "ptypes/struct", - "ptypes/timestamp", + "ptypes/timestamp" ] - pruneopts = "" revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" version = "v1.2.0" [[projects]] branch = "master" - digest = "1:be28c0531a755f2178acf1e327e6f5a8a3968feb5f2567cdc968064253141751" name = "github.com/google/btree" packages = ["."] - pruneopts = "" revision = "e89373fe6b4a7413d7acd6da1725b83ef713e6e4" +[[projects]] + name = "github.com/google/go-cmp" + packages = [ + "cmp", + "cmp/internal/diff", + "cmp/internal/function", + "cmp/internal/value" + ] + revision = "3af367b6b30c263d47e8895973edcca9a49cf029" + version = "v0.2.0" + [[projects]] branch = "master" - digest = "1:9abc49f39e3e23e262594bb4fb70abf74c0c99e94f99153f43b143805e850719" name = "github.com/google/go-querystring" packages = ["query"] - pruneopts = "" revision = "53e6ce116135b80d037921a7fdd5138cf32d7a8a" [[projects]] - digest = "1:a2823c34933d4a2b36284f617f483d51fe156a443923284b3660f183dcfa3338" name = "github.com/google/gofuzz" packages = ["."] - pruneopts = "" revision = "44d81051d367757e1c7c6a5a86423ece9afcf63c" [[projects]] - digest = "1:16b2837c8b3cf045fa2cdc82af0cf78b19582701394484ae76b2c3bc3c99ad73" name = "github.com/googleapis/gnostic" packages = [ "OpenAPIv2", "compiler", - "extensions", + "extensions" ] - pruneopts = "" revision = "7c663266750e7d82587642f65e60bc4083f1f84e" version = "v0.2.0" [[projects]] branch = "master" - digest = "1:54a44d48a24a104e078ef5f94d82f025a6be757e7c42b4370c621a3928d6ab7c" name = "github.com/gophercloud/gophercloud" packages = [ ".", @@ -333,336 +285,258 @@ "openstack/identity/v2/tokens", "openstack/identity/v3/tokens", "openstack/utils", - "pagination", + "pagination" ] - pruneopts = "" revision = "bfc4756e1a693a850d7d459f4b28b21f35a24b5a" [[projects]] - digest = "1:dbbeb8ddb0be949954c8157ee8439c2adfd8dc1c9510eb44a6e58cb68c3dce28" name = "github.com/gorilla/context" packages = ["."] - pruneopts = "" revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42" version = "v1.1.1" [[projects]] - digest = "1:c2c8666b4836c81a1d247bdf21c6a6fc1ab586538ab56f74437c2e0df5c375e1" name = "github.com/gorilla/mux" packages = ["."] - pruneopts = "" revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf" version = "v1.6.2" [[projects]] branch = "master" - digest = "1:009a1928b8c096338b68b5822d838a72b4d8520715c1463614476359f3282ec8" name = "github.com/gregjones/httpcache" packages = [ ".", - "diskcache", + "diskcache" ] - pruneopts = "" revision = "9cad4c3443a7200dd6400aef47183728de563a38" [[projects]] - digest = "1:8e3bd93036b4a925fe2250d3e4f38f21cadb8ef623561cd80c3c50c114b13201" name = "github.com/hashicorp/errwrap" packages = ["."] - pruneopts = "" revision = "8a6fb523712970c966eefc6b39ed2c5e74880354" version = "v1.0.0" [[projects]] branch = "master" - digest = "1:72308fdd6d5ef61106a95be7ca72349a5565809042b6426a3cfb61d99483b824" name = "github.com/hashicorp/go-multierror" packages = ["."] - pruneopts = "" revision = "886a7fbe3eb1c874d46f623bfa70af45f425b3d1" [[projects]] - digest = "1:3313a63031ae281e5f6fd7b0bbca733dfa04d2429df86519e3b4d4c016ccb836" name = "github.com/hashicorp/golang-lru" packages = [ ".", - "simplelru", + "simplelru" ] - pruneopts = "" revision = "20f1fb78b0740ba8c3cb143a61e86ba5c8669768" version = "v0.5.0" [[projects]] - digest = "1:af7e132906cb360f4d7c34a9e1434825467f21c4ff5c521ad4cc5b55352876a8" name = "github.com/imdario/mergo" packages = ["."] - pruneopts = "" revision = "6633656539c1639d9d78127b7d47c622b5d7b6dc" [[projects]] - digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" name = "github.com/inconshreveable/mousetrap" packages = ["."] - pruneopts = "" revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" version = "v1.0" [[projects]] branch = "master" - digest = "1:e0a13d0a368c028716e78448db972657b5292c7238d61405e8289f47c05c8706" name = "github.com/infobloxopen/infoblox-go-client" packages = ["."] - pruneopts = "" revision = "61dc5f9b0a655ebf43026f0d8a837ad1e28e4b96" [[projects]] - digest = "1:6f49eae0c1e5dab1dafafee34b207aeb7a42303105960944828c2079b92fc88e" name = "github.com/jmespath/go-jmespath" packages = ["."] - pruneopts = "" revision = "0b12d6b5" [[projects]] - digest = "1:53ac4e911e12dde0ab68655e2006449d207a5a681f084974da2b06e5dbeaca72" name = "github.com/json-iterator/go" packages = ["."] - pruneopts = "" revision = "ab8a2e0c74be9d3be70b3184d9acc634935ded82" version = "1.1.4" [[projects]] - digest = "1:1c88ec29544b281964ed7a9a365b2802a523cd06c50cdee87eb3eec89cd864f4" name = "github.com/kubernetes/repo-infra" packages = ["verify/boilerplate/test"] - pruneopts = "" revision = "c2f9667a4c29e70a39b0e89db2d4f0cab907dbee" [[projects]] - digest = "1:7c23a751ce2f84663fa411acb87eae0da2d09c39a8e99b08bd8f65fae75d8928" name = "github.com/linki/instrumented_http" packages = ["."] - pruneopts = "" revision = "508103cfb3b315fa9752b5bcd4fb2d97bbc26d89" version = "v0.2.0" [[projects]] - digest = "1:1c41354ef11c9dbae2fe1ceee8369fcb2634977ba07e701e19ea53e8742c5420" name = "github.com/linode/linodego" packages = ["."] - pruneopts = "" revision = "7edbc87f0140b7561dbc20458877a56bdded5eb8" version = "v0.3.0" [[projects]] branch = "master" - digest = "1:63722a4b1e1717be7b98fc686e0b30d5e7f734b9e93d7dee86293b6deab7ea28" name = "github.com/matttproud/golang_protobuf_extensions" packages = ["pbutil"] - pruneopts = "" revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" [[projects]] - digest = "1:4c8d8358c45ba11ab7bb15df749d4df8664ff1582daead28bae58cf8cbe49890" name = "github.com/miekg/dns" packages = ["."] - pruneopts = "" revision = "5a2b9fab83ff0f8bfc99684bd5f43a37abe560f1" version = "v1.0.8" [[projects]] - digest = "1:0c0ff2a89c1bb0d01887e1dac043ad7efbf3ec77482ef058ac423d13497e16fd" name = "github.com/modern-go/concurrent" packages = ["."] - pruneopts = "" revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94" version = "1.0.3" [[projects]] - digest = "1:e32bdbdb7c377a07a9a46378290059822efdce5c8d96fe71940d87cb4f918855" name = "github.com/modern-go/reflect2" packages = ["."] - pruneopts = "" revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" version = "1.0.1" [[projects]] - digest = "1:11c58e19ff7ce22740423bb933f1ddca3bf575def40d5ac3437ec12871b1648b" name = "github.com/natefinch/lumberjack" packages = ["."] - pruneopts = "" revision = "a96e63847dc3c67d17befa69c303767e2f84e54f" version = "v2.1" [[projects]] - digest = "1:d8b5d0ecca348c835914a1ed8589f17a6a7f309befab7327b0470324531f7ac4" name = "github.com/nesv/go-dynect" packages = ["dynect"] - pruneopts = "" revision = "cdd946344b54bdf7dbeac406c2f1fe93150f08ea" version = "v0.6.0" [[projects]] - digest = "1:70df8e71a953626770223d4982801fa73e7e99cbfcca068b95127f72af9b9edd" name = "github.com/oracle/oci-go-sdk" packages = [ "common", - "dns", + "dns" ] - pruneopts = "" revision = "a2ded717dc4bb4916c0416ec79f81718b576dbc4" version = "v1.8.0" [[projects]] branch = "master" - digest = "1:c24598ffeadd2762552269271b3b1510df2d83ee6696c1e543a0ff653af494bc" name = "github.com/petar/GoLLRB" packages = ["llrb"] - pruneopts = "" revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4" [[projects]] - digest = "1:b46305723171710475f2dd37547edd57b67b9de9f2a6267cafdd98331fd6897f" name = "github.com/peterbourgon/diskv" packages = ["."] - pruneopts = "" revision = "5f041e8faa004a95c88a202771f4cc3e991971e6" version = "v2.0.1" [[projects]] - digest = "1:cf172c58bb2a13ed39ea1c9e79525567c63bcc2c4afbb6cf023e87b31780f249" name = "github.com/pkg/errors" packages = ["."] - pruneopts = "" revision = "f15c970de5b76fac0b59abb32d62c17cc7bed265" [[projects]] - digest = "1:256484dbbcd271f9ecebc6795b2df8cad4c458dd0f5fd82a8c2fa0c29f233411" name = "github.com/pmezard/go-difflib" packages = ["difflib"] - pruneopts = "" revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" [[projects]] - digest = "1:2f69dc6b2685b31a1a410ef697410aa8a669704fb201d45dbd8c1911728afa75" name = "github.com/prometheus/client_golang" packages = [ "prometheus", - "prometheus/promhttp", + "prometheus/promhttp" ] - pruneopts = "" revision = "967789050ba94deca04a5e84cce8ad472ce313c1" version = "v0.9.0-pre1" [[projects]] branch = "master" - digest = "1:60aca47f4eeeb972f1b9da7e7db51dee15ff6c59f7b401c1588b8e6771ba15ef" name = "github.com/prometheus/client_model" packages = ["go"] - pruneopts = "" revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c" [[projects]] branch = "master" - digest = "1:e3aa5178be4fc4ae8cdb37d11c02f7490c00450a9f419e6aa84d02d3b47e90d2" name = "github.com/prometheus/common" packages = [ "expfmt", "internal/bitbucket.org/ww/goautoneg", - "model", + "model" ] - pruneopts = "" revision = "2e54d0b93cba2fd133edc32211dcc32c06ef72ca" [[projects]] - digest = "1:a6a85fc81f2a06ccac3d45005523afbeee45138d781d4f3cb7ad9889d5c65aab" name = "github.com/prometheus/procfs" packages = [ ".", - "xfs", + "xfs" ] - pruneopts = "" revision = "a6e9df898b1336106c743392c48ee0b71f5c4efa" [[projects]] - digest = "1:7f569d906bdd20d906b606415b7d794f798f91a62fcfb6a4daa6d50690fb7a3f" name = "github.com/satori/go.uuid" packages = ["."] - pruneopts = "" revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3" version = "v1.2.0" [[projects]] - digest = "1:3ac248add5bb40a3c631c5334adcd09aa72d15af2768a5bc0274084ea7b2e5ba" name = "github.com/sirupsen/logrus" packages = ["."] - pruneopts = "" revision = "f006c2ac4710855cf0f916dd6b77acf6b048dc6e" version = "v1.0.3" [[projects]] - digest = "1:a1403cc8a94b8d7956ee5e9694badef0e7b051af289caad1cf668331e3ffa4f6" name = "github.com/spf13/cobra" packages = ["."] - pruneopts = "" revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385" version = "v0.0.3" [[projects]] - digest = "1:0a52bcb568386d98f4894575d53ce3e456f56471de6897bb8b9de13c33d9340e" name = "github.com/spf13/pflag" packages = ["."] - pruneopts = "" revision = "9a97c102cda95a86cec2345a6f09f55a939babf5" version = "v1.0.2" [[projects]] - digest = "1:306417ea2f31ea733df356a2b895de63776b6a5107085b33458e5cd6eb1d584d" name = "github.com/stretchr/objx" packages = ["."] - pruneopts = "" revision = "facf9a85c22f48d2f52f2380e4efce1768749a89" version = "v0.1" [[projects]] - digest = "1:a30066593578732a356dc7e5d7f78d69184ca65aeeff5939241a3ab10559bb06" name = "github.com/stretchr/testify" packages = [ "assert", "mock", "require", - "suite", + "suite" ] - pruneopts = "" revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" version = "v1.2.1" [[projects]] branch = "master" - digest = "1:81f435c83e3523a7ee3f277769727f73ca66218ca8188d96a0935a4841b47a76" name = "github.com/tent/http-link-go" packages = ["."] - pruneopts = "" revision = "ac974c61c2f990f4115b119354b5e0b47550e888" [[projects]] - digest = "1:74f86c458e82e1c4efbab95233e0cf51b7cc02dc03193be9f62cd81224e10401" name = "go.uber.org/atomic" packages = ["."] - pruneopts = "" revision = "1ea20fb1cbb1cc08cbd0d913a96dead89aa18289" version = "v1.3.2" [[projects]] - digest = "1:22c7effcb4da0eacb2bb1940ee173fac010e9ef3c691f5de4b524d538bd980f5" name = "go.uber.org/multierr" packages = ["."] - pruneopts = "" revision = "3c4937480c32f4c13a875a1829af76c98ca3d40a" version = "v1.1.0" [[projects]] - digest = "1:246f378f80fba6fcf0f191c486b6613265abd2bc0f2fa55a36b928c67352021e" name = "go.uber.org/zap" packages = [ ".", @@ -671,26 +545,22 @@ "internal/color", "internal/exit", "zapcore", - "zapgrpc", + "zapgrpc" ] - pruneopts = "" revision = "ff33455a0e382e8a81d14dd7c922020b6b5e7982" version = "v1.9.1" [[projects]] branch = "master" - digest = "1:b2d8b39397ca07929a3de3a3fd2b6ca4c8d48e9cadaa7cf2b083e27fd9e78107" name = "golang.org/x/crypto" packages = [ "ed25519", "ed25519/internal/edwards25519", - "ssh/terminal", + "ssh/terminal" ] - pruneopts = "" revision = "0709b304e793a5edb4a2c0145f281ecdc20838a4" [[projects]] - digest = "1:782723d6fc27d202f1943219d68d58b3f6bcab6212c85294b1ddd8b586b1d356" name = "golang.org/x/net" packages = [ "bpf", @@ -706,36 +576,30 @@ "ipv4", "ipv6", "publicsuffix", - "trace", + "trace" ] - pruneopts = "" revision = "161cd47e91fd58ac17490ef4d742dc98bb4cf60e" [[projects]] - digest = "1:dad5a319c4710358be1f2bf68f9fb7f90a71d7c641221b79801d5667b28f19e3" name = "golang.org/x/oauth2" packages = [ ".", "google", "internal", "jws", - "jwt", + "jwt" ] - pruneopts = "" revision = "3c3a985cb79f52a3190fbc056984415ca6763d01" [[projects]] - digest = "1:39d88a855976e160babdd254802e1c2ae75ed380328c39742b57928852da6207" name = "golang.org/x/sys" packages = [ "unix", - "windows", + "windows" ] - pruneopts = "" revision = "13d03a9a82fba647c21a0ef8fba44a795d0f0835" [[projects]] - digest = "1:5acd3512b047305d49e8763eef7ba423901e85d5dd2fd1e71778a0ea8de10bd4" name = "golang.org/x/text" packages = [ "collate", @@ -751,34 +615,28 @@ "unicode/bidi", "unicode/cldr", "unicode/norm", - "unicode/rangetable", + "unicode/rangetable" ] - pruneopts = "" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" [[projects]] branch = "master" - digest = "1:55a681cb66f28755765fa5fa5104cbd8dc85c55c02d206f9f89566451e3fe1aa" name = "golang.org/x/time" packages = ["rate"] - pruneopts = "" revision = "fbb02b2291d28baffd63558aa44b4b56f178d650" [[projects]] - digest = "1:2ad38d79865e33dde6157b7048debd6e7d47e0709df7b5e11bb888168e316908" name = "google.golang.org/api" packages = [ "dns/v1", "gensupport", "googleapi", - "googleapi/internal/uritemplates", + "googleapi/internal/uritemplates" ] - pruneopts = "" revision = "a0ff90edab763c86aa88f2b1eb4f3301b82f6336" [[projects]] - digest = "1:41e2b7e287117f6136f75286d48072ecf781ba54823ffeb2098e152e7dc45ef6" name = "google.golang.org/appengine" packages = [ ".", @@ -790,24 +648,20 @@ "internal/modules", "internal/remote_api", "internal/urlfetch", - "urlfetch", + "urlfetch" ] - pruneopts = "" revision = "4f7eeb5305a4ba1966344836ba4af9996b7b4e05" [[projects]] branch = "master" - digest = "1:e43f1cb3f488a0c2be85939c2a594636f60b442a12a196c778bd2d6c9aca3df7" name = "google.golang.org/genproto" packages = [ "googleapis/api/annotations", - "googleapis/rpc/status", + "googleapis/rpc/status" ] - pruneopts = "" revision = "11092d34479b07829b72e10713b159248caf5dad" [[projects]] - digest = "1:ca75b3775a5d4e5d1fb48f57ef0865b4aaa8b3f00e6b52be68db991c4594e0a7" name = "google.golang.org/grpc" packages = [ ".", @@ -836,31 +690,25 @@ "resolver/passthrough", "stats", "status", - "tap", + "tap" ] - pruneopts = "" revision = "32fb0ac620c32ba40a4626ddf94d90d12cce3455" version = "v1.14.0" [[projects]] - digest = "1:e5d1fb981765b6f7513f793a3fcaac7158408cca77f75f7311ac82cc88e9c445" name = "gopkg.in/inf.v0" packages = ["."] - pruneopts = "" revision = "3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4" version = "v0.9.0" [[projects]] - digest = "1:f0620375dd1f6251d9973b5f2596228cc8042e887cd7f827e4220bc1ce8c30e2" name = "gopkg.in/yaml.v2" packages = ["."] - pruneopts = "" revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" version = "v2.2.1" [[projects]] branch = "release-1.0" - digest = "1:bc43af6616d8ca12a7b8e806874387f0f1e181c08f547e9cd77f6cbac8cefd86" name = "istio.io/api" packages = [ "authentication/v1alpha1", @@ -868,13 +716,11 @@ "mixer/v1", "mixer/v1/config/client", "networking/v1alpha3", - "rbac/v1alpha1", + "rbac/v1alpha1" ] - pruneopts = "" revision = "76349c53b87f03f1e610b3aa3843dba3c38138d7" [[projects]] - digest = "1:7eb25280e1f610470bb0c43ab6c91573cfc78672a58542106b9b71705581429a" name = "istio.io/istio" packages = [ "pilot/pkg/config/kube/crd", @@ -883,14 +729,12 @@ "pilot/pkg/serviceregistry/kube", "pkg/cache", "pkg/kube", - "pkg/log", + "pkg/log" ] - pruneopts = "" revision = "42773aacced474d97159902d20579a25b1f98106" version = "1.0.1" [[projects]] - digest = "1:f420c8548c93242d8e5dcfa5b34e0243883b4e660f65076e869daafac877144d" name = "k8s.io/api" packages = [ "admissionregistration/v1alpha1", @@ -921,28 +765,24 @@ "settings/v1alpha1", "storage/v1", "storage/v1alpha1", - "storage/v1beta1", + "storage/v1beta1" ] - pruneopts = "" revision = "072894a440bdee3a891dea811fe42902311cd2a3" version = "kubernetes-1.11.0" [[projects]] - digest = "1:66d1421ecff35bc48ee0b11a3f891f3af6f775ed6bb1d3e0deeaba221bf42490" name = "k8s.io/apiextensions-apiserver" packages = [ "pkg/apis/apiextensions", "pkg/apis/apiextensions/v1beta1", "pkg/client/clientset/clientset", "pkg/client/clientset/clientset/scheme", - "pkg/client/clientset/clientset/typed/apiextensions/v1beta1", + "pkg/client/clientset/clientset/typed/apiextensions/v1beta1" ] - pruneopts = "" revision = "8e7f43002fec5394a8d96ebca781aa9d4b37aaef" version = "kubernetes-1.10.4" [[projects]] - digest = "1:b6b2fb7b4da1ac973b64534ace2299a02504f16bc7820cb48edb8ca4077183e1" name = "k8s.io/apimachinery" packages = [ "pkg/api/errors", @@ -985,14 +825,12 @@ "pkg/version", "pkg/watch", "third_party/forked/golang/json", - "third_party/forked/golang/reflect", + "third_party/forked/golang/reflect" ] - pruneopts = "" revision = "103fd098999dc9c0c88536f5c9ad2e5da39373ae" version = "kubernetes-1.11.0" [[projects]] - digest = "1:d04779a8de7d5465e0463bd986506348de5e89677c74777f695d3145a7a8d15e" name = "k8s.io/client-go" packages = [ "discovery", @@ -1090,92 +928,20 @@ "util/homedir", "util/integer", "util/jsonpath", - "util/retry", + "util/retry" ] - pruneopts = "" revision = "7d04d0e2a0a1a4d4a1cd6baa432a2301492e4e65" version = "v8.0.0" [[projects]] branch = "master" - digest = "1:526095379da1098c3f191a0008cc59c9bf9927492e63da7689e5de424219c162" name = "k8s.io/kube-openapi" packages = ["pkg/util/proto"] - pruneopts = "" revision = "d8ea2fe547a448256204cfc68dfee7b26c720acb" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - input-imports = [ - "cloud.google.com/go/compute/metadata", - "github.com/Azure/azure-sdk-for-go/arm/dns", - "github.com/Azure/go-autorest/autorest", - "github.com/Azure/go-autorest/autorest/adal", - "github.com/Azure/go-autorest/autorest/azure", - "github.com/Azure/go-autorest/autorest/to", - "github.com/alecthomas/kingpin", - "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests", - "github.com/aliyun/alibaba-cloud-sdk-go/services/alidns", - "github.com/aliyun/alibaba-cloud-sdk-go/services/pvtz", - "github.com/aws/aws-sdk-go/aws", - "github.com/aws/aws-sdk-go/aws/credentials/stscreds", - "github.com/aws/aws-sdk-go/aws/request", - "github.com/aws/aws-sdk-go/aws/session", - "github.com/aws/aws-sdk-go/service/route53", - "github.com/aws/aws-sdk-go/service/servicediscovery", - "github.com/cloudflare/cloudflare-go", - "github.com/coreos/etcd/clientv3", - "github.com/denverdino/aliyungo/metadata", - "github.com/digitalocean/godo", - "github.com/digitalocean/godo/context", - "github.com/dnsimple/dnsimple-go/dnsimple", - "github.com/exoscale/egoscale", - "github.com/ffledgling/pdns-go", - "github.com/gophercloud/gophercloud", - "github.com/gophercloud/gophercloud/openstack", - "github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets", - "github.com/gophercloud/gophercloud/openstack/dns/v2/zones", - "github.com/gophercloud/gophercloud/pagination", - "github.com/infobloxopen/infoblox-go-client", - "github.com/kubernetes/repo-infra/verify/boilerplate/test", - "github.com/linki/instrumented_http", - "github.com/linode/linodego", - "github.com/miekg/dns", - "github.com/nesv/go-dynect/dynect", - "github.com/oracle/oci-go-sdk/common", - "github.com/oracle/oci-go-sdk/dns", - "github.com/pkg/errors", - "github.com/prometheus/client_golang/prometheus", - "github.com/prometheus/client_golang/prometheus/promhttp", - "github.com/sirupsen/logrus", - "github.com/stretchr/testify/assert", - "github.com/stretchr/testify/mock", - "github.com/stretchr/testify/require", - "github.com/stretchr/testify/suite", - "golang.org/x/net/context", - "golang.org/x/oauth2", - "golang.org/x/oauth2/google", - "google.golang.org/api/dns/v1", - "google.golang.org/api/googleapi", - "gopkg.in/yaml.v2", - "istio.io/api/networking/v1alpha3", - "istio.io/istio/pilot/pkg/config/kube/crd", - "istio.io/istio/pilot/pkg/model", - "k8s.io/api/core/v1", - "k8s.io/api/extensions/v1beta1", - "k8s.io/apimachinery/pkg/api/errors", - "k8s.io/apimachinery/pkg/apis/meta/v1", - "k8s.io/apimachinery/pkg/labels", - "k8s.io/apimachinery/pkg/runtime", - "k8s.io/apimachinery/pkg/runtime/schema", - "k8s.io/apimachinery/pkg/runtime/serializer", - "k8s.io/client-go/kubernetes", - "k8s.io/client-go/kubernetes/fake", - "k8s.io/client-go/plugin/pkg/client/auth", - "k8s.io/client-go/rest", - "k8s.io/client-go/rest/fake", - "k8s.io/client-go/tools/clientcmd", - ] + inputs-digest = "e4a3503a3672ad47f8e8f73bc26322fd1a01e2887675a444a87fee626f42da42" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 328d11f42..88f0c65ca 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -115,3 +115,7 @@ ignored = ["github.com/kubernetes/repo-infra/kazel"] [[constraint]] name = "github.com/miekg/dns" version = "1.0.8" + +[[constraint]] + name = "github.com/google/go-cmp" + version = "0.2.0" diff --git a/plan/plan.go b/plan/plan.go index 21bf5b677..22f39b9f2 100644 --- a/plan/plan.go +++ b/plan/plan.go @@ -19,6 +19,7 @@ package plan import ( "strings" + "github.com/google/go-cmp/cmp" "github.com/kubernetes-incubator/external-dns/endpoint" ) @@ -100,7 +101,7 @@ func (t planTable) getUpdates() (updateNew []*endpoint.Endpoint, updateOld []*en 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) { + if shouldUpdateTTL(update, row.current) || targetChanged(update, row.current) || shouldUpdateProviderSpecific(update, row.current) { inheritOwner(row.current, update) updateNew = append(updateNew, update) updateOld = append(updateOld, row.current) @@ -180,6 +181,10 @@ func shouldUpdateTTL(desired, current *endpoint.Endpoint) bool { return desired.RecordTTL != current.RecordTTL } +func shouldUpdateProviderSpecific(desired, current *endpoint.Endpoint) bool { + return !cmp.Equal(desired.ProviderSpecific, current.ProviderSpecific) +} + // filterRecordsForPlan removes records that are not relevant to the planner. // Currently this just removes TXT records to prevent them from being // deleted erroneously by the planner (only the TXT registry should do this.) diff --git a/plan/plan_test.go b/plan/plan_test.go index e36b343a6..ca2b3ac1a 100644 --- a/plan/plan_test.go +++ b/plan/plan_test.go @@ -27,15 +27,17 @@ import ( type PlanTestSuite struct { suite.Suite - fooV1Cname *endpoint.Endpoint - fooV2Cname *endpoint.Endpoint - fooV2TXT *endpoint.Endpoint - fooV2CnameNoLabel *endpoint.Endpoint - fooV3CnameSameResource *endpoint.Endpoint - fooA5 *endpoint.Endpoint - bar127A *endpoint.Endpoint - bar127AWithTTL *endpoint.Endpoint - bar192A *endpoint.Endpoint + fooV1Cname *endpoint.Endpoint + fooV2Cname *endpoint.Endpoint + fooV2TXT *endpoint.Endpoint + fooV2CnameNoLabel *endpoint.Endpoint + fooV3CnameSameResource *endpoint.Endpoint + fooA5 *endpoint.Endpoint + bar127A *endpoint.Endpoint + bar127AWithTTL *endpoint.Endpoint + bar127AWithProviderSpecificTrue *endpoint.Endpoint + bar127AWithProviderSpecificFalse *endpoint.Endpoint + bar192A *endpoint.Endpoint } func (suite *PlanTestSuite) SetupTest() { @@ -100,6 +102,34 @@ func (suite *PlanTestSuite) SetupTest() { endpoint.ResourceLabelKey: "ingress/default/bar-127", }, } + suite.bar127AWithProviderSpecificTrue = &endpoint.Endpoint{ + DNSName: "bar", + Targets: endpoint.Targets{"127.0.0.1"}, + RecordType: "A", + Labels: map[string]string{ + endpoint.ResourceLabelKey: "ingress/default/bar-127", + }, + ProviderSpecific: endpoint.ProviderSpecific{ + endpoint.ProviderSpecificProperty{ + Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied", + Value: "true", + }, + }, + } + suite.bar127AWithProviderSpecificFalse = &endpoint.Endpoint{ + DNSName: "bar", + Targets: endpoint.Targets{"127.0.0.1"}, + RecordType: "A", + Labels: map[string]string{ + endpoint.ResourceLabelKey: "ingress/default/bar-127", + }, + ProviderSpecific: endpoint.ProviderSpecific{ + endpoint.ProviderSpecificProperty{ + Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied", + Value: "false", + }, + }, + } suite.bar192A = &endpoint.Endpoint{ DNSName: "bar", Targets: endpoint.Targets{"192.168.0.1"}, @@ -108,6 +138,7 @@ func (suite *PlanTestSuite) SetupTest() { endpoint.ResourceLabelKey: "ingress/default/bar-192", }, } + } func (suite *PlanTestSuite) TestSyncFirstRound() { @@ -194,6 +225,27 @@ func (suite *PlanTestSuite) TestSyncSecondRoundWithTTLChange() { validateEntries(suite.T(), changes.Delete, expectedDelete) } +func (suite *PlanTestSuite) TestSyncSecondRoundWithProviderSpecificChange() { + current := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificTrue} + desired := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificFalse} + expectedCreate := []*endpoint.Endpoint{} + expectedUpdateOld := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificTrue} + expectedUpdateNew := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificFalse} + expectedDelete := []*endpoint.Endpoint{} + + p := &Plan{ + Policies: []Policy{&SyncPolicy{}}, + Current: current, + Desired: desired, + } + + 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) TestSyncSecondRoundWithOwnerInherited() { current := []*endpoint.Endpoint{suite.fooV1Cname} desired := []*endpoint.Endpoint{suite.fooV2Cname} @@ -354,6 +406,7 @@ func (suite *PlanTestSuite) TestDuplicatedEndpointsForSameResourceReplace() { //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{} diff --git a/provider/cloudflare.go b/provider/cloudflare.go index ffb6bf81c..3594c1d5f 100644 --- a/provider/cloudflare.go +++ b/provider/cloudflare.go @@ -165,7 +165,7 @@ func (p *CloudFlareProvider) Records() ([]*endpoint.Endpoint, error) { for _, r := range records { if supportedRecordType(r.Type) { - endpoints = append(endpoints, endpoint.NewEndpointWithTTL(r.Name, r.Type, endpoint.TTL(r.TTL), r.Content)) + endpoints = append(endpoints, endpoint.NewEndpointWithTTL(r.Name, r.Type, endpoint.TTL(r.TTL), r.Content).WithProviderSpecific(source.CloudflareProxiedKey, strconv.FormatBool(r.Proxied))) } } } From 0258cf6f894406efe8037ba15b162630e5df8414 Mon Sep 17 00:00:00 2001 From: Ignasi Fosch Date: Mon, 14 Jan 2019 17:42:49 +0100 Subject: [PATCH 033/136] Make awscli commands use JSON output This way the use of `jq`, and the output in this document would make sense. --- docs/tutorials/aws.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/tutorials/aws.md b/docs/tutorials/aws.md index 5ab4050de..e8c25254a 100644 --- a/docs/tutorials/aws.md +++ b/docs/tutorials/aws.md @@ -47,14 +47,14 @@ $ aws route53 create-hosted-zone --name "external-dns-test.my-org.com." --caller Make a note of the ID of the hosted zone you just created. ```console -$ aws route53 list-hosted-zones-by-name --dns-name "external-dns-test.my-org.com." | jq -r '.HostedZones[0].Id' +$ aws route53 list-hosted-zones-by-name --output json --dns-name "external-dns-test.my-org.com." | jq -r '.HostedZones[0].Id' /hostedzone/ZEWFWZ4R16P7IB ``` Make a note of the nameservers that were assigned to your new zone. ```console -$ aws route53 list-resource-record-sets --hosted-zone-id "/hostedzone/ZEWFWZ4R16P7IB" \ +$ aws route53 list-resource-record-sets --output json --hosted-zone-id "/hostedzone/ZEWFWZ4R16P7IB" \ --query "ResourceRecordSets[?Type == 'NS']" | jq -r '.[0].ResourceRecords[].Value' ns-5514.awsdns-53.org. ... @@ -247,7 +247,7 @@ spec: After roughly two minutes check that a corresponding DNS record for your service was created. ```console -$ aws route53 list-resource-record-sets --hosted-zone-id "/hostedzone/ZEWFWZ4R16P7IB" \ +$ aws route53 list-resource-record-sets --output json --hosted-zone-id "/hostedzone/ZEWFWZ4R16P7IB" \ --query "ResourceRecordSets[?Name == 'nginx.external-dns-test.my-org.com.']|[?Type == 'A']" [ { From 370bae6dd38d39f1473535d791b185842c96df6f Mon Sep 17 00:00:00 2001 From: njuettner Date: Wed, 16 Jan 2019 15:55:28 +0100 Subject: [PATCH 034/136] Cloudflare pagination for zones --- Gopkg.lock | 6 +-- Gopkg.toml | 2 +- main.go | 2 +- pkg/apis/externaldns/types.go | 3 ++ pkg/apis/externaldns/types_test.go | 4 ++ provider/cloudflare.go | 43 +++++++++++++-------- provider/cloudflare_test.go | 61 +++++++++++++++++++++++++++--- 7 files changed, 95 insertions(+), 26 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index d5b1ec160..470fba59c 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -128,12 +128,12 @@ revision = "4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9" [[projects]] - digest = "1:85fd00554a6ed5b33687684b76635d532c74141508b5bce2843d85e8a3c9dc91" + branch = "master" + digest = "1:3e90c0d9954bf53a2061c4a0f193e6569c9ab2118c8f3a250871498f6b4645e5" name = "github.com/cloudflare/cloudflare-go" packages = ["."] pruneopts = "" - revision = "4c6994ac3877fbb627766edadc67f4e816e8c890" - version = "v0.7.4" + revision = "0c85496d873009e53e64d391ade643e7ac02e0d4" [[projects]] digest = "1:eaeede87b418b97f9dee473f8940fd9b65ca5cdac0503350c7c8f8965ea3cf4d" diff --git a/Gopkg.toml b/Gopkg.toml index 17cc1e3cc..6590be2e0 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -21,8 +21,8 @@ ignored = ["github.com/kubernetes/repo-infra/kazel"] version = "~1.13.7" [[constraint]] + branch = "master" name = "github.com/cloudflare/cloudflare-go" - version = "0.7.3" [[constraint]] name = "github.com/digitalocean/godo" diff --git a/main.go b/main.go index 56d4e5777..7c95bf790 100644 --- a/main.go +++ b/main.go @@ -129,7 +129,7 @@ func main() { case "azure": p, err = provider.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.DryRun) case "cloudflare": - p, err = provider.NewCloudFlareProvider(domainFilter, zoneIDFilter, cfg.CloudflareProxied, cfg.DryRun) + p, err = provider.NewCloudFlareProvider(domainFilter, zoneIDFilter, cfg.CloudflareZonesPerPage, cfg.CloudflareProxied, cfg.DryRun) case "google": p, err = provider.NewGoogleProvider(cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.DryRun) case "digitalocean": diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 5de4aca5c..6d079e054 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -64,6 +64,7 @@ type Config struct { AzureConfigFile string AzureResourceGroup string CloudflareProxied bool + CloudflareZonesPerPage int InfobloxGridHost string InfobloxWapiPort int InfobloxWapiUsername string @@ -136,6 +137,7 @@ var defaultConfig = &Config{ AzureConfigFile: "/etc/kubernetes/azure.json", AzureResourceGroup: "", CloudflareProxied: false, + CloudflareZonesPerPage: 50, InfobloxGridHost: "", InfobloxWapiPort: 443, InfobloxWapiUsername: "admin", @@ -251,6 +253,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("azure-config-file", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure").Default(defaultConfig.AzureConfigFile).StringVar(&cfg.AzureConfigFile) app.Flag("azure-resource-group", "When using the Azure provider, override the Azure resource group to use (optional)").Default(defaultConfig.AzureResourceGroup).StringVar(&cfg.AzureResourceGroup) app.Flag("cloudflare-proxied", "When using the Cloudflare provider, specify if the proxy mode must be enabled (default: disabled)").BoolVar(&cfg.CloudflareProxied) + app.Flag("cloudflare-zones-per-page", "When using the Cloudflare provider, specify how many zones per page listed, max. possible 50 (default: 50)").Default(strconv.Itoa(defaultConfig.CloudflareZonesPerPage)).IntVar(&cfg.CloudflareZonesPerPage) app.Flag("infoblox-grid-host", "When using the Infoblox provider, specify the Grid Manager host (required when --provider=infoblox)").Default(defaultConfig.InfobloxGridHost).StringVar(&cfg.InfobloxGridHost) app.Flag("infoblox-wapi-port", "When using the Infoblox provider, specify the WAPI port (default: 443)").Default(strconv.Itoa(defaultConfig.InfobloxWapiPort)).IntVar(&cfg.InfobloxWapiPort) app.Flag("infoblox-wapi-username", "When using the Infoblox provider, specify the WAPI username (default: admin)").Default(defaultConfig.InfobloxWapiUsername).StringVar(&cfg.InfobloxWapiUsername) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index 8a1f1854f..0d5f6f30e 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -51,6 +51,7 @@ var ( AzureConfigFile: "/etc/kubernetes/azure.json", AzureResourceGroup: "", CloudflareProxied: false, + CloudflareZonesPerPage: 50, InfobloxGridHost: "", InfobloxWapiPort: 443, InfobloxWapiUsername: "admin", @@ -103,6 +104,7 @@ var ( AzureConfigFile: "azure.json", AzureResourceGroup: "arg", CloudflareProxied: true, + CloudflareZonesPerPage: 20, InfobloxGridHost: "127.0.0.1", InfobloxWapiPort: 8443, InfobloxWapiUsername: "infoblox", @@ -171,6 +173,7 @@ func TestParseFlags(t *testing.T) { "--azure-config-file=azure.json", "--azure-resource-group=arg", "--cloudflare-proxied", + "--cloudflare-zones-per-page=20", "--infoblox-grid-host=127.0.0.1", "--infoblox-wapi-port=8443", "--infoblox-wapi-username=infoblox", @@ -234,6 +237,7 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_AZURE_CONFIG_FILE": "azure.json", "EXTERNAL_DNS_AZURE_RESOURCE_GROUP": "arg", "EXTERNAL_DNS_CLOUDFLARE_PROXIED": "1", + "EXTERNAL_DNS_CLOUDFLARE_ZONES_PER_PAGE": "20", "EXTERNAL_DNS_INFOBLOX_GRID_HOST": "127.0.0.1", "EXTERNAL_DNS_INFOBLOX_WAPI_PORT": "8443", "EXTERNAL_DNS_INFOBLOX_WAPI_USERNAME": "infoblox", diff --git a/provider/cloudflare.go b/provider/cloudflare.go index 75e24f414..73bc4b9ed 100644 --- a/provider/cloudflare.go +++ b/provider/cloudflare.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "fmt" "os" "strings" @@ -53,6 +54,7 @@ type cloudFlareDNS interface { UserDetails() (cloudflare.User, error) ZoneIDByName(zoneName string) (string, error) ListZones(zoneID ...string) ([]cloudflare.Zone, error) + ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) DNSRecords(zoneID string, rr cloudflare.DNSRecord) ([]cloudflare.DNSRecord, error) CreateDNSRecord(zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) DeleteDNSRecord(zoneID, recordID string) error @@ -89,14 +91,19 @@ func (z zoneService) DeleteDNSRecord(zoneID, recordID string) error { return z.service.DeleteDNSRecord(zoneID, recordID) } +func (z zoneService) ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) { + return z.service.ListZonesContext(ctx, opts...) +} + // CloudFlareProvider is an implementation of Provider for CloudFlare DNS. type CloudFlareProvider struct { Client cloudFlareDNS // only consider hosted zones managing domains ending in this suffix - domainFilter DomainFilter - zoneIDFilter ZoneIDFilter - proxied bool - DryRun bool + domainFilter DomainFilter + zoneIDFilter ZoneIDFilter + proxied bool + DryRun bool + PaginationOptions cloudflare.PaginationOptions } // cloudFlareChange differentiates between ChangActions @@ -106,7 +113,7 @@ type cloudFlareChange struct { } // NewCloudFlareProvider initializes a new CloudFlare DNS based Provider. -func NewCloudFlareProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, proxied bool, dryRun bool) (*CloudFlareProvider, error) { +func NewCloudFlareProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, zonesPerPage int, proxied bool, dryRun bool) (*CloudFlareProvider, error) { // initialize via API email and API key and returns new API object config, err := cloudflare.New(os.Getenv("CF_API_KEY"), os.Getenv("CF_API_EMAIL")) if err != nil { @@ -119,6 +126,9 @@ func NewCloudFlareProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, zoneIDFilter: zoneIDFilter, proxied: proxied, DryRun: dryRun, + PaginationOptions: cloudflare.PaginationOptions{ + PerPage: zonesPerPage, + }, } return provider, nil } @@ -126,22 +136,23 @@ func NewCloudFlareProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, // Zones returns the list of hosted zones. func (p *CloudFlareProvider) Zones() ([]cloudflare.Zone, error) { result := []cloudflare.Zone{} - - zones, err := p.Client.ListZones() + ctx := context.TODO() + zonesResponse, err := p.Client.ListZonesContext(ctx, cloudflare.WithPagination(p.PaginationOptions)) if err != nil { return nil, err } + for pages := 0; pages < zonesResponse.ResultInfo.TotalPages; pages++ { + for _, zone := range zonesResponse.Result { + if !p.domainFilter.Match(zone.Name) { + continue + } - for _, zone := range zones { - if !p.domainFilter.Match(zone.Name) { - continue + if !p.zoneIDFilter.Match(zone.ID) { + continue + } + + result = append(result, zone) } - - if !p.zoneIDFilter.Match(zone.ID) { - continue - } - - result = append(result, zone) } return result, nil diff --git a/provider/cloudflare_test.go b/provider/cloudflare_test.go index cfa54ca21..6c1dfc34c 100644 --- a/provider/cloudflare_test.go +++ b/provider/cloudflare_test.go @@ -17,15 +17,14 @@ limitations under the License. package provider import ( + "context" "fmt" "os" "testing" + cloudflare "github.com/cloudflare/cloudflare-go" "github.com/kubernetes-incubator/external-dns/endpoint" "github.com/kubernetes-incubator/external-dns/plan" - - cloudflare "github.com/cloudflare/cloudflare-go" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -66,6 +65,17 @@ func (m *mockCloudFlareClient) ListZones(zoneID ...string) ([]cloudflare.Zone, e return []cloudflare.Zone{{ID: "1234567890", Name: "ext-dns-test.zalando.to."}, {ID: "1234567891", Name: "foo.com."}}, nil } +func (m *mockCloudFlareClient) ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) { + return cloudflare.ZonesResponse{ + Result: []cloudflare.Zone{ + {ID: "1234567890", Name: "ext-dns-test.zalando.to."}, + {ID: "1234567891", Name: "foo.com."}}, + ResultInfo: cloudflare.ResultInfo{ + TotalPages: 1, + }, + }, nil +} + type mockCloudFlareUserDetailsFail struct{} func (m *mockCloudFlareUserDetailsFail) CreateDNSRecord(zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) { @@ -96,6 +106,10 @@ func (m *mockCloudFlareUserDetailsFail) ListZones(zoneID ...string) ([]cloudflar return []cloudflare.Zone{{Name: "ext-dns-test.zalando.to."}}, nil } +func (m *mockCloudFlareUserDetailsFail) ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) { + return cloudflare.ZonesResponse{}, nil +} + type mockCloudFlareCreateZoneFail struct{} func (m *mockCloudFlareCreateZoneFail) CreateDNSRecord(zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) { @@ -155,6 +169,17 @@ func (m *mockCloudFlareDNSRecordsFail) ListZones(zoneID ...string) ([]cloudflare return []cloudflare.Zone{{Name: "ext-dns-test.zalando.to."}}, nil } +func (m *mockCloudFlareDNSRecordsFail) ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) { + return cloudflare.ZonesResponse{ + Result: []cloudflare.Zone{ + {ID: "1234567890", Name: "ext-dns-test.zalando.to."}, + {ID: "1234567891", Name: "foo.com."}}, + ResultInfo: cloudflare.ResultInfo{ + TotalPages: 1, + }, + }, nil +} + type mockCloudFlareZoneIDByNameFail struct{} func (m *mockCloudFlareZoneIDByNameFail) CreateDNSRecord(zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) { @@ -245,6 +270,10 @@ func (m *mockCloudFlareListZonesFail) ListZones(zoneID ...string) ([]cloudflare. return []cloudflare.Zone{{}}, fmt.Errorf("no zones available") } +func (m *mockCloudFlareListZonesFail) ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) { + return cloudflare.ZonesResponse{}, fmt.Errorf("no zones available") +} + type mockCloudFlareCreateRecordsFail struct{} func (m *mockCloudFlareCreateRecordsFail) CreateDNSRecord(zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) { @@ -275,6 +304,10 @@ func (m *mockCloudFlareCreateRecordsFail) ListZones(zoneID ...string) ([]cloudfl return []cloudflare.Zone{{}}, fmt.Errorf("no zones available") } +func (m *mockCloudFlareCreateRecordsFail) ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) { + return cloudflare.ZonesResponse{}, nil +} + type mockCloudFlareDeleteRecordsFail struct{} func (m *mockCloudFlareDeleteRecordsFail) CreateDNSRecord(zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) { @@ -305,6 +338,10 @@ func (m *mockCloudFlareDeleteRecordsFail) ListZones(zoneID ...string) ([]cloudfl return []cloudflare.Zone{{Name: "ext-dns-test.zalando.to."}}, nil } +func (m *mockCloudFlareDeleteRecordsFail) ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) { + return cloudflare.ZonesResponse{}, nil +} + type mockCloudFlareUpdateRecordsFail struct{} func (m *mockCloudFlareUpdateRecordsFail) CreateDNSRecord(zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) { @@ -335,6 +372,10 @@ func (m *mockCloudFlareUpdateRecordsFail) ListZones(zoneID ...string) ([]cloudfl return []cloudflare.Zone{{Name: "ext-dns-test.zalando.to."}}, nil } +func (m *mockCloudFlareUpdateRecordsFail) ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) { + return cloudflare.ZonesResponse{}, nil +} + func TestNewCloudFlareChanges(t *testing.T) { expect := []struct { Name string @@ -439,13 +480,23 @@ func TestRecords(t *testing.T) { func TestNewCloudFlareProvider(t *testing.T) { _ = os.Setenv("CF_API_KEY", "xxxxxxxxxxxxxxxxx") _ = os.Setenv("CF_API_EMAIL", "test@test.com") - _, err := NewCloudFlareProvider(NewDomainFilter([]string{"ext-dns-test.zalando.to."}), NewZoneIDFilter([]string{""}), false, true) + _, err := NewCloudFlareProvider( + NewDomainFilter([]string{"ext-dns-test.zalando.to."}), + NewZoneIDFilter([]string{""}), + 1, + false, + true) if err != nil { t.Errorf("should not fail, %s", err) } _ = os.Unsetenv("CF_API_KEY") _ = os.Unsetenv("CF_API_EMAIL") - _, err = NewCloudFlareProvider(NewDomainFilter([]string{"ext-dns-test.zalando.to."}), NewZoneIDFilter([]string{""}), false, true) + _, err = NewCloudFlareProvider( + NewDomainFilter([]string{"ext-dns-test.zalando.to."}), + NewZoneIDFilter([]string{""}), + 50, + false, + true) if err == nil { t.Errorf("expected to fail") } From 7a28e3047a1794feac7087e0016ca6b20ae23e6d Mon Sep 17 00:00:00 2001 From: Joe Hohertz Date: Thu, 17 Jan 2019 12:37:27 -0500 Subject: [PATCH 035/136] Adds a new flag `--aws-api-retries` which allows overriding the number of retries that API calls will attempt before giving up. This somewhat mitigates the issues discussed in #484 by allowing the current sync attempt to complete vs. failing and starting anew. Defaults to 3, which is what the aws-sdk-go defaults to where not specified. Signed-off-by: Joe Hohertz --- main.go | 1 + pkg/apis/externaldns/types.go | 3 +++ pkg/apis/externaldns/types_test.go | 4 ++++ provider/aws.go | 3 ++- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 56d4e5777..7bc1f0d4a 100644 --- a/main.go +++ b/main.go @@ -116,6 +116,7 @@ func main() { BatchChangeInterval: cfg.AWSBatchChangeInterval, EvaluateTargetHealth: cfg.AWSEvaluateTargetHealth, AssumeRole: cfg.AWSAssumeRole, + APIRetries: cfg.AWSAPIRetries, DryRun: cfg.DryRun, }, ) diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 5de4aca5c..8f51cee03 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -61,6 +61,7 @@ type Config struct { AWSBatchChangeSize int AWSBatchChangeInterval time.Duration AWSEvaluateTargetHealth bool + AWSAPIRetries int AzureConfigFile string AzureResourceGroup string CloudflareProxied bool @@ -133,6 +134,7 @@ var defaultConfig = &Config{ AWSBatchChangeSize: 1000, AWSBatchChangeInterval: time.Second, AWSEvaluateTargetHealth: true, + AWSAPIRetries: 3, AzureConfigFile: "/etc/kubernetes/azure.json", AzureResourceGroup: "", CloudflareProxied: false, @@ -248,6 +250,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("aws-batch-change-size", "When using the AWS provider, set the maximum number of changes that will be applied in each batch.").Default(strconv.Itoa(defaultConfig.AWSBatchChangeSize)).IntVar(&cfg.AWSBatchChangeSize) app.Flag("aws-batch-change-interval", "When using the AWS provider, set the interval between batch changes.").Default(defaultConfig.AWSBatchChangeInterval.String()).DurationVar(&cfg.AWSBatchChangeInterval) app.Flag("aws-evaluate-target-health", "When using the AWS provider, set whether to evaluate the health of a DNS target (default: enabled, disable with --no-aws-evaluate-target-health)").Default(strconv.FormatBool(defaultConfig.AWSEvaluateTargetHealth)).BoolVar(&cfg.AWSEvaluateTargetHealth) + app.Flag("aws-api-retries", "When using the AWS provider, set the maximum number of retries for API calls before giving up.").Default(strconv.Itoa(defaultConfig.AWSAPIRetries)).IntVar(&cfg.AWSAPIRetries) app.Flag("azure-config-file", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure").Default(defaultConfig.AzureConfigFile).StringVar(&cfg.AzureConfigFile) app.Flag("azure-resource-group", "When using the Azure provider, override the Azure resource group to use (optional)").Default(defaultConfig.AzureResourceGroup).StringVar(&cfg.AzureResourceGroup) app.Flag("cloudflare-proxied", "When using the Cloudflare provider, specify if the proxy mode must be enabled (default: disabled)").BoolVar(&cfg.CloudflareProxied) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index 8a1f1854f..7d04abadb 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -48,6 +48,7 @@ var ( AWSBatchChangeSize: 1000, AWSBatchChangeInterval: time.Second, AWSEvaluateTargetHealth: true, + AWSAPIRetries: 3, AzureConfigFile: "/etc/kubernetes/azure.json", AzureResourceGroup: "", CloudflareProxied: false, @@ -100,6 +101,7 @@ var ( AWSBatchChangeSize: 100, AWSBatchChangeInterval: time.Second * 2, AWSEvaluateTargetHealth: false, + AWSAPIRetries: 13, AzureConfigFile: "azure.json", AzureResourceGroup: "arg", CloudflareProxied: true, @@ -195,6 +197,7 @@ func TestParseFlags(t *testing.T) { "--aws-assume-role=some-other-role", "--aws-batch-change-size=100", "--aws-batch-change-interval=2s", + "--aws-api-retries=13", "--no-aws-evaluate-target-health", "--policy=upsert-only", "--registry=noop", @@ -256,6 +259,7 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_AWS_BATCH_CHANGE_SIZE": "100", "EXTERNAL_DNS_AWS_BATCH_CHANGE_INTERVAL": "2s", "EXTERNAL_DNS_AWS_EVALUATE_TARGET_HEALTH": "0", + "EXTERNAL_DNS_AWS_API_RETRIES": "13", "EXTERNAL_DNS_POLICY": "upsert-only", "EXTERNAL_DNS_REGISTRY": "noop", "EXTERNAL_DNS_TXT_OWNER_ID": "owner-1", diff --git a/provider/aws.go b/provider/aws.go index fb328cbd2..3d5c57409 100644 --- a/provider/aws.go +++ b/provider/aws.go @@ -117,12 +117,13 @@ type AWSConfig struct { BatchChangeInterval time.Duration EvaluateTargetHealth bool AssumeRole string + APIRetries int DryRun bool } // NewAWSProvider initializes a new AWS Route53 based Provider. func NewAWSProvider(awsConfig AWSConfig) (*AWSProvider, error) { - config := aws.NewConfig() + config := aws.NewConfig().WithMaxRetries(awsConfig.APIRetries) config.WithHTTPClient( instrumented_http.NewClient(config.HTTPClient, &instrumented_http.Callbacks{ From 7c8cc028566d34d9c25e7b969a4e6f246f698ca7 Mon Sep 17 00:00:00 2001 From: Anand Patel Date: Fri, 18 Jan 2019 11:27:51 -0500 Subject: [PATCH 036/136] fix gofmt issue --- pkg/apis/externaldns/types_test.go | 108 ++++++++++++++--------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index 146a0e594..413c85093 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -82,61 +82,61 @@ var ( } overriddenConfig = &Config{ - Master: "http://127.0.0.1:8080", - KubeConfig: "/some/path", - RequestTimeout: time.Second * 77, - IstioIngressGateway: "istio-other/istio-otheringressgateway", - Sources: []string{"service", "ingress", "connector"}, - Namespace: "namespace", - FQDNTemplate: "{{.Name}}.service.example.com", + Master: "http://127.0.0.1:8080", + KubeConfig: "/some/path", + RequestTimeout: time.Second * 77, + IstioIngressGateway: "istio-other/istio-otheringressgateway", + Sources: []string{"service", "ingress", "connector"}, + Namespace: "namespace", + FQDNTemplate: "{{.Name}}.service.example.com", IgnoreHostnameAnnotation: true, - Compatibility: "mate", - Provider: "google", - GoogleProject: "project", - DomainFilter: []string{"example.org", "company.com"}, - ZoneIDFilter: []string{"/hostedzone/ZTST1", "/hostedzone/ZTST2"}, - AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json", - AWSZoneType: "private", - AWSZoneTagFilter: []string{"tag=foo"}, - AWSAssumeRole: "some-other-role", - AWSBatchChangeSize: 100, - AWSBatchChangeInterval: time.Second * 2, - AWSEvaluateTargetHealth: false, - AWSAPIRetries: 13, - AzureConfigFile: "azure.json", - AzureResourceGroup: "arg", - CloudflareProxied: true, - InfobloxGridHost: "127.0.0.1", - InfobloxWapiPort: 8443, - InfobloxWapiUsername: "infoblox", - InfobloxWapiPassword: "infoblox", - InfobloxWapiVersion: "2.6.1", - InfobloxSSLVerify: false, - OCIConfigFile: "oci.yaml", - InMemoryZones: []string{"example.org", "company.com"}, - PDNSServer: "http://ns.example.com:8081", - PDNSAPIKey: "some-secret-key", - PDNSTLSEnabled: true, - TLSCA: "/path/to/ca.crt", - TLSClientCert: "/path/to/cert.pem", - TLSClientCertKey: "/path/to/key.pem", - Policy: "upsert-only", - Registry: "noop", - TXTOwnerID: "owner-1", - TXTPrefix: "associated-txt-record", - TXTCacheInterval: 12 * time.Hour, - Interval: 10 * time.Minute, - Once: true, - DryRun: true, - LogFormat: "json", - MetricsAddress: "127.0.0.1:9099", - LogLevel: logrus.DebugLevel.String(), - ConnectorSourceServer: "localhost:8081", - ExoscaleEndpoint: "https://api.foo.ch/dns", - ExoscaleAPIKey: "1", - ExoscaleAPISecret: "2", - CRDSourceAPIVersion: "test.k8s.io/v1alpha1", - CRDSourceKind: "Endpoint", + Compatibility: "mate", + Provider: "google", + GoogleProject: "project", + DomainFilter: []string{"example.org", "company.com"}, + ZoneIDFilter: []string{"/hostedzone/ZTST1", "/hostedzone/ZTST2"}, + AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json", + AWSZoneType: "private", + AWSZoneTagFilter: []string{"tag=foo"}, + AWSAssumeRole: "some-other-role", + AWSBatchChangeSize: 100, + AWSBatchChangeInterval: time.Second * 2, + AWSEvaluateTargetHealth: false, + AWSAPIRetries: 13, + AzureConfigFile: "azure.json", + AzureResourceGroup: "arg", + CloudflareProxied: true, + InfobloxGridHost: "127.0.0.1", + InfobloxWapiPort: 8443, + InfobloxWapiUsername: "infoblox", + InfobloxWapiPassword: "infoblox", + InfobloxWapiVersion: "2.6.1", + InfobloxSSLVerify: false, + OCIConfigFile: "oci.yaml", + InMemoryZones: []string{"example.org", "company.com"}, + PDNSServer: "http://ns.example.com:8081", + PDNSAPIKey: "some-secret-key", + PDNSTLSEnabled: true, + TLSCA: "/path/to/ca.crt", + TLSClientCert: "/path/to/cert.pem", + TLSClientCertKey: "/path/to/key.pem", + Policy: "upsert-only", + Registry: "noop", + TXTOwnerID: "owner-1", + TXTPrefix: "associated-txt-record", + TXTCacheInterval: 12 * time.Hour, + Interval: 10 * time.Minute, + Once: true, + DryRun: true, + LogFormat: "json", + MetricsAddress: "127.0.0.1:9099", + LogLevel: logrus.DebugLevel.String(), + ConnectorSourceServer: "localhost:8081", + ExoscaleEndpoint: "https://api.foo.ch/dns", + ExoscaleAPIKey: "1", + ExoscaleAPISecret: "2", + CRDSourceAPIVersion: "test.k8s.io/v1alpha1", + CRDSourceKind: "Endpoint", } ) From 67df440b360b8987fccba661394c7dbb90f8f16a Mon Sep 17 00:00:00 2001 From: Martin Linkhorst Date: Wed, 23 Jan 2019 14:52:26 +0100 Subject: [PATCH 037/136] Add questions from slack to the faq --- docs/faq.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/faq.md b/docs/faq.md index 447cf561d..bcd2240f0 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -243,3 +243,11 @@ To do this with ExternalDNS you can use the `--annotation-filter` to specificall 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=nginx-internal` and one with `--annotation-filter=kubernetes.io/ingress.class=nginx-external`. + +### Can external-dns manage(add/remove) records in a hosted zone which is setup in different aws account. + +yes, give it the correct cross-account/assume-role permissions and use the `--aws-assume-role` flag https://github.com/kubernetes-incubator/external-dns/pull/524#issue-181256561 + +### how do I provide multiple values to the annotation `external-dns.alpha.kubernetes.io/hostname` + +separate them by `,` From 18b343efdd815fec066e6dffceba28f4acb401a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nick=20J=C3=BCttner?= Date: Thu, 24 Jan 2019 11:38:47 +0100 Subject: [PATCH 038/136] Update Gopkg.toml --- Gopkg.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gopkg.toml b/Gopkg.toml index c4112b411..4e99abd9e 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -120,6 +120,6 @@ ignored = ["github.com/kubernetes/repo-infra/kazel"] name = "github.com/google/go-cmp" version = "0.2.0" -[constraint]] +[[constraint]] name = "github.com/sanyu/dynectsoap" - branch = "master" \ No newline at end of file + branch = "master" From a8b127ad237d6fa6d5b84cd037d69d510ba6815c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nick=20J=C3=BCttner?= Date: Thu, 24 Jan 2019 11:48:42 +0100 Subject: [PATCH 039/136] Update Gopkg.toml From 7fb022ca138cb5fb8dde4b42268f4c3d8b31c6e1 Mon Sep 17 00:00:00 2001 From: njuettner Date: Wed, 16 Jan 2019 15:55:28 +0100 Subject: [PATCH 040/136] Cloudflare pagination for zones --- Gopkg.lock | 6 +-- Gopkg.toml | 2 +- main.go | 2 +- pkg/apis/externaldns/types.go | 3 ++ pkg/apis/externaldns/types_test.go | 4 ++ provider/cloudflare.go | 50 ++++++++++++++++-------- provider/cloudflare_test.go | 62 +++++++++++++++++++++++++++--- 7 files changed, 104 insertions(+), 25 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index d5b1ec160..470fba59c 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -128,12 +128,12 @@ revision = "4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9" [[projects]] - digest = "1:85fd00554a6ed5b33687684b76635d532c74141508b5bce2843d85e8a3c9dc91" + branch = "master" + digest = "1:3e90c0d9954bf53a2061c4a0f193e6569c9ab2118c8f3a250871498f6b4645e5" name = "github.com/cloudflare/cloudflare-go" packages = ["."] pruneopts = "" - revision = "4c6994ac3877fbb627766edadc67f4e816e8c890" - version = "v0.7.4" + revision = "0c85496d873009e53e64d391ade643e7ac02e0d4" [[projects]] digest = "1:eaeede87b418b97f9dee473f8940fd9b65ca5cdac0503350c7c8f8965ea3cf4d" diff --git a/Gopkg.toml b/Gopkg.toml index 17cc1e3cc..6590be2e0 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -21,8 +21,8 @@ ignored = ["github.com/kubernetes/repo-infra/kazel"] version = "~1.13.7" [[constraint]] + branch = "master" name = "github.com/cloudflare/cloudflare-go" - version = "0.7.3" [[constraint]] name = "github.com/digitalocean/godo" diff --git a/main.go b/main.go index 56d4e5777..7c95bf790 100644 --- a/main.go +++ b/main.go @@ -129,7 +129,7 @@ func main() { case "azure": p, err = provider.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.DryRun) case "cloudflare": - p, err = provider.NewCloudFlareProvider(domainFilter, zoneIDFilter, cfg.CloudflareProxied, cfg.DryRun) + p, err = provider.NewCloudFlareProvider(domainFilter, zoneIDFilter, cfg.CloudflareZonesPerPage, cfg.CloudflareProxied, cfg.DryRun) case "google": p, err = provider.NewGoogleProvider(cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.DryRun) case "digitalocean": diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 5de4aca5c..6d079e054 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -64,6 +64,7 @@ type Config struct { AzureConfigFile string AzureResourceGroup string CloudflareProxied bool + CloudflareZonesPerPage int InfobloxGridHost string InfobloxWapiPort int InfobloxWapiUsername string @@ -136,6 +137,7 @@ var defaultConfig = &Config{ AzureConfigFile: "/etc/kubernetes/azure.json", AzureResourceGroup: "", CloudflareProxied: false, + CloudflareZonesPerPage: 50, InfobloxGridHost: "", InfobloxWapiPort: 443, InfobloxWapiUsername: "admin", @@ -251,6 +253,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("azure-config-file", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure").Default(defaultConfig.AzureConfigFile).StringVar(&cfg.AzureConfigFile) app.Flag("azure-resource-group", "When using the Azure provider, override the Azure resource group to use (optional)").Default(defaultConfig.AzureResourceGroup).StringVar(&cfg.AzureResourceGroup) app.Flag("cloudflare-proxied", "When using the Cloudflare provider, specify if the proxy mode must be enabled (default: disabled)").BoolVar(&cfg.CloudflareProxied) + app.Flag("cloudflare-zones-per-page", "When using the Cloudflare provider, specify how many zones per page listed, max. possible 50 (default: 50)").Default(strconv.Itoa(defaultConfig.CloudflareZonesPerPage)).IntVar(&cfg.CloudflareZonesPerPage) app.Flag("infoblox-grid-host", "When using the Infoblox provider, specify the Grid Manager host (required when --provider=infoblox)").Default(defaultConfig.InfobloxGridHost).StringVar(&cfg.InfobloxGridHost) app.Flag("infoblox-wapi-port", "When using the Infoblox provider, specify the WAPI port (default: 443)").Default(strconv.Itoa(defaultConfig.InfobloxWapiPort)).IntVar(&cfg.InfobloxWapiPort) app.Flag("infoblox-wapi-username", "When using the Infoblox provider, specify the WAPI username (default: admin)").Default(defaultConfig.InfobloxWapiUsername).StringVar(&cfg.InfobloxWapiUsername) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index 8a1f1854f..0d5f6f30e 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -51,6 +51,7 @@ var ( AzureConfigFile: "/etc/kubernetes/azure.json", AzureResourceGroup: "", CloudflareProxied: false, + CloudflareZonesPerPage: 50, InfobloxGridHost: "", InfobloxWapiPort: 443, InfobloxWapiUsername: "admin", @@ -103,6 +104,7 @@ var ( AzureConfigFile: "azure.json", AzureResourceGroup: "arg", CloudflareProxied: true, + CloudflareZonesPerPage: 20, InfobloxGridHost: "127.0.0.1", InfobloxWapiPort: 8443, InfobloxWapiUsername: "infoblox", @@ -171,6 +173,7 @@ func TestParseFlags(t *testing.T) { "--azure-config-file=azure.json", "--azure-resource-group=arg", "--cloudflare-proxied", + "--cloudflare-zones-per-page=20", "--infoblox-grid-host=127.0.0.1", "--infoblox-wapi-port=8443", "--infoblox-wapi-username=infoblox", @@ -234,6 +237,7 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_AZURE_CONFIG_FILE": "azure.json", "EXTERNAL_DNS_AZURE_RESOURCE_GROUP": "arg", "EXTERNAL_DNS_CLOUDFLARE_PROXIED": "1", + "EXTERNAL_DNS_CLOUDFLARE_ZONES_PER_PAGE": "20", "EXTERNAL_DNS_INFOBLOX_GRID_HOST": "127.0.0.1", "EXTERNAL_DNS_INFOBLOX_WAPI_PORT": "8443", "EXTERNAL_DNS_INFOBLOX_WAPI_USERNAME": "infoblox", diff --git a/provider/cloudflare.go b/provider/cloudflare.go index 75e24f414..b5751dbe7 100644 --- a/provider/cloudflare.go +++ b/provider/cloudflare.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "fmt" "os" "strings" @@ -53,6 +54,7 @@ type cloudFlareDNS interface { UserDetails() (cloudflare.User, error) ZoneIDByName(zoneName string) (string, error) ListZones(zoneID ...string) ([]cloudflare.Zone, error) + ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) DNSRecords(zoneID string, rr cloudflare.DNSRecord) ([]cloudflare.DNSRecord, error) CreateDNSRecord(zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) DeleteDNSRecord(zoneID, recordID string) error @@ -89,14 +91,19 @@ func (z zoneService) DeleteDNSRecord(zoneID, recordID string) error { return z.service.DeleteDNSRecord(zoneID, recordID) } +func (z zoneService) ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) { + return z.service.ListZonesContext(ctx, opts...) +} + // CloudFlareProvider is an implementation of Provider for CloudFlare DNS. type CloudFlareProvider struct { Client cloudFlareDNS // only consider hosted zones managing domains ending in this suffix - domainFilter DomainFilter - zoneIDFilter ZoneIDFilter - proxied bool - DryRun bool + domainFilter DomainFilter + zoneIDFilter ZoneIDFilter + proxied bool + DryRun bool + PaginationOptions cloudflare.PaginationOptions } // cloudFlareChange differentiates between ChangActions @@ -106,7 +113,7 @@ type cloudFlareChange struct { } // NewCloudFlareProvider initializes a new CloudFlare DNS based Provider. -func NewCloudFlareProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, proxied bool, dryRun bool) (*CloudFlareProvider, error) { +func NewCloudFlareProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, zonesPerPage int, proxied bool, dryRun bool) (*CloudFlareProvider, error) { // initialize via API email and API key and returns new API object config, err := cloudflare.New(os.Getenv("CF_API_KEY"), os.Getenv("CF_API_EMAIL")) if err != nil { @@ -119,6 +126,10 @@ func NewCloudFlareProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, zoneIDFilter: zoneIDFilter, proxied: proxied, DryRun: dryRun, + PaginationOptions: cloudflare.PaginationOptions{ + PerPage: zonesPerPage, + Page: 1, + }, } return provider, nil } @@ -126,22 +137,31 @@ func NewCloudFlareProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, // Zones returns the list of hosted zones. func (p *CloudFlareProvider) Zones() ([]cloudflare.Zone, error) { result := []cloudflare.Zone{} - - zones, err := p.Client.ListZones() + ctx := context.TODO() + zonesResponse, err := p.Client.ListZonesContext(ctx, cloudflare.WithPagination(p.PaginationOptions)) if err != nil { return nil, err } - - for _, zone := range zones { - if !p.domainFilter.Match(zone.Name) { - continue + for page := zonesResponse.ResultInfo.Page; page <= zonesResponse.ResultInfo.TotalPages; page++ { + p.PaginationOptions = cloudflare.PaginationOptions{ + PerPage: p.PaginationOptions.PerPage, + Page: page, } - - if !p.zoneIDFilter.Match(zone.ID) { - continue + zonesResponse, err := p.Client.ListZonesContext(ctx, cloudflare.WithPagination(p.PaginationOptions)) + if err != nil { + return nil, err } + for _, zone := range zonesResponse.Result { + if !p.domainFilter.Match(zone.Name) { + continue + } - result = append(result, zone) + if !p.zoneIDFilter.Match(zone.ID) { + continue + } + + result = append(result, zone) + } } return result, nil diff --git a/provider/cloudflare_test.go b/provider/cloudflare_test.go index cfa54ca21..2f9e7c504 100644 --- a/provider/cloudflare_test.go +++ b/provider/cloudflare_test.go @@ -17,15 +17,14 @@ limitations under the License. package provider import ( + "context" "fmt" "os" "testing" + cloudflare "github.com/cloudflare/cloudflare-go" "github.com/kubernetes-incubator/external-dns/endpoint" "github.com/kubernetes-incubator/external-dns/plan" - - cloudflare "github.com/cloudflare/cloudflare-go" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -66,6 +65,18 @@ func (m *mockCloudFlareClient) ListZones(zoneID ...string) ([]cloudflare.Zone, e return []cloudflare.Zone{{ID: "1234567890", Name: "ext-dns-test.zalando.to."}, {ID: "1234567891", Name: "foo.com."}}, nil } +func (m *mockCloudFlareClient) ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) { + return cloudflare.ZonesResponse{ + Result: []cloudflare.Zone{ + {ID: "1234567890", Name: "ext-dns-test.zalando.to."}, + {ID: "1234567891", Name: "foo.com."}}, + ResultInfo: cloudflare.ResultInfo{ + Page: 1, + TotalPages: 1, + }, + }, nil +} + type mockCloudFlareUserDetailsFail struct{} func (m *mockCloudFlareUserDetailsFail) CreateDNSRecord(zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) { @@ -96,6 +107,10 @@ func (m *mockCloudFlareUserDetailsFail) ListZones(zoneID ...string) ([]cloudflar return []cloudflare.Zone{{Name: "ext-dns-test.zalando.to."}}, nil } +func (m *mockCloudFlareUserDetailsFail) ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) { + return cloudflare.ZonesResponse{}, nil +} + type mockCloudFlareCreateZoneFail struct{} func (m *mockCloudFlareCreateZoneFail) CreateDNSRecord(zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) { @@ -155,6 +170,17 @@ func (m *mockCloudFlareDNSRecordsFail) ListZones(zoneID ...string) ([]cloudflare return []cloudflare.Zone{{Name: "ext-dns-test.zalando.to."}}, nil } +func (m *mockCloudFlareDNSRecordsFail) ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) { + return cloudflare.ZonesResponse{ + Result: []cloudflare.Zone{ + {ID: "1234567890", Name: "ext-dns-test.zalando.to."}, + {ID: "1234567891", Name: "foo.com."}}, + ResultInfo: cloudflare.ResultInfo{ + TotalPages: 1, + }, + }, nil +} + type mockCloudFlareZoneIDByNameFail struct{} func (m *mockCloudFlareZoneIDByNameFail) CreateDNSRecord(zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) { @@ -245,6 +271,10 @@ func (m *mockCloudFlareListZonesFail) ListZones(zoneID ...string) ([]cloudflare. return []cloudflare.Zone{{}}, fmt.Errorf("no zones available") } +func (m *mockCloudFlareListZonesFail) ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) { + return cloudflare.ZonesResponse{}, fmt.Errorf("no zones available") +} + type mockCloudFlareCreateRecordsFail struct{} func (m *mockCloudFlareCreateRecordsFail) CreateDNSRecord(zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) { @@ -275,6 +305,10 @@ func (m *mockCloudFlareCreateRecordsFail) ListZones(zoneID ...string) ([]cloudfl return []cloudflare.Zone{{}}, fmt.Errorf("no zones available") } +func (m *mockCloudFlareCreateRecordsFail) ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) { + return cloudflare.ZonesResponse{}, nil +} + type mockCloudFlareDeleteRecordsFail struct{} func (m *mockCloudFlareDeleteRecordsFail) CreateDNSRecord(zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) { @@ -305,6 +339,10 @@ func (m *mockCloudFlareDeleteRecordsFail) ListZones(zoneID ...string) ([]cloudfl return []cloudflare.Zone{{Name: "ext-dns-test.zalando.to."}}, nil } +func (m *mockCloudFlareDeleteRecordsFail) ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) { + return cloudflare.ZonesResponse{}, nil +} + type mockCloudFlareUpdateRecordsFail struct{} func (m *mockCloudFlareUpdateRecordsFail) CreateDNSRecord(zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) { @@ -335,6 +373,10 @@ func (m *mockCloudFlareUpdateRecordsFail) ListZones(zoneID ...string) ([]cloudfl return []cloudflare.Zone{{Name: "ext-dns-test.zalando.to."}}, nil } +func (m *mockCloudFlareUpdateRecordsFail) ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) { + return cloudflare.ZonesResponse{}, nil +} + func TestNewCloudFlareChanges(t *testing.T) { expect := []struct { Name string @@ -439,13 +481,23 @@ func TestRecords(t *testing.T) { func TestNewCloudFlareProvider(t *testing.T) { _ = os.Setenv("CF_API_KEY", "xxxxxxxxxxxxxxxxx") _ = os.Setenv("CF_API_EMAIL", "test@test.com") - _, err := NewCloudFlareProvider(NewDomainFilter([]string{"ext-dns-test.zalando.to."}), NewZoneIDFilter([]string{""}), false, true) + _, err := NewCloudFlareProvider( + NewDomainFilter([]string{"ext-dns-test.zalando.to."}), + NewZoneIDFilter([]string{""}), + 1, + false, + true) if err != nil { t.Errorf("should not fail, %s", err) } _ = os.Unsetenv("CF_API_KEY") _ = os.Unsetenv("CF_API_EMAIL") - _, err = NewCloudFlareProvider(NewDomainFilter([]string{"ext-dns-test.zalando.to."}), NewZoneIDFilter([]string{""}), false, true) + _, err = NewCloudFlareProvider( + NewDomainFilter([]string{"ext-dns-test.zalando.to."}), + NewZoneIDFilter([]string{""}), + 50, + false, + true) if err == nil { t.Errorf("expected to fail") } From ffee2018ba1c2b5d40d66042d246ac3b71969298 Mon Sep 17 00:00:00 2001 From: Alex Nederlof Date: Sat, 26 Jan 2019 09:35:13 +0100 Subject: [PATCH 041/136] Improve documentation regarding Alias I got stuck here and opened #865 because I thought it was a bug. I hope this will help others set it up correctly the first time. --- docs/tutorials/aws.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/aws.md b/docs/tutorials/aws.md index e8c25254a..7056de801 100644 --- a/docs/tutorials/aws.md +++ b/docs/tutorials/aws.md @@ -177,7 +177,7 @@ Annotations which are specific to AWS. ### alias -`external-dns.alpha.kubernetes.io/alias` if set to `true` on an ingress, it will create an ALIAS record when the target is an ALIAS as well. +`external-dns.alpha.kubernetes.io/alias` if set to `true` on an ingress, it will create an ALIAS record when the target is an ALIAS as well. To make the target an alias, the ingress needs to be configured correctly as described in [the docs](./nginx-ingress.md#with-a-separate-tcp-load-balancer). ## Verify ExternalDNS works (Ingress example) From 98f21696469d24c4d999eb1b9ff0a7e23a22c517 Mon Sep 17 00:00:00 2001 From: Martin Linkhorst Date: Mon, 28 Jan 2019 12:02:19 +0100 Subject: [PATCH 042/136] Remove linki from SECURITY_CONTACTS As per responsibilities of a security contact: https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#responsibilities --- SECURITY_CONTACTS | 1 - 1 file changed, 1 deletion(-) diff --git a/SECURITY_CONTACTS b/SECURITY_CONTACTS index 16ccfb1fe..0811c743f 100644 --- a/SECURITY_CONTACTS +++ b/SECURITY_CONTACTS @@ -10,7 +10,6 @@ # DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE # INSTRUCTIONS AT https://kubernetes.io/security/ -linki njuettner hjacobs raffo From 901f46061064ca2c86ffbc2e4862d746aa208494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nick=20J=C3=BCttner?= Date: Mon, 28 Jan 2019 13:40:40 +0100 Subject: [PATCH 043/136] Update cloudflare.go --- provider/cloudflare.go | 77 +++++++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 28 deletions(-) diff --git a/provider/cloudflare.go b/provider/cloudflare.go index b5751dbe7..6fa7c3db5 100644 --- a/provider/cloudflare.go +++ b/provider/cloudflare.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "os" + "strconv" "strings" cloudflare "github.com/cloudflare/cloudflare-go" @@ -27,6 +28,7 @@ import ( "github.com/kubernetes-incubator/external-dns/endpoint" "github.com/kubernetes-incubator/external-dns/plan" + "github.com/kubernetes-incubator/external-dns/source" ) const ( @@ -101,7 +103,7 @@ type CloudFlareProvider struct { // only consider hosted zones managing domains ending in this suffix domainFilter DomainFilter zoneIDFilter ZoneIDFilter - proxied bool + proxiedByDefault bool DryRun bool PaginationOptions cloudflare.PaginationOptions } @@ -113,7 +115,7 @@ type cloudFlareChange struct { } // NewCloudFlareProvider initializes a new CloudFlare DNS based Provider. -func NewCloudFlareProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, zonesPerPage int, proxied bool, dryRun bool) (*CloudFlareProvider, error) { +func NewCloudFlareProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, zonesPerPage int, proxiedByDefault bool, dryRun bool) (*CloudFlareProvider, error) { // initialize via API email and API key and returns new API object config, err := cloudflare.New(os.Getenv("CF_API_KEY"), os.Getenv("CF_API_EMAIL")) if err != nil { @@ -121,11 +123,11 @@ func NewCloudFlareProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, } provider := &CloudFlareProvider{ //Client: config, - Client: zoneService{config}, - domainFilter: domainFilter, - zoneIDFilter: zoneIDFilter, - proxied: proxied, - DryRun: dryRun, + Client: zoneService{config}, + domainFilter: domainFilter, + zoneIDFilter: zoneIDFilter, + proxiedByDefault: proxiedByDefault, + DryRun: dryRun, PaginationOptions: cloudflare.PaginationOptions{ PerPage: zonesPerPage, Page: 1, @@ -138,19 +140,14 @@ func NewCloudFlareProvider(domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, func (p *CloudFlareProvider) Zones() ([]cloudflare.Zone, error) { result := []cloudflare.Zone{} ctx := context.TODO() - zonesResponse, err := p.Client.ListZonesContext(ctx, cloudflare.WithPagination(p.PaginationOptions)) - if err != nil { - return nil, err - } - for page := zonesResponse.ResultInfo.Page; page <= zonesResponse.ResultInfo.TotalPages; page++ { - p.PaginationOptions = cloudflare.PaginationOptions{ - PerPage: p.PaginationOptions.PerPage, - Page: page, - } + p.PaginationOptions.Page = 1 + + for { zonesResponse, err := p.Client.ListZonesContext(ctx, cloudflare.WithPagination(p.PaginationOptions)) if err != nil { return nil, err } + for _, zone := range zonesResponse.Result { if !p.domainFilter.Match(zone.Name) { continue @@ -159,11 +156,13 @@ func (p *CloudFlareProvider) Zones() ([]cloudflare.Zone, error) { if !p.zoneIDFilter.Match(zone.ID) { continue } - result = append(result, zone) } + if p.PaginationOptions.Page == zonesResponse.ResultInfo.TotalPages { + break + } + p.PaginationOptions.Page++ } - return result, nil } @@ -183,7 +182,7 @@ func (p *CloudFlareProvider) Records() ([]*endpoint.Endpoint, error) { for _, r := range records { if supportedRecordType(r.Type) { - endpoints = append(endpoints, endpoint.NewEndpointWithTTL(r.Name, r.Type, endpoint.TTL(r.TTL), r.Content)) + endpoints = append(endpoints, endpoint.NewEndpointWithTTL(r.Name, r.Type, endpoint.TTL(r.TTL), r.Content).WithProviderSpecific(source.CloudflareProxiedKey, strconv.FormatBool(r.Proxied))) } } } @@ -193,11 +192,13 @@ func (p *CloudFlareProvider) Records() ([]*endpoint.Endpoint, error) { // ApplyChanges applies a given set of changes in a given zone. func (p *CloudFlareProvider) ApplyChanges(changes *plan.Changes) error { + proxiedByDefault := p.proxiedByDefault + combinedChanges := make([]*cloudFlareChange, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete)) - combinedChanges = append(combinedChanges, newCloudFlareChanges(cloudFlareCreate, changes.Create, p.proxied)...) - combinedChanges = append(combinedChanges, newCloudFlareChanges(cloudFlareUpdate, changes.UpdateNew, p.proxied)...) - combinedChanges = append(combinedChanges, newCloudFlareChanges(cloudFlareDelete, changes.Delete, p.proxied)...) + combinedChanges = append(combinedChanges, newCloudFlareChanges(cloudFlareCreate, changes.Create, proxiedByDefault)...) + combinedChanges = append(combinedChanges, newCloudFlareChanges(cloudFlareUpdate, changes.UpdateNew, proxiedByDefault)...) + combinedChanges = append(combinedChanges, newCloudFlareChanges(cloudFlareDelete, changes.Delete, proxiedByDefault)...) return p.submitChanges(combinedChanges) } @@ -290,21 +291,20 @@ func (p *CloudFlareProvider) getRecordID(records []cloudflare.DNSRecord, record } // newCloudFlareChanges returns a collection of Changes based on the given records and action. -func newCloudFlareChanges(action string, endpoints []*endpoint.Endpoint, proxied bool) []*cloudFlareChange { +func newCloudFlareChanges(action string, endpoints []*endpoint.Endpoint, proxiedByDefault bool) []*cloudFlareChange { changes := make([]*cloudFlareChange, 0, len(endpoints)) for _, endpoint := range endpoints { - changes = append(changes, newCloudFlareChange(action, endpoint, proxied)) + changes = append(changes, newCloudFlareChange(action, endpoint, proxiedByDefault)) } return changes } -func newCloudFlareChange(action string, endpoint *endpoint.Endpoint, proxied bool) *cloudFlareChange { +func newCloudFlareChange(action string, endpoint *endpoint.Endpoint, proxiedByDefault bool) *cloudFlareChange { ttl := defaultCloudFlareRecordTTL - if proxied && (cloudFlareTypeNotSupported[endpoint.RecordType] || strings.Contains(endpoint.DNSName, "*")) { - proxied = false - } + proxied := shouldBeProxied(endpoint, proxiedByDefault) + if endpoint.RecordTTL.IsConfigured() { ttl = int(endpoint.RecordTTL) } @@ -320,3 +320,24 @@ func newCloudFlareChange(action string, endpoint *endpoint.Endpoint, proxied boo }, } } + +func shouldBeProxied(endpoint *endpoint.Endpoint, proxiedByDefault bool) bool { + proxied := proxiedByDefault + + for _, v := range endpoint.ProviderSpecific { + if v.Name == source.CloudflareProxiedKey { + b, err := strconv.ParseBool(v.Value) + if err != nil { + log.Errorf("Failed to parse annotation [%s]: %v", source.CloudflareProxiedKey, err) + } else { + proxied = b + } + break + } + } + + if cloudFlareTypeNotSupported[endpoint.RecordType] || strings.Contains(endpoint.DNSName, "*") { + proxied = false + } + return proxied +} From 82fd370f6ed5499c1e63fb623f40b1c2ffdaf279 Mon Sep 17 00:00:00 2001 From: Martin Linkhorst Date: Mon, 28 Jan 2019 12:09:06 +0100 Subject: [PATCH 044/136] chore: update changelog for v0.5.10 --- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5789b6b13..265f45991 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,36 @@ +## v0.5.10 - 2019-01-28 + + - Docs: Improve documentation regarding Alias (#868) @alexnederlof + - Adds a new flag `--aws-api-retries` which allows overriding the number of retries (#858) @viafoura + - Docs: Make awscli commands use JSON output (#849) @ifosch + - Docs: Add missing apiVersion to Ingress resource (#847) @shlao + - Fix for AWS private DNS zone (#844) @xianlubird + - Add support for AWS ELBs in eu-north-1 (#843) @argoyle + - Create a SECURITY_CONTACTS file (#842) @njuettner + - Use correct product name for Google Cloud DNS (#841) @seils + - Change default AWSBatchChangeSize to 1000 (#839) @medzin + - Fix dry-run mode in rfc2136 provider (#838) @lachlancooper + - Fix typos in rfc2136 provider (#837) @lachlancooper + - rfc2136 provider: one IP Target per RRSET (#836) @ivanfilippov + - Normalize DNS names during planning (#833) @justinsb + - Implement Stringer for planTableRow (#832) @justinsb + - Docs: Better security granularity concerning external dns service principal for Azure (#829) @DenisBiondic + - Docs: Update links in Cloudflare docs (#824) @PascalKu + - Docs: Add metrics info to FAQ (#822) @zachyam + - Docs: Update nameserver IPs in coredns.md (#820) @mozhuli + - Docs: Fix commands to cleanup Cloudflare (#818) @acrogenesis + - Avoid unnecessary updating for CRD resource (#810) @xunpan + - Fix issues with CoreDNS provider and more than 1 targets (#807) @xunpan + - AWS: Add zone tag filter (#804) @csrwng + - Docs: Update CoreDNS tutorial with RBAC manifest (#803) @Lujeni + - Use SOAP API to improve DYN's provider's performance (#799) @sanyu + - Expose managed resources and records as metrics (#793) @linki + - Docs: Updating Azure tutorial (#788) @pelithne + - Improve errors in Records() of Infoblox provider (#785) @dsbrng25b + - Change default apiVersion of CRD Source (#774) @dsbrng25b + - Allow setting Cloudflare proxying on a per-Ingress basis (#650) @eswets + - Support A record for multiple IPs for headless services (#645) @toshipp + ## v0.5.9 - 2018-11-22 - Core: Update delivery.yaml to new format (#782) @linki From 492ae50d90158c77b549ccf1369d786d5d6eb5f8 Mon Sep 17 00:00:00 2001 From: Raffaele Di Fazio Date: Mon, 28 Jan 2019 17:35:19 +0100 Subject: [PATCH 045/136] Fixes some style in the faq.md file --- docs/faq.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index bcd2240f0..1c92dd4e6 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -244,10 +244,10 @@ an instance of a ingress controller. Let's assume you have two ingress controlle then you can start two ExternalDNS providers one with `--annotation-filter=kubernetes.io/ingress.class=nginx-internal` and one with `--annotation-filter=kubernetes.io/ingress.class=nginx-external`. -### Can external-dns manage(add/remove) records in a hosted zone which is setup in different aws account. +### Can external-dns manage(add/remove) records in a hosted zone which is setup in different AWS account? -yes, give it the correct cross-account/assume-role permissions and use the `--aws-assume-role` flag https://github.com/kubernetes-incubator/external-dns/pull/524#issue-181256561 +Yes, give it the correct cross-account/assume-role permissions and use the `--aws-assume-role` flag https://github.com/kubernetes-incubator/external-dns/pull/524#issue-181256561 -### how do I provide multiple values to the annotation `external-dns.alpha.kubernetes.io/hostname` +### How do I provide multiple values to the annotation `external-dns.alpha.kubernetes.io/hostname`? -separate them by `,` +Separate them by `,`. From a29d24ddaf59e9a0effb8de4d9bb71df2efcd397 Mon Sep 17 00:00:00 2001 From: Martin Linkhorst Date: Tue, 29 Jan 2019 14:55:06 +0100 Subject: [PATCH 046/136] fix: reduce number of API requests by caching a bit --- provider/aws.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/provider/aws.go b/provider/aws.go index 36b9ab772..7bfd17ac0 100644 --- a/provider/aws.go +++ b/provider/aws.go @@ -368,10 +368,15 @@ func (p *AWSProvider) submitChanges(changes []*route53.Change) error { // newChanges returns a collection of Changes based on the given records and action. func (p *AWSProvider) newChanges(action string, endpoints []*endpoint.Endpoint) []*route53.Change { + records, err := p.Records() + if err != nil { + log.Errorf("getting records failed: %v", err) + } + changes := make([]*route53.Change, 0, len(endpoints)) for _, endpoint := range endpoints { - changes = append(changes, p.newChange(action, endpoint)) + changes = append(changes, p.newChange(action, endpoint, records)) } return changes @@ -380,7 +385,7 @@ func (p *AWSProvider) newChanges(action string, endpoints []*endpoint.Endpoint) // newChange returns a Change of the given record by the given action, e.g. // action=ChangeActionCreate returns a change for creation of the record and // action=ChangeActionDelete returns a change for deletion of the record. -func (p *AWSProvider) newChange(action string, endpoint *endpoint.Endpoint) *route53.Change { +func (p *AWSProvider) newChange(action string, endpoint *endpoint.Endpoint, recordsCache []*endpoint.Endpoint) *route53.Change { change := &route53.Change{ Action: aws.String(action), ResourceRecordSet: &route53.ResourceRecordSet{ @@ -388,11 +393,6 @@ func (p *AWSProvider) newChange(action string, endpoint *endpoint.Endpoint) *rou }, } - rec, err := p.Records() - if err != nil { - log.Infof("getting records failed: %v", err) - } - if isAWSLoadBalancer(endpoint) { evalTargetHealth := p.evaluateTargetHealth if prop, ok := endpoint.GetProviderSpecificProperty(providerSpecificEvaluateTargetHealth); ok { @@ -405,7 +405,7 @@ func (p *AWSProvider) newChange(action string, endpoint *endpoint.Endpoint) *rou HostedZoneId: aws.String(canonicalHostedZone(endpoint.Targets[0])), EvaluateTargetHealth: aws.Bool(evalTargetHealth), } - } else if hostedZone := isAWSAlias(endpoint, rec); hostedZone != "" { + } else if hostedZone := isAWSAlias(endpoint, recordsCache); hostedZone != "" { zones, err := p.Zones() if err != nil { log.Errorf("getting zones failed: %v", err) From 69b951d7b20493e11aca0a6f9e93dcf9fa5ac9a4 Mon Sep 17 00:00:00 2001 From: Martin Linkhorst Date: Tue, 29 Jan 2019 14:25:38 +0100 Subject: [PATCH 047/136] only compare provider-specific annotations when they exist in the provider, skip target-health annotation --- Gopkg.lock | 247 +++++++++++++++++++++++++++++++++++++++++++-------- Gopkg.toml | 4 - plan/plan.go | 17 +++- 3 files changed, 226 insertions(+), 42 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index a8091b3ea..df3e200fa 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,53 +2,66 @@ [[projects]] + digest = "1:ae9d0182a5cf7dbb025a8fc5821234cc1f26ca342fc41d951a99f71b9adc1b87" name = "cloud.google.com/go" packages = [ "compute/metadata", - "internal" + "internal", ] + pruneopts = "" revision = "3b1ae45394a234c385be014e9a488f2bb6eef821" [[projects]] + digest = "1:fd38e3b8c27cab6561a7d2e8557205c3ca5c57cbb6d3a79e10f22e73e84fd776" name = "github.com/Azure/azure-sdk-for-go" packages = ["arm/dns"] + pruneopts = "" revision = "2629e2dfcfeab50896230140542c3b9d89b35ae1" version = "v10.0.4-beta" [[projects]] + digest = "1:f719ef698feb8da2923ebda9a8d553b977320b02182f3797e185202e588a94b1" name = "github.com/Azure/go-autorest" packages = [ "autorest", "autorest/adal", "autorest/azure", "autorest/date", - "autorest/to" + "autorest/to", ] + pruneopts = "" revision = "aa2a4534ab680e938d933870f58f23f77e0e208e" version = "v10.9.0" [[projects]] + digest = "1:7dc69d1597e4773ec5f64e5c078d55f0f011bb05ec0435346d0649ad978a23fd" name = "github.com/alecthomas/kingpin" packages = ["."] + pruneopts = "" revision = "1087e65c9441605df944fb12c33f0fe7072d18ca" version = "v2.2.5" [[projects]] branch = "master" + digest = "1:a74730e052a45a3fab1d310fdef2ec17ae3d6af16228421e238320846f2aaec8" name = "github.com/alecthomas/template" packages = [ ".", - "parse" + "parse", ] + pruneopts = "" revision = "a0175ee3bccc567396460bf5acd36800cb10c49c" [[projects]] branch = "master" + digest = "1:8483994d21404c8a1d489f6be756e25bfccd3b45d65821f25695577791a08e68" name = "github.com/alecthomas/units" packages = ["."] + pruneopts = "" revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" [[projects]] + digest = "1:d2dc5d0ccc137594ea6fb3870964967b96b43daac19b8093930c7b02873fd5ca" name = "github.com/aliyun/alibaba-cloud-sdk-go" packages = [ "sdk", @@ -61,12 +74,14 @@ "sdk/responses", "sdk/utils", "services/alidns", - "services/pvtz" + "services/pvtz", ] + pruneopts = "" revision = "cad214d7d71fba7883fcf3b7e550ba782c15b400" version = "1.27.7" [[projects]] + digest = "1:1c82dd6a02941a3c4f23a32eca182717ab79691939e97d6b971466b780f06eea" name = "github.com/aws/aws-sdk-go" packages = [ "aws", @@ -98,15 +113,18 @@ "private/protocol/xml/xmlutil", "service/route53", "service/servicediscovery", - "service/sts" + "service/sts", ] + pruneopts = "" revision = "9b0098a71f6d4d473a26ec8ad3c2feaac6eb1da6" version = "v1.13.32" [[projects]] branch = "master" + digest = "1:0c5485088ce274fac2e931c1b979f2619345097b39d91af3239977114adf0320" name = "github.com/beorn7/perks" packages = ["quantile"] + pruneopts = "" revision = "4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9" [[projects]] @@ -118,6 +136,7 @@ revision = "0c85496d873009e53e64d391ade643e7ac02e0d4" [[projects]] + digest = "1:eaeede87b418b97f9dee473f8940fd9b65ca5cdac0503350c7c8f8965ea3cf4d" name = "github.com/coreos/etcd" packages = [ "auth/authpb", @@ -125,84 +144,108 @@ "etcdserver/api/v3rpc/rpctypes", "etcdserver/etcdserverpb", "mvcc/mvccpb", - "pkg/types" + "pkg/types", ] + pruneopts = "" revision = "1b3ac99e8a431b381e633802cc42fe70e663baf5" version = "v3.2.15" [[projects]] + digest = "1:56c130d885a4aacae1dd9c7b71cfe39912c7ebc1ff7d2b46083c8812996dc43b" name = "github.com/davecgh/go-spew" packages = ["spew"] + pruneopts = "" revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" [[projects]] branch = "master" + digest = "1:dc166ce7345c060c2153561130d6d49ac580c804226ac675e368d533b36eb169" name = "github.com/denverdino/aliyungo" packages = [ "metadata", - "util" + "util", ] + pruneopts = "" revision = "69560d9530f5265ba00ffad2520d7ef01c2cddd4" [[projects]] + digest = "1:6098222470fe0172157ce9bbef5d2200df4edde17ee649c5d6e48330e4afa4c6" name = "github.com/dgrijalva/jwt-go" packages = ["."] + pruneopts = "" revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e" version = "v3.2.0" [[projects]] + digest = "1:32d1941b093bb945de75b0276348494be318d34f3df39c4413d61e002c800bc6" name = "github.com/digitalocean/godo" packages = [ ".", - "context" + "context", ] + pruneopts = "" revision = "77ea48de76a7b31b234d854f15d003c68bb2fb90" version = "v1.1.1" [[projects]] + digest = "1:5ffd39844bdd1259a6227d544f582c6686ce43c8c44399a46052fe3bd2bed93c" name = "github.com/dnsimple/dnsimple-go" packages = ["dnsimple"] + pruneopts = "" revision = "d1105abc03b313d7b8d9b04364f6bd053b346e59" version = "v0.14.0" [[projects]] + digest = "1:e17d18b233f506404061c27ac4a08624dad38dcd0d49f9cfdae67a7772a4fb8c" name = "github.com/exoscale/egoscale" packages = ["."] + pruneopts = "" revision = "c6d915cb993f1a54f604acefc0fc15cf6578a87a" version = "v0.11.0" [[projects]] branch = "master" + digest = "1:ae7fb2062735e966ab69d14d2a091f3778b0d676dc8d1f01d092bcb0fb8ed45b" name = "github.com/ffledgling/pdns-go" packages = ["."] + pruneopts = "" revision = "524e7daccd99651cdb56426eb15b7d61f9597a5c" [[projects]] + digest = "1:b13707423743d41665fd23f0c36b2f37bb49c30e94adb813319c44188a51ba22" name = "github.com/ghodss/yaml" packages = ["."] + pruneopts = "" revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7" version = "v1.0.0" [[projects]] + digest = "1:a00483fe4106b86fb1187a92b5cf6915c85f294ed4c129ccbe7cb1f1a06abd46" name = "github.com/go-ini/ini" packages = ["."] + pruneopts = "" revision = "32e4c1e6bc4e7d0d8451aa6b75200d19e37a536a" version = "v1.32.0" [[projects]] + digest = "1:8e67153fc0a9fb0d6c9707e36cf80e217a012364307b222eb4ba6828f7e881e6" name = "github.com/go-resty/resty" packages = ["."] + pruneopts = "" revision = "97a15579492cd5f35632499f315d7a8df94160a1" version = "v1.8.0" [[projects]] + digest = "1:54d5c6a784a9de9c836fc070d51d0689c3e99ee6d24dba8a36f0762039dae830" name = "github.com/gogo/googleapis" packages = ["google/rpc"] + pruneopts = "" revision = "8558fb44d2f1fc223118afc694129d2c2d2924d1" version = "v1.1.0" [[projects]] + digest = "1:6e73003ecd35f4487a5e88270d3ca0a81bc80dc88053ac7e4dcfec5fba30d918" name = "github.com/gogo/protobuf" packages = [ "gogoproto", @@ -210,19 +253,23 @@ "proto", "protoc-gen-gogo/descriptor", "sortkeys", - "types" + "types", ] + pruneopts = "" revision = "636bf0302bc95575d69441b25a2603156ffdddf1" version = "v1.1.1" [[projects]] branch = "master" + digest = "1:b12aff239810a9fa71e901a712a52f9da4c6e536852e943be693dec1d4519dfd" name = "github.com/golang/glog" packages = ["."] + pruneopts = "" revision = "3fa5b9870d1d29f6d7907b29f1ae8c6eeb403829" source = "github.com/kubermatic/glog-logrus" [[projects]] + digest = "1:3dd078fda7500c341bc26cfbc6c6a34614f295a2457149fc1045cab767cbcf18" name = "github.com/golang/protobuf" packages = [ "jsonpb", @@ -232,51 +279,63 @@ "ptypes/any", "ptypes/duration", "ptypes/struct", - "ptypes/timestamp" + "ptypes/timestamp", ] + pruneopts = "" revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" version = "v1.2.0" [[projects]] branch = "master" + digest = "1:be28c0531a755f2178acf1e327e6f5a8a3968feb5f2567cdc968064253141751" name = "github.com/google/btree" packages = ["."] + pruneopts = "" revision = "e89373fe6b4a7413d7acd6da1725b83ef713e6e4" [[projects]] + digest = "1:f9f45f75f332e03fc7e9fe9188ea4e1ce4d14779ef34fa1b023da67518e36327" name = "github.com/google/go-cmp" packages = [ "cmp", "cmp/internal/diff", "cmp/internal/function", - "cmp/internal/value" + "cmp/internal/value", ] + pruneopts = "" revision = "3af367b6b30c263d47e8895973edcca9a49cf029" version = "v0.2.0" [[projects]] branch = "master" + digest = "1:9abc49f39e3e23e262594bb4fb70abf74c0c99e94f99153f43b143805e850719" name = "github.com/google/go-querystring" packages = ["query"] + pruneopts = "" revision = "53e6ce116135b80d037921a7fdd5138cf32d7a8a" [[projects]] + digest = "1:a2823c34933d4a2b36284f617f483d51fe156a443923284b3660f183dcfa3338" name = "github.com/google/gofuzz" packages = ["."] + pruneopts = "" revision = "44d81051d367757e1c7c6a5a86423ece9afcf63c" [[projects]] + digest = "1:16b2837c8b3cf045fa2cdc82af0cf78b19582701394484ae76b2c3bc3c99ad73" name = "github.com/googleapis/gnostic" packages = [ "OpenAPIv2", "compiler", - "extensions" + "extensions", ] + pruneopts = "" revision = "7c663266750e7d82587642f65e60bc4083f1f84e" version = "v0.2.0" [[projects]] branch = "master" + digest = "1:54a44d48a24a104e078ef5f94d82f025a6be757e7c42b4370c621a3928d6ab7c" name = "github.com/gophercloud/gophercloud" packages = [ ".", @@ -287,77 +346,100 @@ "openstack/identity/v2/tokens", "openstack/identity/v3/tokens", "openstack/utils", - "pagination" + "pagination", ] + pruneopts = "" revision = "bfc4756e1a693a850d7d459f4b28b21f35a24b5a" [[projects]] + digest = "1:dbbeb8ddb0be949954c8157ee8439c2adfd8dc1c9510eb44a6e58cb68c3dce28" name = "github.com/gorilla/context" packages = ["."] + pruneopts = "" revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42" version = "v1.1.1" [[projects]] + digest = "1:c2c8666b4836c81a1d247bdf21c6a6fc1ab586538ab56f74437c2e0df5c375e1" name = "github.com/gorilla/mux" packages = ["."] + pruneopts = "" revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf" version = "v1.6.2" [[projects]] branch = "master" + digest = "1:009a1928b8c096338b68b5822d838a72b4d8520715c1463614476359f3282ec8" name = "github.com/gregjones/httpcache" packages = [ ".", - "diskcache" + "diskcache", ] + pruneopts = "" revision = "9cad4c3443a7200dd6400aef47183728de563a38" [[projects]] + digest = "1:8e3bd93036b4a925fe2250d3e4f38f21cadb8ef623561cd80c3c50c114b13201" name = "github.com/hashicorp/errwrap" packages = ["."] + pruneopts = "" revision = "8a6fb523712970c966eefc6b39ed2c5e74880354" version = "v1.0.0" [[projects]] branch = "master" + digest = "1:72308fdd6d5ef61106a95be7ca72349a5565809042b6426a3cfb61d99483b824" name = "github.com/hashicorp/go-multierror" packages = ["."] + pruneopts = "" revision = "886a7fbe3eb1c874d46f623bfa70af45f425b3d1" [[projects]] + digest = "1:3313a63031ae281e5f6fd7b0bbca733dfa04d2429df86519e3b4d4c016ccb836" name = "github.com/hashicorp/golang-lru" packages = [ ".", - "simplelru" + "simplelru", ] + pruneopts = "" revision = "20f1fb78b0740ba8c3cb143a61e86ba5c8669768" version = "v0.5.0" [[projects]] + digest = "1:af7e132906cb360f4d7c34a9e1434825467f21c4ff5c521ad4cc5b55352876a8" name = "github.com/imdario/mergo" packages = ["."] + pruneopts = "" revision = "6633656539c1639d9d78127b7d47c622b5d7b6dc" [[projects]] + digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" name = "github.com/inconshreveable/mousetrap" packages = ["."] + pruneopts = "" revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" version = "v1.0" [[projects]] branch = "master" + digest = "1:e0a13d0a368c028716e78448db972657b5292c7238d61405e8289f47c05c8706" name = "github.com/infobloxopen/infoblox-go-client" packages = ["."] + pruneopts = "" revision = "61dc5f9b0a655ebf43026f0d8a837ad1e28e4b96" [[projects]] + digest = "1:6f49eae0c1e5dab1dafafee34b207aeb7a42303105960944828c2079b92fc88e" name = "github.com/jmespath/go-jmespath" packages = ["."] + pruneopts = "" revision = "0b12d6b5" [[projects]] + digest = "1:53ac4e911e12dde0ab68655e2006449d207a5a681f084974da2b06e5dbeaca72" name = "github.com/json-iterator/go" packages = ["."] + pruneopts = "" revision = "ab8a2e0c74be9d3be70b3184d9acc634935ded82" version = "1.1.4" @@ -370,185 +452,239 @@ revision = "1bcb110c8726cee477939f507f4760a95e155347" [[projects]] + digest = "1:7c23a751ce2f84663fa411acb87eae0da2d09c39a8e99b08bd8f65fae75d8928" name = "github.com/linki/instrumented_http" packages = ["."] + pruneopts = "" revision = "508103cfb3b315fa9752b5bcd4fb2d97bbc26d89" version = "v0.2.0" [[projects]] + digest = "1:1c41354ef11c9dbae2fe1ceee8369fcb2634977ba07e701e19ea53e8742c5420" name = "github.com/linode/linodego" packages = ["."] + pruneopts = "" revision = "7edbc87f0140b7561dbc20458877a56bdded5eb8" version = "v0.3.0" [[projects]] branch = "master" + digest = "1:63722a4b1e1717be7b98fc686e0b30d5e7f734b9e93d7dee86293b6deab7ea28" name = "github.com/matttproud/golang_protobuf_extensions" packages = ["pbutil"] + pruneopts = "" revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" [[projects]] + digest = "1:4c8d8358c45ba11ab7bb15df749d4df8664ff1582daead28bae58cf8cbe49890" name = "github.com/miekg/dns" packages = ["."] + pruneopts = "" revision = "5a2b9fab83ff0f8bfc99684bd5f43a37abe560f1" version = "v1.0.8" [[projects]] + digest = "1:0c0ff2a89c1bb0d01887e1dac043ad7efbf3ec77482ef058ac423d13497e16fd" name = "github.com/modern-go/concurrent" packages = ["."] + pruneopts = "" revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94" version = "1.0.3" [[projects]] + digest = "1:e32bdbdb7c377a07a9a46378290059822efdce5c8d96fe71940d87cb4f918855" name = "github.com/modern-go/reflect2" packages = ["."] + pruneopts = "" revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" version = "1.0.1" [[projects]] + digest = "1:11c58e19ff7ce22740423bb933f1ddca3bf575def40d5ac3437ec12871b1648b" name = "github.com/natefinch/lumberjack" packages = ["."] + pruneopts = "" revision = "a96e63847dc3c67d17befa69c303767e2f84e54f" version = "v2.1" [[projects]] + digest = "1:d8b5d0ecca348c835914a1ed8589f17a6a7f309befab7327b0470324531f7ac4" name = "github.com/nesv/go-dynect" packages = ["dynect"] + pruneopts = "" revision = "cdd946344b54bdf7dbeac406c2f1fe93150f08ea" version = "v0.6.0" [[projects]] + digest = "1:70df8e71a953626770223d4982801fa73e7e99cbfcca068b95127f72af9b9edd" name = "github.com/oracle/oci-go-sdk" packages = [ "common", - "dns" + "dns", ] + pruneopts = "" revision = "a2ded717dc4bb4916c0416ec79f81718b576dbc4" version = "v1.8.0" [[projects]] branch = "master" + digest = "1:c24598ffeadd2762552269271b3b1510df2d83ee6696c1e543a0ff653af494bc" name = "github.com/petar/GoLLRB" packages = ["llrb"] + pruneopts = "" revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4" [[projects]] + digest = "1:b46305723171710475f2dd37547edd57b67b9de9f2a6267cafdd98331fd6897f" name = "github.com/peterbourgon/diskv" packages = ["."] + pruneopts = "" revision = "5f041e8faa004a95c88a202771f4cc3e991971e6" version = "v2.0.1" [[projects]] + digest = "1:cf172c58bb2a13ed39ea1c9e79525567c63bcc2c4afbb6cf023e87b31780f249" name = "github.com/pkg/errors" packages = ["."] + pruneopts = "" revision = "f15c970de5b76fac0b59abb32d62c17cc7bed265" [[projects]] + digest = "1:256484dbbcd271f9ecebc6795b2df8cad4c458dd0f5fd82a8c2fa0c29f233411" name = "github.com/pmezard/go-difflib" packages = ["difflib"] + pruneopts = "" revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" [[projects]] + digest = "1:2f69dc6b2685b31a1a410ef697410aa8a669704fb201d45dbd8c1911728afa75" name = "github.com/prometheus/client_golang" packages = [ "prometheus", - "prometheus/promhttp" + "prometheus/promhttp", ] + pruneopts = "" revision = "967789050ba94deca04a5e84cce8ad472ce313c1" version = "v0.9.0-pre1" [[projects]] branch = "master" + digest = "1:60aca47f4eeeb972f1b9da7e7db51dee15ff6c59f7b401c1588b8e6771ba15ef" name = "github.com/prometheus/client_model" packages = ["go"] + pruneopts = "" revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c" [[projects]] branch = "master" + digest = "1:e3aa5178be4fc4ae8cdb37d11c02f7490c00450a9f419e6aa84d02d3b47e90d2" name = "github.com/prometheus/common" packages = [ "expfmt", "internal/bitbucket.org/ww/goautoneg", - "model" + "model", ] + pruneopts = "" revision = "2e54d0b93cba2fd133edc32211dcc32c06ef72ca" [[projects]] + digest = "1:a6a85fc81f2a06ccac3d45005523afbeee45138d781d4f3cb7ad9889d5c65aab" name = "github.com/prometheus/procfs" packages = [ ".", - "xfs" + "xfs", ] + pruneopts = "" revision = "a6e9df898b1336106c743392c48ee0b71f5c4efa" [[projects]] branch = "master" + digest = "1:bb2ccb2d56cbafdec58af0f473f45304e19876f09fa671960ca87802b656a9c0" name = "github.com/sanyu/dynectsoap" packages = ["dynectsoap"] pruneopts = "" revision = "b83de5edc4e022f22903eeb3b428d2f39fb740e5" [[projects]] + digest = "1:7f569d906bdd20d906b606415b7d794f798f91a62fcfb6a4daa6d50690fb7a3f" name = "github.com/satori/go.uuid" packages = ["."] + pruneopts = "" revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3" version = "v1.2.0" [[projects]] + digest = "1:3ac248add5bb40a3c631c5334adcd09aa72d15af2768a5bc0274084ea7b2e5ba" name = "github.com/sirupsen/logrus" packages = ["."] + pruneopts = "" revision = "f006c2ac4710855cf0f916dd6b77acf6b048dc6e" version = "v1.0.3" [[projects]] + digest = "1:a1403cc8a94b8d7956ee5e9694badef0e7b051af289caad1cf668331e3ffa4f6" name = "github.com/spf13/cobra" packages = ["."] + pruneopts = "" revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385" version = "v0.0.3" [[projects]] + digest = "1:0a52bcb568386d98f4894575d53ce3e456f56471de6897bb8b9de13c33d9340e" name = "github.com/spf13/pflag" packages = ["."] + pruneopts = "" revision = "9a97c102cda95a86cec2345a6f09f55a939babf5" version = "v1.0.2" [[projects]] + digest = "1:306417ea2f31ea733df356a2b895de63776b6a5107085b33458e5cd6eb1d584d" name = "github.com/stretchr/objx" packages = ["."] + pruneopts = "" revision = "facf9a85c22f48d2f52f2380e4efce1768749a89" version = "v0.1" [[projects]] + digest = "1:a30066593578732a356dc7e5d7f78d69184ca65aeeff5939241a3ab10559bb06" name = "github.com/stretchr/testify" packages = [ "assert", "mock", "require", - "suite" + "suite", ] + pruneopts = "" revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" version = "v1.2.1" [[projects]] branch = "master" + digest = "1:81f435c83e3523a7ee3f277769727f73ca66218ca8188d96a0935a4841b47a76" name = "github.com/tent/http-link-go" packages = ["."] + pruneopts = "" revision = "ac974c61c2f990f4115b119354b5e0b47550e888" [[projects]] + digest = "1:74f86c458e82e1c4efbab95233e0cf51b7cc02dc03193be9f62cd81224e10401" name = "go.uber.org/atomic" packages = ["."] + pruneopts = "" revision = "1ea20fb1cbb1cc08cbd0d913a96dead89aa18289" version = "v1.3.2" [[projects]] + digest = "1:22c7effcb4da0eacb2bb1940ee173fac010e9ef3c691f5de4b524d538bd980f5" name = "go.uber.org/multierr" packages = ["."] + pruneopts = "" revision = "3c4937480c32f4c13a875a1829af76c98ca3d40a" version = "v1.1.0" [[projects]] + digest = "1:246f378f80fba6fcf0f191c486b6613265abd2bc0f2fa55a36b928c67352021e" name = "go.uber.org/zap" packages = [ ".", @@ -557,22 +693,26 @@ "internal/color", "internal/exit", "zapcore", - "zapgrpc" + "zapgrpc", ] + pruneopts = "" revision = "ff33455a0e382e8a81d14dd7c922020b6b5e7982" version = "v1.9.1" [[projects]] branch = "master" + digest = "1:b2d8b39397ca07929a3de3a3fd2b6ca4c8d48e9cadaa7cf2b083e27fd9e78107" name = "golang.org/x/crypto" packages = [ "ed25519", "ed25519/internal/edwards25519", - "ssh/terminal" + "ssh/terminal", ] + pruneopts = "" revision = "0709b304e793a5edb4a2c0145f281ecdc20838a4" [[projects]] + digest = "1:782723d6fc27d202f1943219d68d58b3f6bcab6212c85294b1ddd8b586b1d356" name = "golang.org/x/net" packages = [ "bpf", @@ -588,30 +728,36 @@ "ipv4", "ipv6", "publicsuffix", - "trace" + "trace", ] + pruneopts = "" revision = "161cd47e91fd58ac17490ef4d742dc98bb4cf60e" [[projects]] + digest = "1:dad5a319c4710358be1f2bf68f9fb7f90a71d7c641221b79801d5667b28f19e3" name = "golang.org/x/oauth2" packages = [ ".", "google", "internal", "jws", - "jwt" + "jwt", ] + pruneopts = "" revision = "3c3a985cb79f52a3190fbc056984415ca6763d01" [[projects]] + digest = "1:39d88a855976e160babdd254802e1c2ae75ed380328c39742b57928852da6207" name = "golang.org/x/sys" packages = [ "unix", - "windows" + "windows", ] + pruneopts = "" revision = "13d03a9a82fba647c21a0ef8fba44a795d0f0835" [[projects]] + digest = "1:5acd3512b047305d49e8763eef7ba423901e85d5dd2fd1e71778a0ea8de10bd4" name = "golang.org/x/text" packages = [ "collate", @@ -627,28 +773,34 @@ "unicode/bidi", "unicode/cldr", "unicode/norm", - "unicode/rangetable" + "unicode/rangetable", ] + pruneopts = "" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" [[projects]] branch = "master" + digest = "1:55a681cb66f28755765fa5fa5104cbd8dc85c55c02d206f9f89566451e3fe1aa" name = "golang.org/x/time" packages = ["rate"] + pruneopts = "" revision = "fbb02b2291d28baffd63558aa44b4b56f178d650" [[projects]] + digest = "1:2ad38d79865e33dde6157b7048debd6e7d47e0709df7b5e11bb888168e316908" name = "google.golang.org/api" packages = [ "dns/v1", "gensupport", "googleapi", - "googleapi/internal/uritemplates" + "googleapi/internal/uritemplates", ] + pruneopts = "" revision = "a0ff90edab763c86aa88f2b1eb4f3301b82f6336" [[projects]] + digest = "1:41e2b7e287117f6136f75286d48072ecf781ba54823ffeb2098e152e7dc45ef6" name = "google.golang.org/appengine" packages = [ ".", @@ -660,20 +812,24 @@ "internal/modules", "internal/remote_api", "internal/urlfetch", - "urlfetch" + "urlfetch", ] + pruneopts = "" revision = "4f7eeb5305a4ba1966344836ba4af9996b7b4e05" [[projects]] branch = "master" + digest = "1:e43f1cb3f488a0c2be85939c2a594636f60b442a12a196c778bd2d6c9aca3df7" name = "google.golang.org/genproto" packages = [ "googleapis/api/annotations", - "googleapis/rpc/status" + "googleapis/rpc/status", ] + pruneopts = "" revision = "11092d34479b07829b72e10713b159248caf5dad" [[projects]] + digest = "1:ca75b3775a5d4e5d1fb48f57ef0865b4aaa8b3f00e6b52be68db991c4594e0a7" name = "google.golang.org/grpc" packages = [ ".", @@ -702,25 +858,31 @@ "resolver/passthrough", "stats", "status", - "tap" + "tap", ] + pruneopts = "" revision = "32fb0ac620c32ba40a4626ddf94d90d12cce3455" version = "v1.14.0" [[projects]] + digest = "1:e5d1fb981765b6f7513f793a3fcaac7158408cca77f75f7311ac82cc88e9c445" name = "gopkg.in/inf.v0" packages = ["."] + pruneopts = "" revision = "3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4" version = "v0.9.0" [[projects]] + digest = "1:f0620375dd1f6251d9973b5f2596228cc8042e887cd7f827e4220bc1ce8c30e2" name = "gopkg.in/yaml.v2" packages = ["."] + pruneopts = "" revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" version = "v2.2.1" [[projects]] branch = "release-1.0" + digest = "1:bc43af6616d8ca12a7b8e806874387f0f1e181c08f547e9cd77f6cbac8cefd86" name = "istio.io/api" packages = [ "authentication/v1alpha1", @@ -728,11 +890,13 @@ "mixer/v1", "mixer/v1/config/client", "networking/v1alpha3", - "rbac/v1alpha1" + "rbac/v1alpha1", ] + pruneopts = "" revision = "76349c53b87f03f1e610b3aa3843dba3c38138d7" [[projects]] + digest = "1:7eb25280e1f610470bb0c43ab6c91573cfc78672a58542106b9b71705581429a" name = "istio.io/istio" packages = [ "pilot/pkg/config/kube/crd", @@ -741,12 +905,14 @@ "pilot/pkg/serviceregistry/kube", "pkg/cache", "pkg/kube", - "pkg/log" + "pkg/log", ] + pruneopts = "" revision = "42773aacced474d97159902d20579a25b1f98106" version = "1.0.1" [[projects]] + digest = "1:f420c8548c93242d8e5dcfa5b34e0243883b4e660f65076e869daafac877144d" name = "k8s.io/api" packages = [ "admissionregistration/v1alpha1", @@ -777,24 +943,28 @@ "settings/v1alpha1", "storage/v1", "storage/v1alpha1", - "storage/v1beta1" + "storage/v1beta1", ] + pruneopts = "" revision = "072894a440bdee3a891dea811fe42902311cd2a3" version = "kubernetes-1.11.0" [[projects]] + digest = "1:66d1421ecff35bc48ee0b11a3f891f3af6f775ed6bb1d3e0deeaba221bf42490" name = "k8s.io/apiextensions-apiserver" packages = [ "pkg/apis/apiextensions", "pkg/apis/apiextensions/v1beta1", "pkg/client/clientset/clientset", "pkg/client/clientset/clientset/scheme", - "pkg/client/clientset/clientset/typed/apiextensions/v1beta1" + "pkg/client/clientset/clientset/typed/apiextensions/v1beta1", ] + pruneopts = "" revision = "8e7f43002fec5394a8d96ebca781aa9d4b37aaef" version = "kubernetes-1.10.4" [[projects]] + digest = "1:b6b2fb7b4da1ac973b64534ace2299a02504f16bc7820cb48edb8ca4077183e1" name = "k8s.io/apimachinery" packages = [ "pkg/api/errors", @@ -837,12 +1007,14 @@ "pkg/version", "pkg/watch", "third_party/forked/golang/json", - "third_party/forked/golang/reflect" + "third_party/forked/golang/reflect", ] + pruneopts = "" revision = "103fd098999dc9c0c88536f5c9ad2e5da39373ae" version = "kubernetes-1.11.0" [[projects]] + digest = "1:d04779a8de7d5465e0463bd986506348de5e89677c74777f695d3145a7a8d15e" name = "k8s.io/client-go" packages = [ "discovery", @@ -940,15 +1112,18 @@ "util/homedir", "util/integer", "util/jsonpath", - "util/retry" + "util/retry", ] + pruneopts = "" revision = "7d04d0e2a0a1a4d4a1cd6baa432a2301492e4e65" version = "v8.0.0" [[projects]] branch = "master" + digest = "1:526095379da1098c3f191a0008cc59c9bf9927492e63da7689e5de424219c162" name = "k8s.io/kube-openapi" packages = ["pkg/util/proto"] + pruneopts = "" revision = "d8ea2fe547a448256204cfc68dfee7b26c720acb" [solve-meta] diff --git a/Gopkg.toml b/Gopkg.toml index f2d17cb9d..6590be2e0 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -116,10 +116,6 @@ ignored = ["github.com/kubernetes/repo-infra/kazel"] name = "github.com/miekg/dns" version = "1.0.8" -[[constraint]] - name = "github.com/google/go-cmp" - version = "0.2.0" - [[constraint]] name = "github.com/sanyu/dynectsoap" branch = "master" diff --git a/plan/plan.go b/plan/plan.go index 9f575451c..f7031b2c9 100644 --- a/plan/plan.go +++ b/plan/plan.go @@ -20,7 +20,6 @@ import ( "fmt" "strings" - "github.com/google/go-cmp/cmp" "github.com/kubernetes-incubator/external-dns/endpoint" ) @@ -187,7 +186,21 @@ func shouldUpdateTTL(desired, current *endpoint.Endpoint) bool { } func shouldUpdateProviderSpecific(desired, current *endpoint.Endpoint) bool { - return !cmp.Equal(desired.ProviderSpecific, current.ProviderSpecific) + for _, c := range current.ProviderSpecific { + // don't consider target health when detecting changes + // see: https://github.com/kubernetes-incubator/external-dns/issues/869#issuecomment-458576954 + if c.Name == "aws/evaluate-target-health" { + continue + } + + for _, d := range desired.ProviderSpecific { + if d.Name == c.Name && d.Value != c.Value { + return true + } + } + } + + return false } // filterRecordsForPlan removes records that are not relevant to the planner. From 8b1c15780210d0da4d47eff72bffcba911a32034 Mon Sep 17 00:00:00 2001 From: Joe Hohertz Date: Fri, 1 Feb 2019 15:19:45 -0500 Subject: [PATCH 048/136] fix test of ProviderSpecific comparison Signed-off-by: Joe Hohertz --- plan/plan.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plan/plan.go b/plan/plan.go index f7031b2c9..efe949be6 100644 --- a/plan/plan.go +++ b/plan/plan.go @@ -186,6 +186,9 @@ func shouldUpdateTTL(desired, current *endpoint.Endpoint) bool { } func shouldUpdateProviderSpecific(desired, current *endpoint.Endpoint) bool { + if current.ProviderSpecific == nil && len(desired.ProviderSpecific) == 0 { + return false + } for _, c := range current.ProviderSpecific { // don't consider target health when detecting changes // see: https://github.com/kubernetes-incubator/external-dns/issues/869#issuecomment-458576954 From 5829f0219408bc724e3d08b057c89903453e33a4 Mon Sep 17 00:00:00 2001 From: Himanshu Pandey Date: Tue, 12 Feb 2019 09:09:01 -0800 Subject: [PATCH 049/136] Fixed typo in debug output --- provider/rfc2136.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/rfc2136.go b/provider/rfc2136.go index 5250448c6..22356861c 100644 --- a/provider/rfc2136.go +++ b/provider/rfc2136.go @@ -269,7 +269,7 @@ func (r rfc2136Provider) RemoveRecord(ep *endpoint.Endpoint) error { log.Debugf("RemoveRecord.ep=%s", ep) newRR := fmt.Sprintf("%s 0 %s 0.0.0.0", ep.DNSName, ep.RecordType) - log.Debugf("Adding RR: %s", newRR) + log.Debugf("Removing RR: %s", newRR) rr, err := dns.NewRR(newRR) if err != nil { From ed71ab6fb6bcb89ebb0d4eb8b16fe7e35c9683b2 Mon Sep 17 00:00:00 2001 From: Anand Patel Date: Thu, 14 Feb 2019 15:30:31 -0500 Subject: [PATCH 050/136] fix broken test after merge --- source/service_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/service_test.go b/source/service_test.go index 1e7a6efa8..acd6a2b96 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -1565,7 +1565,7 @@ func TestHeadlessServices(t *testing.T) { hostnameAnnotationKey: "service.example.org", }, v1.ClusterIPNone, - "1.1.1.1", + []string{"1.1.1.1", "1.1.1.2"}, map[string]string{ "component": "foo", }, @@ -1796,7 +1796,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { hostnameAnnotationKey: "service.example.org", }, v1.ClusterIPNone, - "1.1.1.1", + []string{"1.1.1.1", "1.1.1.2"}, map[string]string{ "component": "foo", }, From adfe88a5264b7bc3ff1c91308d6eb57d573fd41b Mon Sep 17 00:00:00 2001 From: Anand Singh Kunwar Date: Mon, 24 Dec 2018 16:08:06 +0530 Subject: [PATCH 051/136] Fixed PowerDNS Domain Filter Bug * When using Domain Filters with PowerDNS provider and providing no domain filter, the provider ignores all zones instead of including all zones which is the default behaviour * Added test cases for PartitionZones function of PDNSClient --- provider/pdns.go | 5 +-- provider/pdns_test.go | 84 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/provider/pdns.go b/provider/pdns.go index 8b33a4c96..8bf888daf 100644 --- a/provider/pdns.go +++ b/provider/pdns.go @@ -30,11 +30,12 @@ import ( log "github.com/sirupsen/logrus" "crypto/tls" + "net" + pgo "github.com/ffledgling/pdns-go" "github.com/kubernetes-incubator/external-dns/endpoint" "github.com/kubernetes-incubator/external-dns/pkg/tlsutils" "github.com/kubernetes-incubator/external-dns/plan" - "net" ) type pdnsChangeType string @@ -175,7 +176,7 @@ func (c *PDNSAPIClient) PartitionZones(zones []pgo.Zone) (filteredZones []pgo.Zo } } } else { - residualZones = zones + filteredZones = zones } return filteredZones, residualZones } diff --git a/provider/pdns_test.go b/provider/pdns_test.go index c7c5b592c..76fdb7041 100644 --- a/provider/pdns_test.go +++ b/provider/pdns_test.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "errors" //"fmt" "net/http" @@ -475,6 +476,44 @@ var ( }, }, } + + DomainFilterListSingle = DomainFilter{ + filters: []string{ + "example.com", + }, + } + + DomainFilterListMultiple = DomainFilter{ + filters: []string{ + "example.com", + "mock.com", + }, + } + + DomainFilterListEmpty = DomainFilter{ + filters: []string{}, + } + + DomainFilterEmptyClient = &PDNSAPIClient{ + dryRun: false, + authCtx: context.WithValue(context.TODO(), pgo.ContextAPIKey, pgo.APIKey{Key: "TEST-API-KEY"}), + client: pgo.NewAPIClient(pgo.NewConfiguration()), + domainFilter: DomainFilterListEmpty, + } + + DomainFilterSingleClient = &PDNSAPIClient{ + dryRun: false, + authCtx: context.WithValue(context.TODO(), pgo.ContextAPIKey, pgo.APIKey{Key: "TEST-API-KEY"}), + client: pgo.NewAPIClient(pgo.NewConfiguration()), + domainFilter: DomainFilterListSingle, + } + + DomainFilterMultipleClient = &PDNSAPIClient{ + dryRun: false, + authCtx: context.WithValue(context.TODO(), pgo.ContextAPIKey, pgo.APIKey{Key: "TEST-API-KEY"}), + client: pgo.NewAPIClient(pgo.NewConfiguration()), + domainFilter: DomainFilterListMultiple, + } ) /******************************************************************************/ @@ -912,6 +951,51 @@ func (suite *NewPDNSProviderTestSuite) TestPDNSmutateRecords() { } +func (suite *NewPDNSProviderTestSuite) TestPDNSClientPartitionZones() { + zoneList := []pgo.Zone{ + ZoneEmpty, + ZoneEmpty2, + } + + partitionResultFilteredEmptyFilter := []pgo.Zone{ + ZoneEmpty, + ZoneEmpty2, + } + + partitionResultResidualEmptyFilter := ([]pgo.Zone)(nil) + + partitionResultFilteredSingleFilter := []pgo.Zone{ + ZoneEmpty, + } + + partitionResultResidualSingleFilter := []pgo.Zone{ + ZoneEmpty2, + } + + partitionResultFilteredMultipleFilter := []pgo.Zone{ + ZoneEmpty, + } + + partitionResultResidualMultipleFilter := []pgo.Zone{ + ZoneEmpty2, + } + + // Check filtered, residual zones when no domain filter specified + filteredZones, residualZones := DomainFilterEmptyClient.PartitionZones(zoneList) + assert.Equal(suite.T(), partitionResultFilteredEmptyFilter, filteredZones) + assert.Equal(suite.T(), partitionResultResidualEmptyFilter, residualZones) + + // Check filtered, residual zones when a single domain filter specified + filteredZones, residualZones = DomainFilterSingleClient.PartitionZones(zoneList) + assert.Equal(suite.T(), partitionResultFilteredSingleFilter, filteredZones) + assert.Equal(suite.T(), partitionResultResidualSingleFilter, residualZones) + + // Check filtered, residual zones when a multiple domain filter specified + filteredZones, residualZones = DomainFilterMultipleClient.PartitionZones(zoneList) + assert.Equal(suite.T(), partitionResultFilteredMultipleFilter, filteredZones) + assert.Equal(suite.T(), partitionResultResidualMultipleFilter, residualZones) +} + func TestNewPDNSProviderTestSuite(t *testing.T) { suite.Run(t, new(NewPDNSProviderTestSuite)) } From 79bf8c807e1832c4ce542842f124041fb2bf95e6 Mon Sep 17 00:00:00 2001 From: Dimitrij Klesev Date: Wed, 23 Jan 2019 11:36:13 +0100 Subject: [PATCH 052/136] Add RcodeZero Anycast DNS provider --- README.md | 2 + docs/tutorials/rcodezero.md | 194 ++++++++++++++++ main.go | 4 +- pkg/apis/externaldns/types.go | 5 +- provider/rcode0.go | 336 +++++++++++++++++++++++++++ provider/rcode0_test.go | 417 ++++++++++++++++++++++++++++++++++ 6 files changed, 956 insertions(+), 2 deletions(-) create mode 100644 docs/tutorials/rcodezero.md create mode 100644 provider/rcode0.go create mode 100644 provider/rcode0_test.go diff --git a/README.md b/README.md index d2058e41b..d04b212e1 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ ExternalDNS' current release is `v0.5`. This version allows you to keep selected * [AWS Service Discovery](https://docs.aws.amazon.com/Route53/latest/APIReference/overview-service-discovery.html) * [AzureDNS](https://azure.microsoft.com/en-us/services/dns) * [CloudFlare](https://www.cloudflare.com/dns) +* [RcodeZero](https://www.rcodezero.at/) * [DigitalOcean](https://www.digitalocean.com/products/networking) * [DNSimple](https://dnsimple.com/) * [Infoblox](https://www.infoblox.com/products/dns/) @@ -57,6 +58,7 @@ The following tutorials are provided: * [Azure](docs/tutorials/azure.md) * [CoreDNS](docs/tutorials/coredns.md) * [Cloudflare](docs/tutorials/cloudflare.md) +* [RcodeZero](docs/tutorials/rcodezero.md) * [DigitalOcean](docs/tutorials/digitalocean.md) * [Infoblox](docs/tutorials/infoblox.md) * [Dyn](docs/tutorials/dyn.md) diff --git a/docs/tutorials/rcodezero.md b/docs/tutorials/rcodezero.md new file mode 100644 index 000000000..9db91f65d --- /dev/null +++ b/docs/tutorials/rcodezero.md @@ -0,0 +1,194 @@ +# Setting up ExternalDNS for Services on RcodeZero + +This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using RcodeZero Anycast DNS. + +Make sure to use **>=0.5.0** version of ExternalDNS for this tutorial. + +## Creating a RcodeZero DNS zone + +After logging into RcodeZero Dashboard add a master domain under [RcodeZero Add Zone](https://my.rcodezero.at/domain/create). Use it throughout this guide (substitute example.com). + +## Creating RcodeZero Credentials + +> The RcodeZero Anycast-Network is provisioned via web interface or REST-API. + +RcodeZero API can be enabled and a key generated on [RcodeZero API](https://my.rcodezero.at/enableapi) + +The environment var `RC0_API_KEY` will be needed to run ExternalDNS with RcodeZero. + +## Deploy ExternalDNS + +Connect your `kubectl` client to the cluster you want to test ExternalDNS with. +Then apply one of the following manifests file to deploy ExternalDNS. + +### Manifest (for clusters without RBAC enabled) + +```yaml +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: external-dns +spec: + strategy: + type: Recreate + template: + metadata: + labels: + app: external-dns + spec: + containers: + - name: external-dns + image: registry.opensource.zalan.do/teapot/external-dns:latest + 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. + - --provider=rcodezero + - --rc0-enc-txt # (optional) encrypt TXT records; encryption key has to be provided with RC0_ENC_KEY env var. + env: + - name: RC0_API_KEY + value: "YOUR_RCODEZERO_API_KEY" + - name: RC0_ENC_VAR + value: "YOUR_ENCRYPTION_KEY_STRING" +``` + +### Manifest (for clusters with RBAC enabled) + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: external-dns +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: external-dns +rules: +- apiGroups: [""] + resources: ["services"] + verbs: ["get","watch","list"] +- apiGroups: [""] + resources: ["pods"] + verbs: ["get","watch","list"] +- apiGroups: ["extensions"] + resources: ["ingresses"] + verbs: ["get","watch","list"] +- apiGroups: [""] + resources: ["nodes"] + verbs: ["list"] +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: external-dns-viewer +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: external-dns +subjects: +- kind: ServiceAccount + name: external-dns + namespace: default +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: external-dns +spec: + strategy: + type: Recreate + template: + metadata: + labels: + app: external-dns + spec: + serviceAccountName: external-dns + containers: + - name: external-dns + image: registry.opensource.zalan.do/teapot/external-dns:latest + 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. + - --provider=rcodezero + - --rc0-enc-txt # (optional) encrypt TXT records; encryption key has to be provided with RC0_ENC_KEY env var. + env: + - name: RC0_API_KEY + value: "YOUR_RCODEZERO_API_KEY" + - name: RC0_ENC_VAR + value: "YOUR_ENCRYPTION_KEY_STRING" +``` + +## Deploying an Nginx Service + +Create a service file called 'nginx.yaml' with the following contents: + +```yaml +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: nginx +spec: + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: nginx + annotations: + external-dns.alpha.kubernetes.io/hostname: example.com + external-dns.alpha.kubernetes.io/ttl: "120" #optional +spec: + selector: + app: nginx + type: LoadBalancer + ports: + - protocol: TCP + port: 80 + targetPort: 80 +``` + +Note the annotation on the service; use the same hostname as the RcodeZero DNS zone created above. The annotation may also be a subdomain +of the DNS zone (e.g. 'www.example.com'). + +By setting the TTL annotation on the service, you have to pass a valid TTL, which must be 120 or above. +This annotation is optional, if you won't set it, it will be 1 (automatic) which is 300. + +ExternalDNS uses this annotation to determine what services should be registered with DNS. Removing the annotation +will cause ExternalDNS to remove the corresponding DNS records. + +Create the deployment and service: + +``` +$ kubectl create -f nginx.yaml +``` + +Depending where you run your service it can take a little while for your cloud provider to create an external IP for the service. + +Once the service has an external IP assigned, ExternalDNS will notice the new service IP address and synchronize +the RcodeZero DNS records. + +## Verifying RcodeZero DNS records + +Check your [RcodeZero Configured Zones](https://my.rcodezero.at/domain) and select the ExternalDNS managed domain. + +Substitute the zone for the one created above if a different domain was used. + +This should show the external IP address of the service as the A record for your domain. + +## Cleanup + +Now that we have verified that ExternalDNS will automatically manage RcodeZero DNS records, we can delete the tutorial's example: + +``` +$ kubectl delete -f nginx.yaml +$ kubectl delete -f externaldns.yaml +``` diff --git a/main.go b/main.go index 603a3ad5e..8e00aad6f 100644 --- a/main.go +++ b/main.go @@ -132,7 +132,9 @@ func main() { p, err = provider.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.DryRun) case "cloudflare": p, err = provider.NewCloudFlareProvider(domainFilter, zoneIDFilter, cfg.CloudflareZonesPerPage, cfg.CloudflareProxied, cfg.DryRun) - case "google": + case "rcodezero": + p, err = provider.NewRcodeZeroProvider(domainFilter, cfg.DryRun, cfg.RcodezeroTXTEncrypt) + case "google": p, err = provider.NewGoogleProvider(cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.DryRun) case "digitalocean": p, err = provider.NewDigitalOceanProvider(domainFilter, cfg.DryRun) diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index ecbcd480c..0db7b2452 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -67,6 +67,7 @@ type Config struct { AzureResourceGroup string CloudflareProxied bool CloudflareZonesPerPage int + RcodezeroTXTEncrypt bool InfobloxGridHost string InfobloxWapiPort int InfobloxWapiUsername string @@ -142,6 +143,7 @@ var defaultConfig = &Config{ AzureResourceGroup: "", CloudflareProxied: false, CloudflareZonesPerPage: 50, + RcodezeroTXTEncrypt: false, InfobloxGridHost: "", InfobloxWapiPort: 443, InfobloxWapiUsername: "admin", @@ -243,7 +245,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("service-type-filter", "The service types to take care about (default: all, expected: ClusterIP, NodePort, LoadBalancer or ExternalName)").StringsVar(&cfg.ServiceTypeFilter) // Flags related to providers - app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, google, azure, cloudflare, digitalocean, dnsimple, infoblox, dyn, designate, coredns, skydns, inmemory, pdns, oci, exoscale, linode, rfc2136)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "alibabacloud", "cloudflare", "digitalocean", "dnsimple", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "pdns", "oci", "exoscale", "linode", "rfc2136") + app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, google, azure, cloudflare, rcodezero, digitalocean, dnsimple, infoblox, dyn, designate, coredns, skydns, inmemory, pdns, oci, exoscale, linode, rfc2136)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "pdns", "oci", "exoscale", "linode", "rfc2136") app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter) app.Flag("zone-id-filter", "Filter target zones by hosted zone id; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.ZoneIDFilter) app.Flag("google-project", "When using the Google provider, current project is auto-detected, when running on GCP. Specify other project with this. Must be specified when running outside GCP.").Default(defaultConfig.GoogleProject).StringVar(&cfg.GoogleProject) @@ -271,6 +273,7 @@ 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("rc0-txt-enc", "When using the Rcodezero provider with txt registry option, set if TXT rrs are encrypted (default: false)").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("pdns-server", "When using the PowerDNS/PDNS provider, specify the URL to the pdns server (required when --provider=pdns)").Default(defaultConfig.PDNSServer).StringVar(&cfg.PDNSServer) diff --git a/provider/rcode0.go b/provider/rcode0.go new file mode 100644 index 000000000..1b1ff9b63 --- /dev/null +++ b/provider/rcode0.go @@ -0,0 +1,336 @@ +/* +Copyright 2019 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 provider + +import ( + "fmt" + "github.com/kubernetes-incubator/external-dns/endpoint" + "github.com/kubernetes-incubator/external-dns/plan" + rc0 "github.com/nic-at/rc0go" + log "github.com/sirupsen/logrus" + "net/url" + "os" + "strings" +) + +// RcodeZeroProvider implements the DNS provider for RcodeZero Anycast DNS. +type RcodeZeroProvider struct { + Client *rc0.Client + + DomainFilter DomainFilter + DryRun bool + TXTEncrypt bool + Key []byte +} + +// NewRcodeZeroProvider creates a new RcodeZero Anycast DNS provider. +// +// Returns the provider or an error if a provider could not be created. +func NewRcodeZeroProvider(domainFilter DomainFilter, dryRun bool, txtEnc bool) (*RcodeZeroProvider, error){ + + client, err := rc0.NewClient(os.Getenv("RC0_API_KEY")) + + if err != nil { + return nil, err + } + + value := os.Getenv("RC0_BASE_URL") + if len(value) != 0 { + client.BaseURL, _ = url.Parse(os.Getenv("RC0_BASE_URL")) + } + + if err != nil { + return nil, fmt.Errorf("failed to initialize rcodezero provider: %v", err) + } + + provider := &RcodeZeroProvider{ + Client: client, + DomainFilter: domainFilter, + DryRun: dryRun, + TXTEncrypt: txtEnc, + } + + if txtEnc { + provider.Key = []byte(os.Getenv("RC0_ENC_KEY")) + } + + return provider, nil +} + +// Returns filtered zones if filter is set +func (p *RcodeZeroProvider) Zones() ([]*rc0.Zone, error) { + + var result []*rc0.Zone + + zones, err := p.fetchZones() + if err != nil { + return nil, err + } + + for _, zone := range zones { + if p.DomainFilter.Match(zone.Domain) { + result = append(result, zone) + } + } + + return result, nil +} + +// Returns resource records +// +// Decrypts TXT records if TXT-Encrypt flag is set and key is provided +func (p *RcodeZeroProvider) Records() ([]*endpoint.Endpoint, error) { + + zones, err := p.Zones() + if err != nil { + return nil, err + } + + var endpoints []*endpoint.Endpoint + + for _, zone := range zones { + + rrset, err := p.fetchRecords(zone.Domain) + + if err != nil { + return nil, err + } + + for _, r := range rrset { + + if supportedRecordType(r.Type) { + + if p.TXTEncrypt && (p.Key != nil) && strings.EqualFold(r.Type, "TXT") { + p.Client.RRSet.DecryptTXT(p.Key, r) + } + + if len(r.Records) > 1 { + + for _, _r := range r.Records { + if !_r.Disabled { + endpoints = append(endpoints, endpoint.NewEndpointWithTTL(r.Name, r.Type, endpoint.TTL(r.TTL), _r.Content)) + } + } + + } else { + if !r.Records[0].Disabled { + endpoints = append(endpoints, endpoint.NewEndpointWithTTL(r.Name, r.Type, endpoint.TTL(r.TTL), r.Records[0].Content)) + } + } + + } + } + } + + return endpoints, nil +} + +// ApplyChanges applies a given set of changes in a given zone. +func (p *RcodeZeroProvider) ApplyChanges(changes *plan.Changes) error { + + combinedChanges := make([]*rc0.RRSetChange, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete)) + + combinedChanges = append(combinedChanges, p.NewRcodezeroChanges(rc0.ChangeTypeADD, changes.Create)...) + combinedChanges = append(combinedChanges, p.NewRcodezeroChanges(rc0.ChangeTypeUPDATE, changes.UpdateNew)...) + combinedChanges = append(combinedChanges, p.NewRcodezeroChanges(rc0.ChangeTypeDELETE, changes.Delete)...) + + return p.submitChanges(combinedChanges) +} + +// Helper function +func rcodezeroChangesByZone(zones []*rc0.Zone, changeSet []*rc0.RRSetChange) map[string][]*rc0.RRSetChange { + + changes := make(map[string][]*rc0.RRSetChange) + zoneNameIDMapper := zoneIDName{} + for _, z := range zones { + zoneNameIDMapper.Add(z.Domain, z.Domain) + changes[z.Domain] = []*rc0.RRSetChange{} + } + + for _, c := range changeSet { + zone, _ := zoneNameIDMapper.FindZone(c.Name) + if zone == "" { + log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected ", c.Name) + continue + } + changes[zone] = append(changes[zone], c) + } + + return changes +} + +// Helper function +func (p *RcodeZeroProvider) fetchRecords(zoneName string) ([]*rc0.RRType, error) { + + var allRecords []*rc0.RRType + + listOptions := rc0.NewListOptions() + + for { + records, page, err := p.Client.RRSet.List(zoneName, listOptions) + + if err != nil { + return nil, err + } + + allRecords = append(allRecords, records...) + + if page == nil || (page.CurrentPage == page.LastPage) { + break + } + + listOptions.SetPageNumber(page.CurrentPage + 1) + } + + return allRecords, nil +} + +// Helper function +func (p *RcodeZeroProvider) fetchZones() ([]*rc0.Zone, error) { + + var allZones []*rc0.Zone + + listOptions := rc0.NewListOptions() + + for { + zones, page, err := p.Client.Zones.List(listOptions) + if err != nil { + return nil, err + } + allZones = append(allZones, zones...) + + if page == nil || page.IsLastPage() { + break + } + + listOptions.SetPageNumber(page.CurrentPage + 1) + } + + return allZones, nil +} + +// Helper function to submit changes. +// +// Changes are submitted by change type. +func (p *RcodeZeroProvider) submitChanges(changes []*rc0.RRSetChange) error { + + if len(changes) == 0 { + return nil + } + + zones, err := p.Zones() + if err != nil { + return err + } + + // separate into per-zone change sets to be passed to the API. + changesByZone := rcodezeroChangesByZone(zones, changes) + + for zoneName, changes := range changesByZone { + + for _, change := range changes { + + logFields := log.Fields{ + "record" : change.Name, + "content" : change.Records[0].Content, + "type" : change.Type, + "action" : change.ChangeType, + "zone" : zoneName, + } + + log.WithFields(logFields).Info("Changing record.") + + if p.DryRun { + continue + } + + // to avoid accidentally adding extra dot if already present + change.Name = strings.TrimSuffix(change.Name, ".") + "." + + switch change.ChangeType { + case rc0.ChangeTypeADD: + sr, err := p.Client.RRSet.Create(zoneName, []*rc0.RRSetChange{change}) + + if err != nil { + return err + } + + if sr.HasError() { + return fmt.Errorf("adding new RR resulted in an error: %v", sr.Message) + } + + case rc0.ChangeTypeUPDATE: + sr, err := p.Client.RRSet.Edit(zoneName, []*rc0.RRSetChange{change}) + + if err != nil { + return err + } + + if sr.HasError() { + return fmt.Errorf("updating existing RR resulted in an error: %v", sr.Message) + } + + case rc0.ChangeTypeDELETE: + sr, err := p.Client.RRSet.Delete(zoneName, []*rc0.RRSetChange{change}) + + if err != nil { + return err + } + + if sr.HasError() { + return fmt.Errorf("deleting existing RR resulted in an error: %v", sr.Message) + } + + default: + return fmt.Errorf("unsupported changeType submitted: %v", change.ChangeType) + } + } + } + return nil +} + +// Returns a RcodeZero specific array with rrset change objects. +func (p *RcodeZeroProvider) NewRcodezeroChanges(action string, endpoints []*endpoint.Endpoint) []*rc0.RRSetChange { + + changes := make([]*rc0.RRSetChange, 0, len(endpoints)) + + for _, _endpoint := range endpoints { + changes = append(changes, p.NewRcodezeroChange(action, _endpoint)) + } + + return changes +} + +// Returns a RcodeZero specific rrset change object. +func (p *RcodeZeroProvider) NewRcodezeroChange(action string, endpoint *endpoint.Endpoint) *rc0.RRSetChange { + + change := &rc0.RRSetChange{ + Type: endpoint.RecordType, + ChangeType: action, + Name: endpoint.DNSName, + Records: []*rc0.Record{{ + Disabled: false, + Content: endpoint.Targets[0], + }}, + } + + if p.TXTEncrypt && (p.Key != nil) && strings.EqualFold(endpoint.RecordType, "TXT") { + p.Client.RRSet.EncryptTXT(p.Key, change) + } + + return change +} \ No newline at end of file diff --git a/provider/rcode0_test.go b/provider/rcode0_test.go new file mode 100644 index 000000000..d848ad006 --- /dev/null +++ b/provider/rcode0_test.go @@ -0,0 +1,417 @@ +/* +Copyright 2019 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 provider + +import ( + "fmt" + "github.com/kubernetes-incubator/external-dns/endpoint" + "github.com/kubernetes-incubator/external-dns/plan" + rc0 "github.com/nic-at/rc0go" + "github.com/stretchr/testify/require" + "os" + "testing" +) + +const ( + testZoneOne = "testzone1.at" + testZoneTwo = "testzone2.at" + + rrsetChangesUnsupportedChangeType = 0 + +) + +type mockRcodeZeroClient rc0.Client + +type mockZoneManagementService struct { + TestNilZonesReturned bool + TestErrorReturned bool +} + +type mockRRSetService struct { + TestErrorReturned bool +} + +func (m *mockRcodeZeroClient) resetMockServices() { + m.Zones = &mockZoneManagementService{} + m.RRSet = &mockRRSetService{} +} + +func (m *mockZoneManagementService) resetTestConditions() { + m.TestNilZonesReturned = false + m.TestErrorReturned = false +} + +func (m *mockRRSetService) resetTestConditions() { + m.TestErrorReturned = false +} + +func TestRcodeZeroProvider_Records(t *testing.T) { + + mockRRSetService := &mockRRSetService{} + mockZoneManagementService := &mockZoneManagementService{} + + provider := &RcodeZeroProvider{ + Client: (*rc0.Client)(&mockRcodeZeroClient{ + Zones: mockZoneManagementService, + RRSet: mockRRSetService, + }), + } + + endpoints, err := provider.Records() // should return 6 rrs + + if err != nil { + t.Errorf("should not fail, %s", err) + } + require.Equal(t, 6, len(endpoints)) + + mockRRSetService.TestErrorReturned = true + + _, err = provider.Records() + if err == nil { + t.Errorf("expected to fail, %s", err) + } + +} + +func TestRcodeZeroProvider_ApplyChanges(t *testing.T) { + + mockRRSetService := &mockRRSetService{} + mockZoneManagementService := &mockZoneManagementService{} + + provider := &RcodeZeroProvider{ + Client: (*rc0.Client)(&mockRcodeZeroClient{ + Zones: mockZoneManagementService, + RRSet: mockRRSetService, + }), + DomainFilter: NewDomainFilter([]string{testZoneOne}), + } + + changes := mockChanges() + + err := provider.ApplyChanges(changes) + + if err != nil { + t.Errorf("should not fail, %s", err) + } + +} + +func TestRcodeZeroProvider_NewRcodezeroChanges(t *testing.T) { + + provider := &RcodeZeroProvider{} + + changes := mockChanges() + + createChanges := provider.NewRcodezeroChanges(testZoneOne, changes.Create) + require.Equal(t, 4, len(createChanges)) + + deleteChanges := provider.NewRcodezeroChanges(testZoneOne, changes.Delete) + require.Equal(t, 1, len(deleteChanges)) + + updateOldChanges := provider.NewRcodezeroChanges(testZoneOne, changes.UpdateOld) + require.Equal(t, 1, len(updateOldChanges)) + + updateNewChanges := provider.NewRcodezeroChanges(testZoneOne, changes.UpdateNew) + require.Equal(t, 1, len(updateNewChanges)) +} + +func TestRcodeZeroProvider_NewRcodezeroChange(t *testing.T) { + + _endpoint := &endpoint.Endpoint{ + RecordType: "A", + DNSName: "app." + testZoneOne, + RecordTTL: 300, + Targets: endpoint.Targets{"target"}, + } + + provider := &RcodeZeroProvider{} + + rrsetChange := provider.NewRcodezeroChange(testZoneOne, _endpoint) + + require.Equal(t, _endpoint.RecordType, rrsetChange.Type) + require.Equal(t, _endpoint.DNSName, rrsetChange.Name) + require.Equal(t, _endpoint.Targets[0], rrsetChange.Records[0].Content) + //require.Equal(t, endpoint.RecordTTL, rrsetChange.TTL) + +} + +func Test_submitChanges(t *testing.T) { + + mockRRSetService := &mockRRSetService{} + mockZoneManagementService := &mockZoneManagementService{} + + provider := &RcodeZeroProvider{ + Client: (*rc0.Client)(&mockRcodeZeroClient{ + Zones: mockZoneManagementService, + RRSet: mockRRSetService, + }), + DomainFilter: NewDomainFilter([]string{testZoneOne}), + } + + changes := mockRRSetChanges(rrsetChangesUnsupportedChangeType) + + err := provider.submitChanges(changes) + + if err == nil { + t.Errorf("expected to fail, %s", err) + } + +} + +func mockRRSetChanges(condition int) []*rc0.RRSetChange { + + switch condition { + case rrsetChangesUnsupportedChangeType: + return []*rc0.RRSetChange{ + { + Name: testZoneOne, + Type: "A", + ChangeType: "UNSUPPORTED", + Records: []*rc0.Record{{Content:"fail"}}, + + }, + } + default: + return nil + } +} + +func mockChanges() *plan.Changes { + + changes := &plan.Changes{} + + changes.Create = []*endpoint.Endpoint{ + {DNSName: "new.ext-dns-test."+testZoneOne, Targets: endpoint.Targets{"target"}, RecordType: "A"}, + {DNSName: "new.ext-dns-test-with-ttl."+testZoneOne, Targets: endpoint.Targets{"target"}, RecordType: "A", RecordTTL: 100}, + {DNSName: "new.ext-dns-test.unexpected.com", Targets: endpoint.Targets{"target"}, RecordType: "AAAA"}, + {DNSName: testZoneOne, Targets: endpoint.Targets{"target"}, RecordType: "CNAME"}, + } + changes.Delete = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test."+testZoneOne, Targets: endpoint.Targets{"target"}}} + changes.UpdateOld = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test."+testZoneOne, Targets: endpoint.Targets{"target-old"}}} + changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test."+testZoneOne, Targets: endpoint.Targets{"target-new"}, RecordType: "CNAME", RecordTTL: 100}} + + return changes +} + +func TestRcodeZeroProvider_Zones(t *testing.T) { + + mockRRSetService := &mockRRSetService{} + mockZoneManagementService := &mockZoneManagementService{} + + provider := &RcodeZeroProvider{ + Client: (*rc0.Client)(&mockRcodeZeroClient{ + Zones: mockZoneManagementService, + RRSet: mockRRSetService, + }), + } + + mockZoneManagementService.TestNilZonesReturned = true + + zones, err := provider.Zones() + + if err != nil { + t.Fatal(err) + } + require.Equal(t, 0, len(zones)) + mockZoneManagementService.resetTestConditions() + + mockZoneManagementService.TestErrorReturned = true + + zones, err = provider.Zones() + if err == nil { + t.Errorf("expected to fail, %s", err) + } + +} + +func TestNewRcodeZeroProvider(t *testing.T) { + + _ = os.Setenv("RC0_API_KEY", "123") + p, err := NewRcodeZeroProvider(NewDomainFilter([]string{"ext-dns-test."+testZoneOne+"."}), true, true) + + if err != nil { + t.Errorf("should not fail, %s", err) + } + + require.Equal(t, true, p.DryRun) + require.Equal(t, true, p.TXTEncrypt) + require.Equal(t, true, p.DomainFilter.IsConfigured()) + require.Equal(t, false, p.DomainFilter.Match("ext-dns-test."+testZoneTwo+".")) // filter is set, so it should match only provided domains + + p, err = NewRcodeZeroProvider(DomainFilter{}, false, false) + + if err != nil { + t.Errorf("should not fail, %s", err) + } + + require.Equal(t, false, p.DryRun) + require.Equal(t, false, p.DomainFilter.IsConfigured()) + require.Equal(t, true, p.DomainFilter.Match("ext-dns-test."+testZoneOne+".")) // filter is not set, so it should match any + + _ = os.Unsetenv("RC0_API_KEY") + _, err = NewRcodeZeroProvider(DomainFilter{}, false, false) + + if err == nil { + t.Errorf("expected to fail") + } + +} + +/* mocking mockRRSetServiceInterface */ + +func (m *mockRRSetService) List(zone string, options *rc0.ListOptions) ([]*rc0.RRType, *rc0.Page, error) { + + if m.TestErrorReturned { + return nil, nil, fmt.Errorf("operation RRSet.List failed") + } + + return mockRRSet(zone), nil, nil +} + +func mockRRSet(zone string) []*rc0.RRType { + return []*rc0.RRType{ + { + Name: "app."+zone+".", + Type: "TXT", + TTL: 300, + Records: []*rc0.Record{ + { + Content: "\"heritage=external-dns,external-dns/owner=default,external-dns/resource=ingress/default/app\"", + Disabled: false, + }, + }, + }, + { + Name: "app."+zone+".", + Type: "A", + TTL: 300, + Records: []*rc0.Record{ + { + Content: "127.0.0.1", + Disabled: false, + }, + }, + }, + { + Name: "www."+zone+".", + Type: "A", + TTL: 300, + Records: []*rc0.Record{ + { + Content: "127.0.0.1", + Disabled: false, + }, + }, + }, + { + Name: zone+".", + Type: "SOA", + TTL: 3600, + Records: []*rc0.Record{ + { + Content: "sec1.rcode0.net. rcodezero-soa.ipcom.at. 2019011616 10800 3600 604800 3600", + Disabled: false, + }, + }, + }, + { + Name: zone+".", + Type: "NS", + TTL: 3600, + Records: []*rc0.Record{ + { + Content: "sec2.rcode0.net.", + Disabled: false, + }, + { + Content: "sec1.rcode0.net.", + Disabled: false, + }, + }, + }, + } +} + +func (m *mockRRSetService) Create(zone string, rrsetCreate []*rc0.RRSetChange) (*rc0.StatusResponse, error) { + + return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil + +} +func (m *mockRRSetService) Edit(zone string, rrsetEdit []*rc0.RRSetChange) (*rc0.StatusResponse, error) { + + return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil +} +func (m *mockRRSetService) Delete(zone string, rrsetDelete []*rc0.RRSetChange) (*rc0.StatusResponse, error) { + + return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil +} +func (m *mockRRSetService) SubmitChangeSet(zone string, changeSet []*rc0.RRSetChange) (*rc0.StatusResponse, error) { + + return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil +} + +func (m *mockRRSetService) EncryptTXT(key []byte, rrType *rc0.RRSetChange) {} + +func (m *mockRRSetService) DecryptTXT(key []byte, rrType *rc0.RRType) {} + +/* mocking ZoneManagementServiceInterface */ + +func (m *mockZoneManagementService) List(options *rc0.ListOptions) ([]*rc0.Zone, *rc0.Page, error) { + + if m.TestNilZonesReturned { + return nil, nil, nil + } + + if m.TestErrorReturned { + return nil, nil, fmt.Errorf("operation Zone.List failed") + } + + zones := []*rc0.Zone{ + { + Domain: testZoneOne, + Type: "SLAVE", + // "dnssec": "yes", @todo: add this + // "created": "2018-04-09T09:27:31Z", @todo: add this + LastCheck: "", + Serial: 20180411, + Masters: []string{ + "193.0.2.2", + "2001:db8::2", + }, + }, + { + Domain: testZoneTwo, + Type: "MASTER", + // "dnssec": "no", @todo: add this + // "created": "2019-01-15T13:20:10Z", @todo: add this + LastCheck: "", + Serial: 2019011616, + Masters: []string{ + "", + }, + }, + } + + return zones, nil, nil +} + +func (m *mockZoneManagementService) Get(zone string) (*rc0.Zone, error) { return nil, nil } +func (m *mockZoneManagementService) Create(zoneCreate *rc0.ZoneCreate) (*rc0.StatusResponse, error) { return nil, nil } +func (m *mockZoneManagementService) Edit(zone string, zoneEdit *rc0.ZoneEdit) (*rc0.StatusResponse, error) { return nil, nil } +func (m *mockZoneManagementService) Delete(zone string) (*rc0.StatusResponse, error) { return nil, nil } +func (m *mockZoneManagementService) Transfer(zone string) (*rc0.StatusResponse, error) { return nil ,nil } + From e7f90743e2733699dba8e771e9851dcc2018ed8d Mon Sep 17 00:00:00 2001 From: Dimitrij Klesev Date: Mon, 28 Jan 2019 15:26:56 +0100 Subject: [PATCH 053/136] Apply doc review changes --- Gopkg.lock | 25 ++++++++++++++++ Gopkg.toml | 4 +++ docs/tutorials/rcodezero.md | 58 ++++++++++++++++++++----------------- 3 files changed, 61 insertions(+), 26 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index df3e200fa..dab7e4a09 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -483,6 +483,14 @@ revision = "5a2b9fab83ff0f8bfc99684bd5f43a37abe560f1" version = "v1.0.8" +[[projects]] + digest = "1:bcc46a0fbd9e933087bef394871256b5c60269575bb661935874729c65bbbf60" + name = "github.com/mitchellh/mapstructure" + packages = ["."] + pruneopts = "" + revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe" + version = "v1.1.2" + [[projects]] digest = "1:0c0ff2a89c1bb0d01887e1dac043ad7efbf3ec77482ef058ac423d13497e16fd" name = "github.com/modern-go/concurrent" @@ -515,6 +523,14 @@ revision = "cdd946344b54bdf7dbeac406c2f1fe93150f08ea" version = "v0.6.0" +[[projects]] + digest = "1:d0b493d855fe23836a334940271745a43587f20b2fab4bb7c4ff0add1adcbcd4" + name = "github.com/nic-at/rc0go" + packages = ["."] + pruneopts = "" + revision = "d5560d32f90d7722d154a4e0d3b797d5e0ae0b68" + version = "v1.1.0" + [[projects]] digest = "1:70df8e71a953626770223d4982801fa73e7e99cbfcca068b95127f72af9b9edd" name = "github.com/oracle/oci-go-sdk" @@ -872,6 +888,14 @@ revision = "3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4" version = "v0.9.0" +[[projects]] + digest = "1:ecbf853ae69b7b38a5039077c10892c9798bd5345c73902026622a65a94ea501" + name = "gopkg.in/resty.v1" + packages = ["."] + pruneopts = "" + revision = "03c09fa32a21b7b27b8dbb3877826c1ab3d2daa2" + version = "v1.11.0" + [[projects]] digest = "1:f0620375dd1f6251d9973b5f2596228cc8042e887cd7f827e4220bc1ce8c30e2" name = "gopkg.in/yaml.v2" @@ -1165,6 +1189,7 @@ "github.com/linode/linodego", "github.com/miekg/dns", "github.com/nesv/go-dynect/dynect", + "github.com/nic-at/rc0go", "github.com/oracle/oci-go-sdk/common", "github.com/oracle/oci-go-sdk/dns", "github.com/pkg/errors", diff --git a/Gopkg.toml b/Gopkg.toml index 6590be2e0..05fcda662 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -119,3 +119,7 @@ ignored = ["github.com/kubernetes/repo-infra/kazel"] [[constraint]] name = "github.com/sanyu/dynectsoap" branch = "master" + +[[constraint]] + name = "github.com/nic-at/rc0go" + version = "1.1.0" diff --git a/docs/tutorials/rcodezero.md b/docs/tutorials/rcodezero.md index 9db91f65d..97bba6ce8 100644 --- a/docs/tutorials/rcodezero.md +++ b/docs/tutorials/rcodezero.md @@ -1,25 +1,37 @@ # Setting up ExternalDNS for Services on RcodeZero -This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using RcodeZero Anycast DNS. +This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using [RcodeZero Anycast DNS](https://www.rcodezero.at). Make sure to use **>=0.5.0** version of ExternalDNS for this tutorial. -Make sure to use **>=0.5.0** version of ExternalDNS for this tutorial. +The following steps are required to use RcodeZero with ExternalDNS: + +1. Sign up for an RcodeZero account (or use an existing account). +2. Add your zone to the RcodeZero DNS +3. Enable the RcodeZero API, and generate an API key. +4. Deploy ExternalDNS to use the RcodeZero provider. +5. Verify the setup bey deploying a test services (optional) ## Creating a RcodeZero DNS zone -After logging into RcodeZero Dashboard add a master domain under [RcodeZero Add Zone](https://my.rcodezero.at/domain/create). Use it throughout this guide (substitute example.com). +Before records can be added to your domain name automatically, you need to add your domain name to the set of zones managed by RcodeZero. In order to add the zone, perform the following steps: -## Creating RcodeZero Credentials +1. Log in to the RcodeZero Dashboard, and move to the [Add Zone](https://my.rcodezero.at/domain/create) page. +2. Select "MASTER" as domain type, and add your domain name there. Use this domain name instead of "example.com" throughout the rest of this tutorial. + +Note that "SECONDARY" domains cannot be managed by ExternalDNS, because this would not allow modification of records in the zone. + +## Enable the API, and create Credentials > The RcodeZero Anycast-Network is provisioned via web interface or REST-API. -RcodeZero API can be enabled and a key generated on [RcodeZero API](https://my.rcodezero.at/enableapi) - -The environment var `RC0_API_KEY` will be needed to run ExternalDNS with RcodeZero. +Enable the RcodeZero API to generate an API key on [RcodeZero API](https://my.rcodezero.at/enableapi). The API key will be added to the environment variable 'RC0_API_KEY' via one of the Manifest templates (as described below). ## Deploy ExternalDNS -Connect your `kubectl` client to the cluster you want to test ExternalDNS with. -Then apply one of the following manifests file to deploy ExternalDNS. +Connect your `kubectl` client to the cluster you want to test ExternalDNS with. Choose a Manifest from below, depending on whether or not you have RBAC enabled. Before applying it, modify the Manifest as follows: + +- Replace "example.com" with the domain name you added to RcodeZero. +- Replace YOUR_RCODEZERO_API_KEY with the API key created above. +- Replace YOUR_ENCRYPTION_KEY_STRING with a string to encrypt the TXT records ### Manifest (for clusters without RBAC enabled) @@ -120,6 +132,8 @@ spec: ## Deploying an Nginx Service +After you have deployed ExternalDNS with RcodeZero, you can deploy a simple service based on Nginx to test the setup. This is optional, though highly recommended before using ExternalDNS in production. + Create a service file called 'nginx.yaml' with the following contents: ```yaml @@ -156,37 +170,29 @@ spec: targetPort: 80 ``` -Note the annotation on the service; use the same hostname as the RcodeZero DNS zone created above. The annotation may also be a subdomain +Change the file as follows: + +- Replace the annotation of the service; use the same hostname as the RcodeZero DNS zone created above. The annotation may also be a subdomain of the DNS zone (e.g. 'www.example.com'). +- Set the TTL annotation of the service. A valid TTL of 120 or above must be given. This annotation is optional, and defaults to "300" if no value is given. -By setting the TTL annotation on the service, you have to pass a valid TTL, which must be 120 or above. -This annotation is optional, if you won't set it, it will be 1 (automatic) which is 300. +These annotations will be used to determine what services should be registered with DNS. Removing these annotations will cause ExternalDNS to remove the corresponding DNS records. -ExternalDNS uses this annotation to determine what services should be registered with DNS. Removing the annotation -will cause ExternalDNS to remove the corresponding DNS records. - -Create the deployment and service: +Create the Deployment and Service: ``` $ kubectl create -f nginx.yaml ``` -Depending where you run your service it can take a little while for your cloud provider to create an external IP for the service. - -Once the service has an external IP assigned, ExternalDNS will notice the new service IP address and synchronize -the RcodeZero DNS records. +Depending on your cloud provider, it might take a while to create an external IP for the service. Once an external IP address is assigned to the service, ExternalDNS will notice the new address and synchronize the RcodeZero DNS records accordingly. ## Verifying RcodeZero DNS records -Check your [RcodeZero Configured Zones](https://my.rcodezero.at/domain) and select the ExternalDNS managed domain. - -Substitute the zone for the one created above if a different domain was used. - -This should show the external IP address of the service as the A record for your domain. +Check your [RcodeZero Configured Zones](https://my.rcodezero.at/domain) and select the respective zone name. The zone should now contain the external IP address of the service as an A record. ## Cleanup -Now that we have verified that ExternalDNS will automatically manage RcodeZero DNS records, we can delete the tutorial's example: +Once you have verified that ExternalDNS successfully manages RcodeZero DNS records for external services, you can delete the tutorial example as follows: ``` $ kubectl delete -f nginx.yaml From 86a3ddb3d4a5ddd01567daa34fbe88be4a1fdc75 Mon Sep 17 00:00:00 2001 From: Dimitrij Klesev Date: Mon, 28 Jan 2019 16:00:35 +0100 Subject: [PATCH 054/136] Fix formating Fix linter issues --- provider/rcode0.go | 36 +-- provider/rcode0_test.go | 519 ++++++++++++++++++++-------------------- 2 files changed, 279 insertions(+), 276 deletions(-) diff --git a/provider/rcode0.go b/provider/rcode0.go index 1b1ff9b63..07533ebd9 100644 --- a/provider/rcode0.go +++ b/provider/rcode0.go @@ -33,14 +33,14 @@ type RcodeZeroProvider struct { DomainFilter DomainFilter DryRun bool - TXTEncrypt bool - Key []byte + TXTEncrypt bool + Key []byte } // NewRcodeZeroProvider creates a new RcodeZero Anycast DNS provider. // // Returns the provider or an error if a provider could not be created. -func NewRcodeZeroProvider(domainFilter DomainFilter, dryRun bool, txtEnc bool) (*RcodeZeroProvider, error){ +func NewRcodeZeroProvider(domainFilter DomainFilter, dryRun bool, txtEnc bool) (*RcodeZeroProvider, error) { client, err := rc0.NewClient(os.Getenv("RC0_API_KEY")) @@ -71,7 +71,7 @@ func NewRcodeZeroProvider(domainFilter DomainFilter, dryRun bool, txtEnc bool) ( return provider, nil } -// Returns filtered zones if filter is set +// Zones returns filtered zones if filter is set func (p *RcodeZeroProvider) Zones() ([]*rc0.Zone, error) { var result []*rc0.Zone @@ -90,7 +90,7 @@ func (p *RcodeZeroProvider) Zones() ([]*rc0.Zone, error) { return result, nil } -// Returns resource records +// Records returns resource records // // Decrypts TXT records if TXT-Encrypt flag is set and key is provided func (p *RcodeZeroProvider) Records() ([]*endpoint.Endpoint, error) { @@ -144,7 +144,7 @@ func (p *RcodeZeroProvider) ApplyChanges(changes *plan.Changes) error { combinedChanges := make([]*rc0.RRSetChange, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete)) - combinedChanges = append(combinedChanges, p.NewRcodezeroChanges(rc0.ChangeTypeADD, changes.Create)...) + combinedChanges = append(combinedChanges, p.NewRcodezeroChanges(rc0.ChangeTypeADD, changes.Create)...) combinedChanges = append(combinedChanges, p.NewRcodezeroChanges(rc0.ChangeTypeUPDATE, changes.UpdateNew)...) combinedChanges = append(combinedChanges, p.NewRcodezeroChanges(rc0.ChangeTypeDELETE, changes.Delete)...) @@ -245,11 +245,11 @@ func (p *RcodeZeroProvider) submitChanges(changes []*rc0.RRSetChange) error { for _, change := range changes { logFields := log.Fields{ - "record" : change.Name, - "content" : change.Records[0].Content, - "type" : change.Type, - "action" : change.ChangeType, - "zone" : zoneName, + "record": change.Name, + "content": change.Records[0].Content, + "type": change.Type, + "action": change.ChangeType, + "zone": zoneName, } log.WithFields(logFields).Info("Changing record.") @@ -303,7 +303,7 @@ func (p *RcodeZeroProvider) submitChanges(changes []*rc0.RRSetChange) error { return nil } -// Returns a RcodeZero specific array with rrset change objects. +// NewRcodezeroChanges returns a RcodeZero specific array with rrset change objects. func (p *RcodeZeroProvider) NewRcodezeroChanges(action string, endpoints []*endpoint.Endpoint) []*rc0.RRSetChange { changes := make([]*rc0.RRSetChange, 0, len(endpoints)) @@ -315,16 +315,16 @@ func (p *RcodeZeroProvider) NewRcodezeroChanges(action string, endpoints []*endp return changes } -// Returns a RcodeZero specific rrset change object. +// NewRcodezeroChange returns a RcodeZero specific rrset change object. func (p *RcodeZeroProvider) NewRcodezeroChange(action string, endpoint *endpoint.Endpoint) *rc0.RRSetChange { change := &rc0.RRSetChange{ - Type: endpoint.RecordType, + Type: endpoint.RecordType, ChangeType: action, - Name: endpoint.DNSName, - Records: []*rc0.Record{{ + Name: endpoint.DNSName, + Records: []*rc0.Record{{ Disabled: false, - Content: endpoint.Targets[0], + Content: endpoint.Targets[0], }}, } @@ -333,4 +333,4 @@ func (p *RcodeZeroProvider) NewRcodezeroChange(action string, endpoint *endpoint } return change -} \ No newline at end of file +} diff --git a/provider/rcode0_test.go b/provider/rcode0_test.go index d848ad006..73c312a7a 100644 --- a/provider/rcode0_test.go +++ b/provider/rcode0_test.go @@ -17,257 +17,255 @@ limitations under the License. package provider import ( - "fmt" - "github.com/kubernetes-incubator/external-dns/endpoint" - "github.com/kubernetes-incubator/external-dns/plan" - rc0 "github.com/nic-at/rc0go" - "github.com/stretchr/testify/require" - "os" - "testing" + "fmt" + "github.com/kubernetes-incubator/external-dns/endpoint" + "github.com/kubernetes-incubator/external-dns/plan" + rc0 "github.com/nic-at/rc0go" + "github.com/stretchr/testify/require" + "os" + "testing" ) const ( - testZoneOne = "testzone1.at" - testZoneTwo = "testzone2.at" - - rrsetChangesUnsupportedChangeType = 0 + testZoneOne = "testzone1.at" + testZoneTwo = "testzone2.at" + rrsetChangesUnsupportedChangeType = 0 ) type mockRcodeZeroClient rc0.Client type mockZoneManagementService struct { - TestNilZonesReturned bool - TestErrorReturned bool + TestNilZonesReturned bool + TestErrorReturned bool } type mockRRSetService struct { - TestErrorReturned bool + TestErrorReturned bool } func (m *mockRcodeZeroClient) resetMockServices() { - m.Zones = &mockZoneManagementService{} - m.RRSet = &mockRRSetService{} + m.Zones = &mockZoneManagementService{} + m.RRSet = &mockRRSetService{} } func (m *mockZoneManagementService) resetTestConditions() { - m.TestNilZonesReturned = false - m.TestErrorReturned = false + m.TestNilZonesReturned = false + m.TestErrorReturned = false } func (m *mockRRSetService) resetTestConditions() { - m.TestErrorReturned = false + m.TestErrorReturned = false } func TestRcodeZeroProvider_Records(t *testing.T) { - mockRRSetService := &mockRRSetService{} - mockZoneManagementService := &mockZoneManagementService{} + mockRRSetService := &mockRRSetService{} + mockZoneManagementService := &mockZoneManagementService{} - provider := &RcodeZeroProvider{ - Client: (*rc0.Client)(&mockRcodeZeroClient{ - Zones: mockZoneManagementService, - RRSet: mockRRSetService, - }), - } + provider := &RcodeZeroProvider{ + Client: (*rc0.Client)(&mockRcodeZeroClient{ + Zones: mockZoneManagementService, + RRSet: mockRRSetService, + }), + } - endpoints, err := provider.Records() // should return 6 rrs + endpoints, err := provider.Records() // should return 6 rrs - if err != nil { - t.Errorf("should not fail, %s", err) - } - require.Equal(t, 6, len(endpoints)) + if err != nil { + t.Errorf("should not fail, %s", err) + } + require.Equal(t, 6, len(endpoints)) - mockRRSetService.TestErrorReturned = true + mockRRSetService.TestErrorReturned = true - _, err = provider.Records() - if err == nil { - t.Errorf("expected to fail, %s", err) - } + _, err = provider.Records() + if err == nil { + t.Errorf("expected to fail, %s", err) + } } func TestRcodeZeroProvider_ApplyChanges(t *testing.T) { - mockRRSetService := &mockRRSetService{} - mockZoneManagementService := &mockZoneManagementService{} + mockRRSetService := &mockRRSetService{} + mockZoneManagementService := &mockZoneManagementService{} - provider := &RcodeZeroProvider{ - Client: (*rc0.Client)(&mockRcodeZeroClient{ - Zones: mockZoneManagementService, - RRSet: mockRRSetService, - }), - DomainFilter: NewDomainFilter([]string{testZoneOne}), - } + provider := &RcodeZeroProvider{ + Client: (*rc0.Client)(&mockRcodeZeroClient{ + Zones: mockZoneManagementService, + RRSet: mockRRSetService, + }), + DomainFilter: NewDomainFilter([]string{testZoneOne}), + } - changes := mockChanges() + changes := mockChanges() - err := provider.ApplyChanges(changes) + err := provider.ApplyChanges(changes) - if err != nil { - t.Errorf("should not fail, %s", err) - } + if err != nil { + t.Errorf("should not fail, %s", err) + } } func TestRcodeZeroProvider_NewRcodezeroChanges(t *testing.T) { - provider := &RcodeZeroProvider{} + provider := &RcodeZeroProvider{} - changes := mockChanges() + changes := mockChanges() - createChanges := provider.NewRcodezeroChanges(testZoneOne, changes.Create) - require.Equal(t, 4, len(createChanges)) + createChanges := provider.NewRcodezeroChanges(testZoneOne, changes.Create) + require.Equal(t, 4, len(createChanges)) - deleteChanges := provider.NewRcodezeroChanges(testZoneOne, changes.Delete) - require.Equal(t, 1, len(deleteChanges)) + deleteChanges := provider.NewRcodezeroChanges(testZoneOne, changes.Delete) + require.Equal(t, 1, len(deleteChanges)) - updateOldChanges := provider.NewRcodezeroChanges(testZoneOne, changes.UpdateOld) - require.Equal(t, 1, len(updateOldChanges)) + updateOldChanges := provider.NewRcodezeroChanges(testZoneOne, changes.UpdateOld) + require.Equal(t, 1, len(updateOldChanges)) - updateNewChanges := provider.NewRcodezeroChanges(testZoneOne, changes.UpdateNew) - require.Equal(t, 1, len(updateNewChanges)) + updateNewChanges := provider.NewRcodezeroChanges(testZoneOne, changes.UpdateNew) + require.Equal(t, 1, len(updateNewChanges)) } func TestRcodeZeroProvider_NewRcodezeroChange(t *testing.T) { - _endpoint := &endpoint.Endpoint{ - RecordType: "A", - DNSName: "app." + testZoneOne, - RecordTTL: 300, - Targets: endpoint.Targets{"target"}, - } + _endpoint := &endpoint.Endpoint{ + RecordType: "A", + DNSName: "app." + testZoneOne, + RecordTTL: 300, + Targets: endpoint.Targets{"target"}, + } - provider := &RcodeZeroProvider{} + provider := &RcodeZeroProvider{} - rrsetChange := provider.NewRcodezeroChange(testZoneOne, _endpoint) + rrsetChange := provider.NewRcodezeroChange(testZoneOne, _endpoint) - require.Equal(t, _endpoint.RecordType, rrsetChange.Type) - require.Equal(t, _endpoint.DNSName, rrsetChange.Name) - require.Equal(t, _endpoint.Targets[0], rrsetChange.Records[0].Content) - //require.Equal(t, endpoint.RecordTTL, rrsetChange.TTL) + require.Equal(t, _endpoint.RecordType, rrsetChange.Type) + require.Equal(t, _endpoint.DNSName, rrsetChange.Name) + require.Equal(t, _endpoint.Targets[0], rrsetChange.Records[0].Content) + //require.Equal(t, endpoint.RecordTTL, rrsetChange.TTL) } func Test_submitChanges(t *testing.T) { - mockRRSetService := &mockRRSetService{} - mockZoneManagementService := &mockZoneManagementService{} + mockRRSetService := &mockRRSetService{} + mockZoneManagementService := &mockZoneManagementService{} - provider := &RcodeZeroProvider{ - Client: (*rc0.Client)(&mockRcodeZeroClient{ - Zones: mockZoneManagementService, - RRSet: mockRRSetService, - }), - DomainFilter: NewDomainFilter([]string{testZoneOne}), - } + provider := &RcodeZeroProvider{ + Client: (*rc0.Client)(&mockRcodeZeroClient{ + Zones: mockZoneManagementService, + RRSet: mockRRSetService, + }), + DomainFilter: NewDomainFilter([]string{testZoneOne}), + } - changes := mockRRSetChanges(rrsetChangesUnsupportedChangeType) + changes := mockRRSetChanges(rrsetChangesUnsupportedChangeType) - err := provider.submitChanges(changes) + err := provider.submitChanges(changes) - if err == nil { - t.Errorf("expected to fail, %s", err) - } + if err == nil { + t.Errorf("expected to fail, %s", err) + } } func mockRRSetChanges(condition int) []*rc0.RRSetChange { - switch condition { - case rrsetChangesUnsupportedChangeType: - return []*rc0.RRSetChange{ - { - Name: testZoneOne, - Type: "A", - ChangeType: "UNSUPPORTED", - Records: []*rc0.Record{{Content:"fail"}}, - - }, - } - default: - return nil - } + switch condition { + case rrsetChangesUnsupportedChangeType: + return []*rc0.RRSetChange{ + { + Name: testZoneOne, + Type: "A", + ChangeType: "UNSUPPORTED", + Records: []*rc0.Record{{Content: "fail"}}, + }, + } + default: + return nil + } } func mockChanges() *plan.Changes { - changes := &plan.Changes{} + changes := &plan.Changes{} - changes.Create = []*endpoint.Endpoint{ - {DNSName: "new.ext-dns-test."+testZoneOne, Targets: endpoint.Targets{"target"}, RecordType: "A"}, - {DNSName: "new.ext-dns-test-with-ttl."+testZoneOne, Targets: endpoint.Targets{"target"}, RecordType: "A", RecordTTL: 100}, - {DNSName: "new.ext-dns-test.unexpected.com", Targets: endpoint.Targets{"target"}, RecordType: "AAAA"}, - {DNSName: testZoneOne, Targets: endpoint.Targets{"target"}, RecordType: "CNAME"}, - } - changes.Delete = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test."+testZoneOne, Targets: endpoint.Targets{"target"}}} - changes.UpdateOld = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test."+testZoneOne, Targets: endpoint.Targets{"target-old"}}} - changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test."+testZoneOne, Targets: endpoint.Targets{"target-new"}, RecordType: "CNAME", RecordTTL: 100}} + changes.Create = []*endpoint.Endpoint{ + {DNSName: "new.ext-dns-test." + testZoneOne, Targets: endpoint.Targets{"target"}, RecordType: "A"}, + {DNSName: "new.ext-dns-test-with-ttl." + testZoneOne, Targets: endpoint.Targets{"target"}, RecordType: "A", RecordTTL: 100}, + {DNSName: "new.ext-dns-test.unexpected.com", Targets: endpoint.Targets{"target"}, RecordType: "AAAA"}, + {DNSName: testZoneOne, Targets: endpoint.Targets{"target"}, RecordType: "CNAME"}, + } + changes.Delete = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test." + testZoneOne, Targets: endpoint.Targets{"target"}}} + changes.UpdateOld = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test." + testZoneOne, Targets: endpoint.Targets{"target-old"}}} + changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test." + testZoneOne, Targets: endpoint.Targets{"target-new"}, RecordType: "CNAME", RecordTTL: 100}} - return changes + return changes } func TestRcodeZeroProvider_Zones(t *testing.T) { - mockRRSetService := &mockRRSetService{} - mockZoneManagementService := &mockZoneManagementService{} + mockRRSetService := &mockRRSetService{} + mockZoneManagementService := &mockZoneManagementService{} - provider := &RcodeZeroProvider{ - Client: (*rc0.Client)(&mockRcodeZeroClient{ - Zones: mockZoneManagementService, - RRSet: mockRRSetService, - }), - } + provider := &RcodeZeroProvider{ + Client: (*rc0.Client)(&mockRcodeZeroClient{ + Zones: mockZoneManagementService, + RRSet: mockRRSetService, + }), + } - mockZoneManagementService.TestNilZonesReturned = true + mockZoneManagementService.TestNilZonesReturned = true - zones, err := provider.Zones() + zones, err := provider.Zones() - if err != nil { - t.Fatal(err) - } - require.Equal(t, 0, len(zones)) - mockZoneManagementService.resetTestConditions() + if err != nil { + t.Fatal(err) + } + require.Equal(t, 0, len(zones)) + mockZoneManagementService.resetTestConditions() - mockZoneManagementService.TestErrorReturned = true + mockZoneManagementService.TestErrorReturned = true - zones, err = provider.Zones() - if err == nil { - t.Errorf("expected to fail, %s", err) - } + zones, err = provider.Zones() + if err == nil { + t.Errorf("expected to fail, %s", err) + } } func TestNewRcodeZeroProvider(t *testing.T) { - _ = os.Setenv("RC0_API_KEY", "123") - p, err := NewRcodeZeroProvider(NewDomainFilter([]string{"ext-dns-test."+testZoneOne+"."}), true, true) + _ = os.Setenv("RC0_API_KEY", "123") + p, err := NewRcodeZeroProvider(NewDomainFilter([]string{"ext-dns-test." + testZoneOne + "."}), true, true) - if err != nil { - t.Errorf("should not fail, %s", err) - } + if err != nil { + t.Errorf("should not fail, %s", err) + } - require.Equal(t, true, p.DryRun) - require.Equal(t, true, p.TXTEncrypt) - require.Equal(t, true, p.DomainFilter.IsConfigured()) - require.Equal(t, false, p.DomainFilter.Match("ext-dns-test."+testZoneTwo+".")) // filter is set, so it should match only provided domains + require.Equal(t, true, p.DryRun) + require.Equal(t, true, p.TXTEncrypt) + require.Equal(t, true, p.DomainFilter.IsConfigured()) + require.Equal(t, false, p.DomainFilter.Match("ext-dns-test."+testZoneTwo+".")) // filter is set, so it should match only provided domains - p, err = NewRcodeZeroProvider(DomainFilter{}, false, false) + p, err = NewRcodeZeroProvider(DomainFilter{}, false, false) - if err != nil { - t.Errorf("should not fail, %s", err) - } + if err != nil { + t.Errorf("should not fail, %s", err) + } - require.Equal(t, false, p.DryRun) - require.Equal(t, false, p.DomainFilter.IsConfigured()) - require.Equal(t, true, p.DomainFilter.Match("ext-dns-test."+testZoneOne+".")) // filter is not set, so it should match any + require.Equal(t, false, p.DryRun) + require.Equal(t, false, p.DomainFilter.IsConfigured()) + require.Equal(t, true, p.DomainFilter.Match("ext-dns-test."+testZoneOne+".")) // filter is not set, so it should match any - _ = os.Unsetenv("RC0_API_KEY") - _, err = NewRcodeZeroProvider(DomainFilter{}, false, false) + _ = os.Unsetenv("RC0_API_KEY") + _, err = NewRcodeZeroProvider(DomainFilter{}, false, false) - if err == nil { - t.Errorf("expected to fail") - } + if err == nil { + t.Errorf("expected to fail") + } } @@ -275,93 +273,93 @@ func TestNewRcodeZeroProvider(t *testing.T) { func (m *mockRRSetService) List(zone string, options *rc0.ListOptions) ([]*rc0.RRType, *rc0.Page, error) { - if m.TestErrorReturned { - return nil, nil, fmt.Errorf("operation RRSet.List failed") - } + if m.TestErrorReturned { + return nil, nil, fmt.Errorf("operation RRSet.List failed") + } - return mockRRSet(zone), nil, nil + return mockRRSet(zone), nil, nil } func mockRRSet(zone string) []*rc0.RRType { - return []*rc0.RRType{ - { - Name: "app."+zone+".", - Type: "TXT", - TTL: 300, - Records: []*rc0.Record{ - { - Content: "\"heritage=external-dns,external-dns/owner=default,external-dns/resource=ingress/default/app\"", - Disabled: false, - }, - }, - }, - { - Name: "app."+zone+".", - Type: "A", - TTL: 300, - Records: []*rc0.Record{ - { - Content: "127.0.0.1", - Disabled: false, - }, - }, - }, - { - Name: "www."+zone+".", - Type: "A", - TTL: 300, - Records: []*rc0.Record{ - { - Content: "127.0.0.1", - Disabled: false, - }, - }, - }, - { - Name: zone+".", - Type: "SOA", - TTL: 3600, - Records: []*rc0.Record{ - { - Content: "sec1.rcode0.net. rcodezero-soa.ipcom.at. 2019011616 10800 3600 604800 3600", - Disabled: false, - }, - }, - }, - { - Name: zone+".", - Type: "NS", - TTL: 3600, - Records: []*rc0.Record{ - { - Content: "sec2.rcode0.net.", - Disabled: false, - }, - { - Content: "sec1.rcode0.net.", - Disabled: false, - }, - }, - }, - } + return []*rc0.RRType{ + { + Name: "app." + zone + ".", + Type: "TXT", + TTL: 300, + Records: []*rc0.Record{ + { + Content: "\"heritage=external-dns,external-dns/owner=default,external-dns/resource=ingress/default/app\"", + Disabled: false, + }, + }, + }, + { + Name: "app." + zone + ".", + Type: "A", + TTL: 300, + Records: []*rc0.Record{ + { + Content: "127.0.0.1", + Disabled: false, + }, + }, + }, + { + Name: "www." + zone + ".", + Type: "A", + TTL: 300, + Records: []*rc0.Record{ + { + Content: "127.0.0.1", + Disabled: false, + }, + }, + }, + { + Name: zone + ".", + Type: "SOA", + TTL: 3600, + Records: []*rc0.Record{ + { + Content: "sec1.rcode0.net. rcodezero-soa.ipcom.at. 2019011616 10800 3600 604800 3600", + Disabled: false, + }, + }, + }, + { + Name: zone + ".", + Type: "NS", + TTL: 3600, + Records: []*rc0.Record{ + { + Content: "sec2.rcode0.net.", + Disabled: false, + }, + { + Content: "sec1.rcode0.net.", + Disabled: false, + }, + }, + }, + } } func (m *mockRRSetService) Create(zone string, rrsetCreate []*rc0.RRSetChange) (*rc0.StatusResponse, error) { - return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil + return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil } func (m *mockRRSetService) Edit(zone string, rrsetEdit []*rc0.RRSetChange) (*rc0.StatusResponse, error) { - return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil + return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil } func (m *mockRRSetService) Delete(zone string, rrsetDelete []*rc0.RRSetChange) (*rc0.StatusResponse, error) { - return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil + return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil } func (m *mockRRSetService) SubmitChangeSet(zone string, changeSet []*rc0.RRSetChange) (*rc0.StatusResponse, error) { - return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil + return &rc0.StatusResponse{Status: "ok", Message: "pass"}, nil } func (m *mockRRSetService) EncryptTXT(key []byte, rrType *rc0.RRSetChange) {} @@ -372,46 +370,51 @@ func (m *mockRRSetService) DecryptTXT(key []byte, rrType *rc0.RRType) {} func (m *mockZoneManagementService) List(options *rc0.ListOptions) ([]*rc0.Zone, *rc0.Page, error) { - if m.TestNilZonesReturned { - return nil, nil, nil - } + if m.TestNilZonesReturned { + return nil, nil, nil + } - if m.TestErrorReturned { - return nil, nil, fmt.Errorf("operation Zone.List failed") - } + if m.TestErrorReturned { + return nil, nil, fmt.Errorf("operation Zone.List failed") + } - zones := []*rc0.Zone{ - { - Domain: testZoneOne, - Type: "SLAVE", - // "dnssec": "yes", @todo: add this - // "created": "2018-04-09T09:27:31Z", @todo: add this - LastCheck: "", - Serial: 20180411, - Masters: []string{ - "193.0.2.2", - "2001:db8::2", - }, - }, - { - Domain: testZoneTwo, - Type: "MASTER", - // "dnssec": "no", @todo: add this - // "created": "2019-01-15T13:20:10Z", @todo: add this - LastCheck: "", - Serial: 2019011616, - Masters: []string{ - "", - }, - }, - } + zones := []*rc0.Zone{ + { + Domain: testZoneOne, + Type: "SLAVE", + // "dnssec": "yes", @todo: add this + // "created": "2018-04-09T09:27:31Z", @todo: add this + LastCheck: "", + Serial: 20180411, + Masters: []string{ + "193.0.2.2", + "2001:db8::2", + }, + }, + { + Domain: testZoneTwo, + Type: "MASTER", + // "dnssec": "no", @todo: add this + // "created": "2019-01-15T13:20:10Z", @todo: add this + LastCheck: "", + Serial: 2019011616, + Masters: []string{ + "", + }, + }, + } - return zones, nil, nil + return zones, nil, nil } func (m *mockZoneManagementService) Get(zone string) (*rc0.Zone, error) { return nil, nil } -func (m *mockZoneManagementService) Create(zoneCreate *rc0.ZoneCreate) (*rc0.StatusResponse, error) { return nil, nil } -func (m *mockZoneManagementService) Edit(zone string, zoneEdit *rc0.ZoneEdit) (*rc0.StatusResponse, error) { return nil, nil } +func (m *mockZoneManagementService) Create(zoneCreate *rc0.ZoneCreate) (*rc0.StatusResponse, error) { + return nil, nil +} +func (m *mockZoneManagementService) Edit(zone string, zoneEdit *rc0.ZoneEdit) (*rc0.StatusResponse, error) { + return nil, nil +} func (m *mockZoneManagementService) Delete(zone string) (*rc0.StatusResponse, error) { return nil, nil } -func (m *mockZoneManagementService) Transfer(zone string) (*rc0.StatusResponse, error) { return nil ,nil } - +func (m *mockZoneManagementService) Transfer(zone string) (*rc0.StatusResponse, error) { + return nil, nil +} From 6da894253e2411128504ecd166adeab64f291808 Mon Sep 17 00:00:00 2001 From: Dimitrij Klesev Date: Tue, 29 Jan 2019 12:03:06 +0100 Subject: [PATCH 055/136] Run gofmt on main --- main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index 8e00aad6f..6592cd6ac 100644 --- a/main.go +++ b/main.go @@ -132,9 +132,9 @@ func main() { p, err = provider.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.DryRun) case "cloudflare": p, err = provider.NewCloudFlareProvider(domainFilter, zoneIDFilter, cfg.CloudflareZonesPerPage, cfg.CloudflareProxied, cfg.DryRun) - case "rcodezero": - p, err = provider.NewRcodeZeroProvider(domainFilter, cfg.DryRun, cfg.RcodezeroTXTEncrypt) - case "google": + case "rcodezero": + p, err = provider.NewRcodeZeroProvider(domainFilter, cfg.DryRun, cfg.RcodezeroTXTEncrypt) + case "google": p, err = provider.NewGoogleProvider(cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.DryRun) case "digitalocean": p, err = provider.NewDigitalOceanProvider(domainFilter, cfg.DryRun) From 76b9b4c47261db051d21245c858dfc3d86684f70 Mon Sep 17 00:00:00 2001 From: Dimitrij Klesev Date: Tue, 19 Feb 2019 11:49:41 +0100 Subject: [PATCH 056/136] Trigger travis --- docs/tutorials/rcodezero.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorials/rcodezero.md b/docs/tutorials/rcodezero.md index 97bba6ce8..c408a96ba 100644 --- a/docs/tutorials/rcodezero.md +++ b/docs/tutorials/rcodezero.md @@ -180,7 +180,7 @@ These annotations will be used to determine what services should be registered w Create the Deployment and Service: -``` +```bash $ kubectl create -f nginx.yaml ``` @@ -194,7 +194,7 @@ Check your [RcodeZero Configured Zones](https://my.rcodezero.at/domain) and sele Once you have verified that ExternalDNS successfully manages RcodeZero DNS records for external services, you can delete the tutorial example as follows: -``` +```bash $ kubectl delete -f nginx.yaml $ kubectl delete -f externaldns.yaml ``` From 1eaf02d108fdfd19b44ad5805f27498a17cba175 Mon Sep 17 00:00:00 2001 From: Shota Ito Date: Fri, 22 Feb 2019 19:01:43 +0900 Subject: [PATCH 057/136] Added description for multiple dns name This PR is a comment about "Multiple DNS names per Service" setting. --- docs/tutorials/aws.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/tutorials/aws.md b/docs/tutorials/aws.md index 7056de801..61cefcccb 100644 --- a/docs/tutorials/aws.md +++ b/docs/tutorials/aws.md @@ -208,6 +208,8 @@ Create the following sample application to test that ExternalDNS works. > For services ExternalDNS will look for the annotation `external-dns.alpha.kubernetes.io/hostname` on the service and use the corresponding value. +> If you want to give multiple names to service, you can set it to external-dns.alpha.kubernetes.io/hostname with a comma separator. + ```yaml apiVersion: v1 kind: Service From eea13a85eca781c5f70182450a0232ccdc99e590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dirk=20Go=CC=81mez?= Date: Fri, 22 Feb 2019 17:22:55 +0100 Subject: [PATCH 058/136] Document make dep step which may be needed to run make build --- docs/contributing/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing/getting-started.md b/docs/contributing/getting-started.md index 9fe764c37..6976ebde9 100644 --- a/docs/contributing/getting-started.md +++ b/docs/contributing/getting-started.md @@ -2,7 +2,7 @@ ### Building -You can build ExternalDNS for your platform with `make build`. The binary will land at `build/external-dns`. +You can build ExternalDNS for your platform with `make build`, you may have to install the necessary dependencies with `sudo make dep`. The binary will land at `build/external-dns`. ### Design From 293efb27cffed1b715c7c14b60bcef093c000b97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dirk=20Go=CC=81mez?= Date: Fri, 22 Feb 2019 20:46:58 +0100 Subject: [PATCH 059/136] Turns out sudo is not necessary --- docs/contributing/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing/getting-started.md b/docs/contributing/getting-started.md index 6976ebde9..3ca84b425 100644 --- a/docs/contributing/getting-started.md +++ b/docs/contributing/getting-started.md @@ -2,7 +2,7 @@ ### Building -You can build ExternalDNS for your platform with `make build`, you may have to install the necessary dependencies with `sudo make dep`. The binary will land at `build/external-dns`. +You can build ExternalDNS for your platform with `make build`, you may have to install the necessary dependencies with `make dep`. The binary will land at `build/external-dns`. ### Design From 60a2083921e90c10247e84a40f91f71aaa1c9a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dirk=20Go=CC=81mez?= Date: Fri, 22 Feb 2019 20:52:50 +0100 Subject: [PATCH 060/136] Clarify that hosted zone identifier is to be used --- docs/tutorials/aws.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/tutorials/aws.md b/docs/tutorials/aws.md index 7056de801..5be840fd2 100644 --- a/docs/tutorials/aws.md +++ b/docs/tutorials/aws.md @@ -44,7 +44,7 @@ $ aws route53 create-hosted-zone --name "external-dns-test.my-org.com." --caller ``` -Make a note of the ID of the hosted zone you just created. +Make a note of the ID of the hosted zone you just created, which will serve as the value for my-hostedzone-identifier. ```console $ aws route53 list-hosted-zones-by-name --output json --dns-name "external-dns-test.my-org.com." | jq -r '.HostedZones[0].Id' @@ -92,7 +92,7 @@ spec: - --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization - --aws-zone-type=public # only look at public hosted zones (valid values are public, private or no value for both) - --registry=txt - - --txt-owner-id=my-identifier + - --txt-owner-id=my-hostedzone-identifier ``` ### Manifest (for clusters with RBAC enabled) @@ -158,7 +158,7 @@ spec: - --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization - --aws-zone-type=public # only look at public hosted zones (valid values are public, private or no value for both) - --registry=txt - - --txt-owner-id=my-identifier + - --txt-owner-id=my-hostedzone-identifier ``` @@ -264,7 +264,7 @@ $ aws route53 list-resource-record-sets --output json --hosted-zone-id "/hostedz "TTL": 300, "ResourceRecords": [ { - "Value": "\"heritage=external-dns,external-dns/owner=my-identifier\"" + "Value": "\"heritage=external-dns,external-dns/owner=my-hostedzone-identifier\"" } ], "Type": "TXT" From 73d34db6703a7357d82644c168cb58e9ab7d5f33 Mon Sep 17 00:00:00 2001 From: jlamillan Date: Thu, 21 Feb 2019 13:17:10 -0800 Subject: [PATCH 061/136] Use k8s informer cache instead of active API server calls in ingress and service sources. --- source/ingress.go | 56 +++++++++++++++++++------ source/ingress_test.go | 16 +++++++- source/service.go | 93 ++++++++++++++++++++++++++++++++++-------- source/service_test.go | 17 +++++++- source/shared_test.go | 12 +++++- 5 files changed, 160 insertions(+), 34 deletions(-) diff --git a/source/ingress.go b/source/ingress.go index f522b5242..163ea2886 100644 --- a/source/ingress.go +++ b/source/ingress.go @@ -23,14 +23,17 @@ import ( "strings" "text/template" + "github.com/kubernetes-incubator/external-dns/endpoint" log "github.com/sirupsen/logrus" - "k8s.io/api/extensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/wait" + kubeinformers "k8s.io/client-go/informers" + extinformers "k8s.io/client-go/informers/extensions/v1beta1" "k8s.io/client-go/kubernetes" - - "github.com/kubernetes-incubator/external-dns/endpoint" + "k8s.io/client-go/tools/cache" + "time" ) // ingressSource is an implementation of Source for Kubernetes ingress objects. @@ -44,6 +47,7 @@ type ingressSource struct { fqdnTemplate *template.Template combineFQDNAnnotation bool ignoreHostnameAnnotation bool + ingressInformer extinformers.IngressInformer } // NewIngressSource creates a new ingressSource with the given config. @@ -61,31 +65,57 @@ func NewIngressSource(kubeClient kubernetes.Interface, namespace, annotationFilt } } - return &ingressSource{ + // 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)) + ingressInformer := informerFactory.Extensions().V1beta1().Ingresses() + + // Add default resource event handlers to properly initialize informer. + ingressInformer.Informer().AddEventHandler( + cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + }, + }, + ) + + // TODO informer is not explicitly stopped since controller is not passing in its channel. + informerFactory.Start(wait.NeverStop) + + // wait for the local cache to be populated. + err = wait.Poll(time.Second, 60*time.Second, func() (bool, error) { + return ingressInformer.Informer().HasSynced() == true, nil + }) + if err != nil { + return nil, fmt.Errorf("failed to sync cache: %v", err) + } + + sc := &ingressSource{ client: kubeClient, namespace: namespace, annotationFilter: annotationFilter, fqdnTemplate: tmpl, combineFQDNAnnotation: combineFqdnAnnotation, ignoreHostnameAnnotation: ignoreHostnameAnnotation, - }, nil + ingressInformer: ingressInformer, + } + return sc, nil } // Endpoints returns endpoint objects for each host-target combination that should be processed. // Retrieves all ingress resources on all namespaces func (sc *ingressSource) Endpoints() ([]*endpoint.Endpoint, error) { - ingresses, err := sc.client.Extensions().Ingresses(sc.namespace).List(metav1.ListOptions{}) + ingresses, err := sc.ingressInformer.Lister().Ingresses(sc.namespace).List(labels.Everything()) if err != nil { return nil, err } - ingresses.Items, err = sc.filterByAnnotations(ingresses.Items) + ingresses, err = sc.filterByAnnotations(ingresses) if err != nil { return nil, err } endpoints := []*endpoint.Endpoint{} - for _, ing := range ingresses.Items { + for _, ing := range ingresses { // Check controller annotation to see if we are responsible. controller, ok := ing.Annotations[controllerAnnotationKey] if ok && controller != controllerAnnotationValue { @@ -94,11 +124,11 @@ func (sc *ingressSource) Endpoints() ([]*endpoint.Endpoint, error) { continue } - ingEndpoints := endpointsFromIngress(&ing, sc.ignoreHostnameAnnotation) + ingEndpoints := endpointsFromIngress(ing, sc.ignoreHostnameAnnotation) // apply template if host is missing on ingress if (sc.combineFQDNAnnotation || len(ingEndpoints) == 0) && sc.fqdnTemplate != nil { - iEndpoints, err := sc.endpointsFromTemplate(&ing) + iEndpoints, err := sc.endpointsFromTemplate(ing) if err != nil { return nil, err } @@ -161,7 +191,7 @@ func (sc *ingressSource) endpointsFromTemplate(ing *v1beta1.Ingress) ([]*endpoin } // filterByAnnotations filters a list of ingresses by a given annotation selector. -func (sc *ingressSource) filterByAnnotations(ingresses []v1beta1.Ingress) ([]v1beta1.Ingress, error) { +func (sc *ingressSource) filterByAnnotations(ingresses []*v1beta1.Ingress) ([]*v1beta1.Ingress, error) { labelSelector, err := metav1.ParseToLabelSelector(sc.annotationFilter) if err != nil { return nil, err @@ -176,7 +206,7 @@ func (sc *ingressSource) filterByAnnotations(ingresses []v1beta1.Ingress) ([]v1b return ingresses, nil } - filteredList := []v1beta1.Ingress{} + filteredList := []*v1beta1.Ingress{} for _, ingress := range ingresses { // convert the ingress' annotations to an equivalent label selector @@ -191,7 +221,7 @@ func (sc *ingressSource) filterByAnnotations(ingresses []v1beta1.Ingress) ([]v1b return filteredList, nil } -func (sc *ingressSource) setResourceLabel(ingress v1beta1.Ingress, endpoints []*endpoint.Endpoint) { +func (sc *ingressSource) setResourceLabel(ingress *v1beta1.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/ingress_test.go b/source/ingress_test.go index 2f29f8020..c9b9164de 100644 --- a/source/ingress_test.go +++ b/source/ingress_test.go @@ -18,10 +18,12 @@ package source import ( "testing" + "time" "k8s.io/api/core/v1" "k8s.io/api/extensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes/fake" "github.com/kubernetes-incubator/external-dns/endpoint" @@ -990,7 +992,19 @@ func testIngressEndpoints(t *testing.T) { require.NoError(t, err) } - res, err := ingressSource.Endpoints() + var res []*endpoint.Endpoint + var err error + + // wait up to a few seconds for new resources to appear in informer cache. + err = wait.Poll(time.Second, 3*time.Second, func() (bool, error) { + res, err = ingressSource.Endpoints() + if err != nil { + // stop waiting if we get an error + return true, err + } + return len(res) >= len(ti.expected), nil + }) + if ti.expectError { assert.Error(t, err) } else { diff --git a/source/service.go b/source/service.go index 67b12c69d..4c26bb5bf 100644 --- a/source/service.go +++ b/source/service.go @@ -19,6 +19,9 @@ package source import ( "bytes" "fmt" + kubeinformers "k8s.io/client-go/informers" + coreinformers "k8s.io/client-go/informers/core/v1" + "k8s.io/client-go/tools/cache" "sort" "strings" "text/template" @@ -29,9 +32,11 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" "github.com/kubernetes-incubator/external-dns/endpoint" + "time" ) const ( @@ -54,6 +59,9 @@ type serviceSource struct { ignoreHostnameAnnotation bool publishInternal bool publishHostIP bool + serviceInformer coreinformers.ServiceInformer + podInformer coreinformers.PodInformer + nodeInformer coreinformers.NodeInformer serviceTypeFilter map[string]struct{} } @@ -72,6 +80,47 @@ func NewServiceSource(kubeClient kubernetes.Interface, namespace, annotationFilt } } + // Use shared informers to listen for add/update/delete of services/pods/nodes in the specified namespace. + // Set resync period to 0, to prevent processing when nothing has changed + informerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 0, kubeinformers.WithNamespace(namespace)) + serviceInformer := informerFactory.Core().V1().Services() + podInformer := informerFactory.Core().V1().Pods() + nodeInformer := informerFactory.Core().V1().Nodes() + + // Add default resource event handlers to properly initialize informer. + serviceInformer.Informer().AddEventHandler( + cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + log.Debug("service added") + }, + }, + ) + podInformer.Informer().AddEventHandler( + cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + log.Debug("pod added") + }, + }, + ) + nodeInformer.Informer().AddEventHandler( + cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + log.Debug("node added") + }, + }, + ) + + // TODO informer is not explicitly stopped since controller is not passing in its channel. + informerFactory.Start(wait.NeverStop) + + // wait for the local cache to be populated. + err = wait.Poll(time.Second, 60*time.Second, func() (bool, error) { + return serviceInformer.Informer().HasSynced() == true, nil + }) + if err != nil { + return nil, fmt.Errorf("failed to sync cache: %v", err) + } + // Transform the slice into a map so it will // be way much easier and fast to filter later serviceTypes := make(map[string]struct{}) @@ -89,24 +138,27 @@ func NewServiceSource(kubeClient kubernetes.Interface, namespace, annotationFilt ignoreHostnameAnnotation: ignoreHostnameAnnotation, publishInternal: publishInternal, publishHostIP: publishHostIP, + serviceInformer: serviceInformer, + podInformer: podInformer, + nodeInformer: nodeInformer, serviceTypeFilter: serviceTypes, }, nil } // Endpoints returns endpoint objects for each service that should be processed. func (sc *serviceSource) Endpoints() ([]*endpoint.Endpoint, error) { - services, err := sc.client.CoreV1().Services(sc.namespace).List(metav1.ListOptions{}) + services, err := sc.serviceInformer.Lister().Services(sc.namespace).List(labels.Everything()) if err != nil { return nil, err } - services.Items, err = sc.filterByAnnotations(services.Items) + services, err = sc.filterByAnnotations(services) if err != nil { return nil, err } // filter on service types if at least one has been provided if len(sc.serviceTypeFilter) > 0 { - services.Items = sc.filterByServiceType(services.Items) + services = sc.filterByServiceType(services) } // get the ip addresses of all the nodes and cache them for this run @@ -117,7 +169,7 @@ func (sc *serviceSource) Endpoints() ([]*endpoint.Endpoint, error) { endpoints := []*endpoint.Endpoint{} - for _, svc := range services.Items { + for _, svc := range services { // Check controller annotation to see if we are responsible. controller, ok := svc.Annotations[controllerAnnotationKey] if ok && controller != controllerAnnotationValue { @@ -126,16 +178,16 @@ func (sc *serviceSource) Endpoints() ([]*endpoint.Endpoint, error) { continue } - svcEndpoints := sc.endpoints(&svc, nodeTargets) + svcEndpoints := sc.endpoints(svc, nodeTargets) // process legacy annotations if no endpoints were returned and compatibility mode is enabled. if len(svcEndpoints) == 0 && sc.compatibility != "" { - svcEndpoints = legacyEndpointsFromService(&svc, sc.compatibility) + svcEndpoints = legacyEndpointsFromService(svc, sc.compatibility) } // apply template if none of the above is found if (sc.combineFQDNAnnotation || len(svcEndpoints) == 0) && sc.fqdnTemplate != nil { - sEndpoints, err := sc.endpointsFromTemplate(&svc, nodeTargets) + sEndpoints, err := sc.endpointsFromTemplate(svc, nodeTargets) if err != nil { return nil, err } @@ -167,14 +219,23 @@ func (sc *serviceSource) Endpoints() ([]*endpoint.Endpoint, error) { func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname string, ttl endpoint.TTL) []*endpoint.Endpoint { var endpoints []*endpoint.Endpoint - pods, err := sc.client.CoreV1().Pods(svc.Namespace).List(metav1.ListOptions{LabelSelector: labels.Set(svc.Spec.Selector).AsSelectorPreValidated().String()}) + labelSelector, err := metav1.ParseToLabelSelector(labels.Set(svc.Spec.Selector).AsSelectorPreValidated().String()) + if err != nil { + return nil + } + selector, err := metav1.LabelSelectorAsSelector(labelSelector) + if err != nil { + return nil + } + + pods, err := sc.podInformer.Lister().Pods(svc.Namespace).List(selector) if err != nil { log.Errorf("List Pods of service[%s] error:%v", svc.GetName(), err) return endpoints } targetsByHeadlessDomain := make(map[string][]string) - for _, v := range pods.Items { + for _, v := range pods { headlessDomain := hostname if v.Spec.Hostname != "" { headlessDomain = v.Spec.Hostname + "." + headlessDomain @@ -251,7 +312,7 @@ func (sc *serviceSource) endpoints(svc *v1.Service, nodeTargets endpoint.Targets } // filterByAnnotations filters a list of services by a given annotation selector. -func (sc *serviceSource) filterByAnnotations(services []v1.Service) ([]v1.Service, error) { +func (sc *serviceSource) filterByAnnotations(services []*v1.Service) ([]*v1.Service, error) { labelSelector, err := metav1.ParseToLabelSelector(sc.annotationFilter) if err != nil { return nil, err @@ -266,7 +327,7 @@ func (sc *serviceSource) filterByAnnotations(services []v1.Service) ([]v1.Servic return services, nil } - filteredList := []v1.Service{} + filteredList := []*v1.Service{} for _, service := range services { // convert the service's annotations to an equivalent label selector @@ -282,8 +343,8 @@ func (sc *serviceSource) filterByAnnotations(services []v1.Service) ([]v1.Servic } // filterByServiceType filters services according their types -func (sc *serviceSource) filterByServiceType(services []v1.Service) []v1.Service { - filteredList := []v1.Service{} +func (sc *serviceSource) filterByServiceType(services []*v1.Service) []*v1.Service { + filteredList := []*v1.Service{} for _, service := range services { // Check if the service is of the given type or not if _, ok := sc.serviceTypeFilter[string(service.Spec.Type)]; ok { @@ -294,7 +355,7 @@ func (sc *serviceSource) filterByServiceType(services []v1.Service) []v1.Service return filteredList } -func (sc *serviceSource) setResourceLabel(service v1.Service, endpoints []*endpoint.Endpoint) { +func (sc *serviceSource) setResourceLabel(service *v1.Service, endpoints []*endpoint.Endpoint) { for _, ep := range endpoints { ep.Labels[endpoint.ResourceLabelKey] = fmt.Sprintf("service/%s/%s", service.Namespace, service.Name) } @@ -392,7 +453,7 @@ func (sc *serviceSource) extractNodeTargets() (endpoint.Targets, error) { externalIPs endpoint.Targets ) - nodes, err := sc.client.CoreV1().Nodes().List(metav1.ListOptions{}) + nodes, err := sc.nodeInformer.Lister().List(labels.Everything()) if err != nil { if errors.IsForbidden(err) { // Return an empty list because it makes sense to continue and try other sources. @@ -402,7 +463,7 @@ func (sc *serviceSource) extractNodeTargets() (endpoint.Targets, error) { return nil, err } - for _, node := range nodes.Items { + for _, node := range nodes { for _, address := range node.Status.Addresses { switch address.Type { case v1.NodeExternalIP: diff --git a/source/service_test.go b/source/service_test.go index acd6a2b96..6c8b1fc8f 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -17,8 +17,10 @@ limitations under the License. package source import ( + "k8s.io/apimachinery/pkg/util/wait" "net" "testing" + "time" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -1087,7 +1089,18 @@ func testServiceSourceEndpoints(t *testing.T) { ) require.NoError(t, err) - endpoints, err := client.Endpoints() + var res []*endpoint.Endpoint + + // wait up to a few seconds for new resources to appear in informer cache. + err = wait.Poll(time.Second, 3*time.Second, func() (bool, error) { + res, err = client.Endpoints() + if err != nil { + // stop waiting if we get an error + return true, err + } + return len(res) >= len(tc.expected), nil + }) + if tc.expectError { require.Error(t, err) } else { @@ -1095,7 +1108,7 @@ func testServiceSourceEndpoints(t *testing.T) { } // Validate returned endpoints against desired endpoints. - validateEndpoints(t, endpoints, tc.expected) + validateEndpoints(t, res, tc.expected) }) } } diff --git a/source/shared_test.go b/source/shared_test.go index aedd47106..fe27e626e 100644 --- a/source/shared_test.go +++ b/source/shared_test.go @@ -17,9 +17,10 @@ limitations under the License. package source import ( - "testing" - "github.com/kubernetes-incubator/external-dns/endpoint" + "sort" + "strings" + "testing" ) // test helper functions @@ -28,6 +29,13 @@ func validateEndpoints(t *testing.T, endpoints, expected []*endpoint.Endpoint) { if len(endpoints) != len(expected) { t.Fatalf("expected %d endpoints, got %d", len(expected), len(endpoints)) } + // Make sure endpoints are sorted - validateEndpoint() depends on it. + sort.SliceStable(endpoints, func(i, j int) bool { + return strings.Compare(endpoints[i].DNSName, endpoints[j].DNSName) < 0 + }) + sort.SliceStable(expected, func(i, j int) bool { + return strings.Compare(expected[i].DNSName, expected[j].DNSName) < 0 + }) for i := range endpoints { validateEndpoint(t, endpoints[i], expected[i]) From 142f0dac0e63a8eb11497b68154f9aace88ea98b Mon Sep 17 00:00:00 2001 From: njuettner Date: Wed, 27 Feb 2019 14:23:56 +0100 Subject: [PATCH 062/136] Changelog for v0.5.11 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 265f45991..cfc935ef1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## v0.5.11 - 2019-02-11 + + - Fix constant updating issue introduced with v0.5.10 (#886) @jhohertz + - Ignore evaluate target health for calculating changes for AWS (#880) @linki + - Pagination for cloudflare zones (#873) @njuettner + ## v0.5.10 - 2019-01-28 - Docs: Improve documentation regarding Alias (#868) @alexnederlof From 1f7eea71e6eded474f250143cae37257026ea900 Mon Sep 17 00:00:00 2001 From: vanhumbeecka Date: Mon, 11 Mar 2019 20:02:19 +0100 Subject: [PATCH 063/136] Update README.md Added a reference to a blogpost which uses ExternalDNS in a CI/CD setup. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d2058e41b..9b68de1d6 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ In a broader sense, ExternalDNS allows you to control DNS records dynamically vi The [FAQ](docs/faq.md) contains additional information and addresses several questions about key concepts of ExternalDNS. -To see ExternalDNS in action, have a look at this [video](https://www.youtube.com/watch?v=9HQ2XgL9YVI). +To see ExternalDNS in action, have a look at this [video](https://www.youtube.com/watch?v=9HQ2XgL9YVI) or read this [blogpost](https://medium.com/wearetheledger/deploying-test-environments-with-azure-devops-eks-and-externaldns-67abe647e4e). ## The Latest Release: v0.5 From 48f26ddac1f10b6287a9f67f5344c145f07f7fe7 Mon Sep 17 00:00:00 2001 From: njuettner Date: Thu, 14 Mar 2019 12:38:37 +0100 Subject: [PATCH 064/136] Dropping owners --- OWNERS | 5 ----- 1 file changed, 5 deletions(-) diff --git a/OWNERS b/OWNERS index 5c33fb7d6..f82ca6dfe 100644 --- a/OWNERS +++ b/OWNERS @@ -2,12 +2,7 @@ # https://github.com/kubernetes/kubernetes/blob/master/docs/devel/owners.md approvers: - - justinsb - hjacobs - raffo - linki - - ideahitme - - chrislovecnm - - kris-nova - - iterion - njuettner From 34bf630dedc9aec0340ba007deb5c1354d73b1cb Mon Sep 17 00:00:00 2001 From: Dimitrij Klesev Date: Thu, 14 Mar 2019 14:26:55 +0100 Subject: [PATCH 065/136] Fix rcodezero txt encrypt flag parameter Add rcodezero txt encrypt parameter tests --- pkg/apis/externaldns/types.go | 2 +- pkg/apis/externaldns/types_test.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 0db7b2452..3661f458c 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -273,7 +273,7 @@ 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("rc0-txt-enc", "When using the Rcodezero provider with txt registry option, set if TXT rrs are encrypted (default: false)").BoolVar(&cfg.RcodezeroTXTEncrypt) + 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("pdns-server", "When using the PowerDNS/PDNS provider, specify the URL to the pdns server (required when --provider=pdns)").Default(defaultConfig.PDNSServer).StringVar(&cfg.PDNSServer) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index 394e666c5..829065253 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -80,6 +80,7 @@ var ( ExoscaleAPISecret: "", CRDSourceAPIVersion: "externaldns.k8s.io/v1alpha1", CRDSourceKind: "DNSEndpoint", + RcodezeroTXTEncrypt: false, } overriddenConfig = &Config{ @@ -139,6 +140,7 @@ var ( ExoscaleAPISecret: "2", CRDSourceAPIVersion: "test.k8s.io/v1alpha1", CRDSourceKind: "Endpoint", + RcodezeroTXTEncrypt: true, } ) @@ -221,6 +223,7 @@ func TestParseFlags(t *testing.T) { "--exoscale-apisecret=2", "--crd-source-apiversion=test.k8s.io/v1alpha1", "--crd-source-kind=Endpoint", + "--rcodezero-txt-encrypt", }, envVars: map[string]string{}, expected: overriddenConfig, @@ -284,6 +287,7 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_EXOSCALE_APISECRET": "2", "EXTERNAL_DNS_CRD_SOURCE_APIVERSION": "test.k8s.io/v1alpha1", "EXTERNAL_DNS_CRD_SOURCE_KIND": "Endpoint", + "EXTERNAL_DNS_RCODEZERO_TXT_ENCRYPT": "1", }, expected: overriddenConfig, }, From 909e376387991f3852684ec708863812bbb16680 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 11 Feb 2019 16:46:36 +0100 Subject: [PATCH 066/136] Make view configurable for infoblox provider --- main.go | 1 + pkg/apis/externaldns/types.go | 3 +++ provider/infoblox.go | 10 ++++++++++ 3 files changed, 14 insertions(+) diff --git a/main.go b/main.go index 603a3ad5e..04ccb6a24 100644 --- a/main.go +++ b/main.go @@ -151,6 +151,7 @@ func main() { Password: cfg.InfobloxWapiPassword, Version: cfg.InfobloxWapiVersion, SSLVerify: cfg.InfobloxSSLVerify, + View: cfg.InfobloxView, DryRun: cfg.DryRun, }, ) diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index ecbcd480c..ca74cd572 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -73,6 +73,7 @@ type Config struct { InfobloxWapiPassword string InfobloxWapiVersion string InfobloxSSLVerify bool + InfobloxView string DynCustomerName string DynUsername string DynPassword string @@ -148,6 +149,7 @@ var defaultConfig = &Config{ InfobloxWapiPassword: "", InfobloxWapiVersion: "2.3.1", InfobloxSSLVerify: true, + InfobloxView: "", OCIConfigFile: "/etc/kubernetes/oci.yaml", InMemoryZones: []string{}, PDNSServer: "http://localhost:8081", @@ -266,6 +268,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("infoblox-wapi-password", "When using the Infoblox provider, specify the WAPI password (required when --provider=infoblox)").Default(defaultConfig.InfobloxWapiPassword).StringVar(&cfg.InfobloxWapiPassword) app.Flag("infoblox-wapi-version", "When using the Infoblox provider, specify the WAPI version (default: 2.3.1)").Default(defaultConfig.InfobloxWapiVersion).StringVar(&cfg.InfobloxWapiVersion) app.Flag("infoblox-ssl-verify", "When using the Infoblox provider, specify whether to verify the SSL certificate (default: true, disable with --no-infoblox-ssl-verify)").Default(strconv.FormatBool(defaultConfig.InfobloxSSLVerify)).BoolVar(&cfg.InfobloxSSLVerify) + app.Flag("infoblox-view", "DNS view (default: default)").Default(defaultConfig.InfobloxView).StringVar(&cfg.InfobloxView) app.Flag("dyn-customer-name", "When using the Dyn provider, specify the Customer Name").Default("").StringVar(&cfg.DynCustomerName) app.Flag("dyn-username", "When using the Dyn provider, specify the Username").Default("").StringVar(&cfg.DynUsername) app.Flag("dyn-password", "When using the Dyn provider, specify the pasword").Default("").StringVar(&cfg.DynPassword) diff --git a/provider/infoblox.go b/provider/infoblox.go index 71c89eefe..65913410d 100644 --- a/provider/infoblox.go +++ b/provider/infoblox.go @@ -39,6 +39,7 @@ type InfobloxConfig struct { Version string SSLVerify bool DryRun bool + View string } // InfobloxProvider implements the DNS provider for Infoblox. @@ -46,6 +47,7 @@ type InfobloxProvider struct { client ibclient.IBConnector domainFilter DomainFilter zoneIDFilter ZoneIDFilter + view string dryRun bool } @@ -87,6 +89,7 @@ func NewInfobloxProvider(infobloxConfig InfobloxConfig) (*InfobloxProvider, erro domainFilter: infobloxConfig.DomainFilter, zoneIDFilter: infobloxConfig.ZoneIDFilter, dryRun: infobloxConfig.DryRun, + view: infobloxConfig.View, } return provider, nil @@ -105,6 +108,7 @@ func (p *InfobloxProvider) Records() (endpoints []*endpoint.Endpoint, err error) objA := ibclient.NewRecordA( ibclient.RecordA{ Zone: zone.Fqdn, + View: p.view, }, ) err = p.client.GetObject(objA, "", &resA) @@ -120,6 +124,7 @@ func (p *InfobloxProvider) Records() (endpoints []*endpoint.Endpoint, err error) objH := ibclient.NewHostRecord( ibclient.HostRecord{ Zone: zone.Fqdn, + View: p.view, }, ) err = p.client.GetObject(objH, "", &resH) @@ -136,6 +141,7 @@ func (p *InfobloxProvider) Records() (endpoints []*endpoint.Endpoint, err error) objC := ibclient.NewRecordCNAME( ibclient.RecordCNAME{ Zone: zone.Fqdn, + View: p.view, }, ) err = p.client.GetObject(objC, "", &resC) @@ -150,6 +156,7 @@ func (p *InfobloxProvider) Records() (endpoints []*endpoint.Endpoint, err error) objT := ibclient.NewRecordTXT( ibclient.RecordTXT{ Zone: zone.Fqdn, + View: p.view, }, ) err = p.client.GetObject(objT, "", &resT) @@ -261,6 +268,7 @@ func (p *InfobloxProvider) recordSet(ep *endpoint.Endpoint, getObject bool) (rec ibclient.RecordA{ Name: ep.DNSName, Ipv4Addr: ep.Targets[0], + View: p.view, }, ) if getObject { @@ -279,6 +287,7 @@ func (p *InfobloxProvider) recordSet(ep *endpoint.Endpoint, getObject bool) (rec ibclient.RecordCNAME{ Name: ep.DNSName, Canonical: ep.Targets[0], + View: p.view, }, ) if getObject { @@ -302,6 +311,7 @@ func (p *InfobloxProvider) recordSet(ep *endpoint.Endpoint, getObject bool) (rec ibclient.RecordTXT{ Name: ep.DNSName, Text: ep.Targets[0], + View: p.view, }, ) if getObject { From 943e27ad100739f1a0a3fbc620cbd8431e4a1fcb Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 19 Feb 2019 11:54:20 +0100 Subject: [PATCH 067/136] Add infoblox view flag to tests --- pkg/apis/externaldns/types_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index 394e666c5..dfeb5ef0e 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -58,6 +58,7 @@ var ( InfobloxWapiUsername: "admin", InfobloxWapiPassword: "", InfobloxWapiVersion: "2.3.1", + InfobloxView: "", InfobloxSSLVerify: true, OCIConfigFile: "/etc/kubernetes/oci.yaml", InMemoryZones: []string{""}, @@ -113,6 +114,7 @@ var ( InfobloxWapiUsername: "infoblox", InfobloxWapiPassword: "infoblox", InfobloxWapiVersion: "2.6.1", + InfobloxView: "internal", InfobloxSSLVerify: false, OCIConfigFile: "oci.yaml", InMemoryZones: []string{"example.org", "company.com"}, @@ -183,6 +185,7 @@ func TestParseFlags(t *testing.T) { "--infoblox-wapi-username=infoblox", "--infoblox-wapi-password=infoblox", "--infoblox-wapi-version=2.6.1", + "--infoblox-view=internal", "--inmemory-zone=example.org", "--inmemory-zone=company.com", "--pdns-server=http://ns.example.com:8081", @@ -249,6 +252,7 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_INFOBLOX_WAPI_USERNAME": "infoblox", "EXTERNAL_DNS_INFOBLOX_WAPI_PASSWORD": "infoblox", "EXTERNAL_DNS_INFOBLOX_WAPI_VERSION": "2.6.1", + "EXTERNAL_DNS_INFOBLOX_VIEW": "internal", "EXTERNAL_DNS_INFOBLOX_SSL_VERIFY": "0", "EXTERNAL_DNS_OCI_CONFIG_FILE": "oci.yaml", "EXTERNAL_DNS_INMEMORY_ZONE": "example.org\ncompany.com", From 8d499e3bcfee3b9ea23c6a3939fb9cd382654ab3 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Fri, 15 Mar 2019 22:32:12 +0100 Subject: [PATCH 068/136] Correct default of infoblox-view parameter --- 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 ca74cd572..a1f8b7814 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -268,7 +268,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("infoblox-wapi-password", "When using the Infoblox provider, specify the WAPI password (required when --provider=infoblox)").Default(defaultConfig.InfobloxWapiPassword).StringVar(&cfg.InfobloxWapiPassword) app.Flag("infoblox-wapi-version", "When using the Infoblox provider, specify the WAPI version (default: 2.3.1)").Default(defaultConfig.InfobloxWapiVersion).StringVar(&cfg.InfobloxWapiVersion) app.Flag("infoblox-ssl-verify", "When using the Infoblox provider, specify whether to verify the SSL certificate (default: true, disable with --no-infoblox-ssl-verify)").Default(strconv.FormatBool(defaultConfig.InfobloxSSLVerify)).BoolVar(&cfg.InfobloxSSLVerify) - app.Flag("infoblox-view", "DNS view (default: default)").Default(defaultConfig.InfobloxView).StringVar(&cfg.InfobloxView) + app.Flag("infoblox-view", "DNS view (default: \"\")").Default(defaultConfig.InfobloxView).StringVar(&cfg.InfobloxView) app.Flag("dyn-customer-name", "When using the Dyn provider, specify the Customer Name").Default("").StringVar(&cfg.DynCustomerName) app.Flag("dyn-username", "When using the Dyn provider, specify the Username").Default("").StringVar(&cfg.DynUsername) app.Flag("dyn-password", "When using the Dyn provider, specify the pasword").Default("").StringVar(&cfg.DynPassword) From 0076e4156c4e13d8e481e0b4b258feab080ab30b Mon Sep 17 00:00:00 2001 From: Christian Glombek Date: Mon, 18 Mar 2019 22:13:44 +0100 Subject: [PATCH 069/136] Add support for multiple Istio Ingress Gateways The --istio-ingress-gateway flag may now be specified multiple times. --- docs/tutorials/istio.md | 4 +- main.go | 30 +-- pkg/apis/externaldns/types.go | 294 +++++++++++++-------------- pkg/apis/externaldns/types_test.go | 300 +++++++++++++++++----------- source/gateway.go | 73 +++---- source/gateway_test.go | 305 +++++++++++++++++++---------- source/store.go | 32 +-- source/store_test.go | 2 +- 8 files changed, 615 insertions(+), 425 deletions(-) diff --git a/docs/tutorials/istio.md b/docs/tutorials/istio.md index 4f74908c7..269c10bcb 100644 --- a/docs/tutorials/istio.md +++ b/docs/tutorials/istio.md @@ -25,7 +25,7 @@ spec: - --source=service - --source=ingress - --source=istio-gateway - - --istio-ingress-gateway=custom-istio-namespace/custom-istio-ingressgateway # omit to use the default (istio-system/istio-ingressgateway) + - --istio-ingress-gateway=custom-istio-namespace/custom-istio-ingressgateway # load balancer service to be used; can be specified multiple times. Omit to use the default (istio-system/istio-ingressgateway) - --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 - --provider=aws - --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization @@ -95,7 +95,7 @@ spec: - --source=service - --source=ingress - --source=istio-gateway - - --istio-ingress-gateway=custom-istio-namespace/custom-istio-ingressgateway # omit to use the default (istio-system/istio-ingressgateway) + - --istio-ingress-gateway=custom-istio-namespace/custom-istio-ingressgateway # load balancer service to be used; can be specified multiple times. Omit to use the default (istio-system/istio-ingressgateway) - --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 - --provider=aws - --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization diff --git a/main.go b/main.go index d5786c25a..5e2354cd4 100644 --- a/main.go +++ b/main.go @@ -67,21 +67,21 @@ func main() { // Create a source.Config from the flags passed by the user. sourceCfg := &source.Config{ - Namespace: cfg.Namespace, - AnnotationFilter: cfg.AnnotationFilter, - FQDNTemplate: cfg.FQDNTemplate, - CombineFQDNAndAnnotation: cfg.CombineFQDNAndAnnotation, - IgnoreHostnameAnnotation: cfg.IgnoreHostnameAnnotation, - Compatibility: cfg.Compatibility, - PublishInternal: cfg.PublishInternal, - PublishHostIP: cfg.PublishHostIP, - ConnectorServer: cfg.ConnectorSourceServer, - CRDSourceAPIVersion: cfg.CRDSourceAPIVersion, - CRDSourceKind: cfg.CRDSourceKind, - KubeConfig: cfg.KubeConfig, - KubeMaster: cfg.Master, - ServiceTypeFilter: cfg.ServiceTypeFilter, - IstioIngressGateway: cfg.IstioIngressGateway, + Namespace: cfg.Namespace, + AnnotationFilter: cfg.AnnotationFilter, + FQDNTemplate: cfg.FQDNTemplate, + CombineFQDNAndAnnotation: cfg.CombineFQDNAndAnnotation, + IgnoreHostnameAnnotation: cfg.IgnoreHostnameAnnotation, + Compatibility: cfg.Compatibility, + PublishInternal: cfg.PublishInternal, + PublishHostIP: cfg.PublishHostIP, + ConnectorServer: cfg.ConnectorSourceServer, + CRDSourceAPIVersion: cfg.CRDSourceAPIVersion, + CRDSourceKind: cfg.CRDSourceKind, + KubeConfig: cfg.KubeConfig, + KubeMaster: cfg.Master, + ServiceTypeFilter: cfg.ServiceTypeFilter, + IstioIngressGatewayServices: cfg.IstioIngressGatewayServices, } // 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 367c512e7..a3257fc1d 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -36,155 +36,155 @@ var ( // Config is a project-wide configuration type Config struct { - Master string - KubeConfig string - RequestTimeout time.Duration - IstioIngressGateway string - Sources []string - Namespace string - AnnotationFilter string - FQDNTemplate string - CombineFQDNAndAnnotation bool - IgnoreHostnameAnnotation bool - Compatibility string - PublishInternal bool - PublishHostIP bool - ConnectorSourceServer string - Provider string - GoogleProject string - DomainFilter []string - ZoneIDFilter []string - AlibabaCloudConfigFile string - AlibabaCloudZoneType string - AWSZoneType string - AWSZoneTagFilter []string - AWSAssumeRole string - AWSBatchChangeSize int - AWSBatchChangeInterval time.Duration - AWSEvaluateTargetHealth bool - AWSAPIRetries int - AzureConfigFile string - AzureResourceGroup string - CloudflareProxied bool - CloudflareZonesPerPage int - RcodezeroTXTEncrypt bool - InfobloxGridHost string - InfobloxWapiPort int - InfobloxWapiUsername string - InfobloxWapiPassword string - InfobloxWapiVersion string - InfobloxSSLVerify bool - InfobloxView string - DynCustomerName string - DynUsername string - DynPassword string - DynMinTTLSeconds int - OCIConfigFile string - InMemoryZones []string - PDNSServer string - PDNSAPIKey string - PDNSTLSEnabled bool - TLSCA string - TLSClientCert string - TLSClientCertKey string - Policy string - Registry string - TXTOwnerID string - TXTPrefix string - Interval time.Duration - Once bool - DryRun bool - LogFormat string - MetricsAddress string - LogLevel string - TXTCacheInterval time.Duration - ExoscaleEndpoint string - ExoscaleAPIKey string - ExoscaleAPISecret string - CRDSourceAPIVersion string - CRDSourceKind string - ServiceTypeFilter []string - RFC2136Host string - RFC2136Port int - RFC2136Zone string - RFC2136Insecure bool - RFC2136TSIGKeyName string - RFC2136TSIGSecret string - RFC2136TSIGSecretAlg string - RFC2136TAXFR bool + Master string + KubeConfig string + RequestTimeout time.Duration + IstioIngressGatewayServices []string + Sources []string + Namespace string + AnnotationFilter string + FQDNTemplate string + CombineFQDNAndAnnotation bool + IgnoreHostnameAnnotation bool + Compatibility string + PublishInternal bool + PublishHostIP bool + ConnectorSourceServer string + Provider string + GoogleProject string + DomainFilter []string + ZoneIDFilter []string + AlibabaCloudConfigFile string + AlibabaCloudZoneType string + AWSZoneType string + AWSZoneTagFilter []string + AWSAssumeRole string + AWSBatchChangeSize int + AWSBatchChangeInterval time.Duration + AWSEvaluateTargetHealth bool + AWSAPIRetries int + AzureConfigFile string + AzureResourceGroup string + CloudflareProxied bool + CloudflareZonesPerPage int + RcodezeroTXTEncrypt bool + InfobloxGridHost string + InfobloxWapiPort int + InfobloxWapiUsername string + InfobloxWapiPassword string + InfobloxWapiVersion string + InfobloxSSLVerify bool + InfobloxView string + DynCustomerName string + DynUsername string + DynPassword string + DynMinTTLSeconds int + OCIConfigFile string + InMemoryZones []string + PDNSServer string + PDNSAPIKey string + PDNSTLSEnabled bool + TLSCA string + TLSClientCert string + TLSClientCertKey string + Policy string + Registry string + TXTOwnerID string + TXTPrefix string + Interval time.Duration + Once bool + DryRun bool + LogFormat string + MetricsAddress string + LogLevel string + TXTCacheInterval time.Duration + ExoscaleEndpoint string + ExoscaleAPIKey string + ExoscaleAPISecret string + CRDSourceAPIVersion string + CRDSourceKind string + ServiceTypeFilter []string + RFC2136Host string + RFC2136Port int + RFC2136Zone string + RFC2136Insecure bool + RFC2136TSIGKeyName string + RFC2136TSIGSecret string + RFC2136TSIGSecretAlg string + RFC2136TAXFR bool } var defaultConfig = &Config{ - Master: "", - KubeConfig: "", - RequestTimeout: time.Second * 30, - IstioIngressGateway: "istio-system/istio-ingressgateway", - Sources: nil, - Namespace: "", - AnnotationFilter: "", - FQDNTemplate: "", - CombineFQDNAndAnnotation: false, - IgnoreHostnameAnnotation: false, - Compatibility: "", - PublishInternal: false, - PublishHostIP: false, - ConnectorSourceServer: "localhost:8080", - Provider: "", - GoogleProject: "", - DomainFilter: []string{}, - AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json", - AWSZoneType: "", - AWSZoneTagFilter: []string{}, - AWSAssumeRole: "", - AWSBatchChangeSize: 1000, - AWSBatchChangeInterval: time.Second, - AWSEvaluateTargetHealth: true, - AWSAPIRetries: 3, - AzureConfigFile: "/etc/kubernetes/azure.json", - AzureResourceGroup: "", - CloudflareProxied: false, - CloudflareZonesPerPage: 50, - RcodezeroTXTEncrypt: false, - InfobloxGridHost: "", - InfobloxWapiPort: 443, - InfobloxWapiUsername: "admin", - InfobloxWapiPassword: "", - InfobloxWapiVersion: "2.3.1", - InfobloxSSLVerify: true, - InfobloxView: "", - OCIConfigFile: "/etc/kubernetes/oci.yaml", - InMemoryZones: []string{}, - PDNSServer: "http://localhost:8081", - PDNSAPIKey: "", - PDNSTLSEnabled: false, - TLSCA: "", - TLSClientCert: "", - TLSClientCertKey: "", - Policy: "sync", - Registry: "txt", - TXTOwnerID: "default", - TXTPrefix: "", - TXTCacheInterval: 0, - Interval: time.Minute, - Once: false, - DryRun: false, - LogFormat: "text", - MetricsAddress: ":7979", - LogLevel: logrus.InfoLevel.String(), - ExoscaleEndpoint: "https://api.exoscale.ch/dns", - ExoscaleAPIKey: "", - ExoscaleAPISecret: "", - CRDSourceAPIVersion: "externaldns.k8s.io/v1alpha1", - CRDSourceKind: "DNSEndpoint", - ServiceTypeFilter: []string{}, - RFC2136Host: "", - RFC2136Port: 0, - RFC2136Zone: "", - RFC2136Insecure: false, - RFC2136TSIGKeyName: "", - RFC2136TSIGSecret: "", - RFC2136TSIGSecretAlg: "", - RFC2136TAXFR: true, + Master: "", + KubeConfig: "", + RequestTimeout: time.Second * 30, + IstioIngressGatewayServices: []string{"istio-system/istio-ingressgateway"}, + Sources: nil, + Namespace: "", + AnnotationFilter: "", + FQDNTemplate: "", + CombineFQDNAndAnnotation: false, + IgnoreHostnameAnnotation: false, + Compatibility: "", + PublishInternal: false, + PublishHostIP: false, + ConnectorSourceServer: "localhost:8080", + Provider: "", + GoogleProject: "", + DomainFilter: []string{}, + AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json", + AWSZoneType: "", + AWSZoneTagFilter: []string{}, + AWSAssumeRole: "", + AWSBatchChangeSize: 1000, + AWSBatchChangeInterval: time.Second, + AWSEvaluateTargetHealth: true, + AWSAPIRetries: 3, + AzureConfigFile: "/etc/kubernetes/azure.json", + AzureResourceGroup: "", + CloudflareProxied: false, + CloudflareZonesPerPage: 50, + RcodezeroTXTEncrypt: false, + InfobloxGridHost: "", + InfobloxWapiPort: 443, + InfobloxWapiUsername: "admin", + InfobloxWapiPassword: "", + InfobloxWapiVersion: "2.3.1", + InfobloxSSLVerify: true, + InfobloxView: "", + OCIConfigFile: "/etc/kubernetes/oci.yaml", + InMemoryZones: []string{}, + PDNSServer: "http://localhost:8081", + PDNSAPIKey: "", + PDNSTLSEnabled: false, + TLSCA: "", + TLSClientCert: "", + TLSClientCertKey: "", + Policy: "sync", + Registry: "txt", + TXTOwnerID: "default", + TXTPrefix: "", + TXTCacheInterval: 0, + Interval: time.Minute, + Once: false, + DryRun: false, + LogFormat: "text", + MetricsAddress: ":7979", + LogLevel: logrus.InfoLevel.String(), + ExoscaleEndpoint: "https://api.exoscale.ch/dns", + ExoscaleAPIKey: "", + ExoscaleAPISecret: "", + CRDSourceAPIVersion: "externaldns.k8s.io/v1alpha1", + CRDSourceKind: "DNSEndpoint", + ServiceTypeFilter: []string{}, + RFC2136Host: "", + RFC2136Port: 0, + RFC2136Zone: "", + RFC2136Insecure: false, + RFC2136TSIGKeyName: "", + RFC2136TSIGSecret: "", + RFC2136TSIGSecretAlg: "", + RFC2136TAXFR: true, } // NewConfig returns new Config object @@ -229,7 +229,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("request-timeout", "Request timeout when calling Kubernetes APIs. 0s means no timeout").Default(defaultConfig.RequestTimeout.String()).DurationVar(&cfg.RequestTimeout) // Flags related to Istio - app.Flag("istio-ingress-gateway", "The fully-qualified name of the Istio ingress gateway service (default: istio-system/istio-ingressgateway)").Default(defaultConfig.IstioIngressGateway).StringVar(&cfg.IstioIngressGateway) + app.Flag("istio-ingress-gateway", "The fully-qualified name of the Istio ingress gateway service. Flag can be specified multiple times (default: istio-system/istio-ingressgateway)").Default("istio-system/istio-ingressgateway").StringsVar(&cfg.IstioIngressGatewayServices) // Flags related to processing sources app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, fake, connector, istio-gateway, crd").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "istio-gateway", "fake", "connector", "crd") diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index b2aacbd09..24b3eddc4 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -29,120 +29,177 @@ import ( var ( minimalConfig = &Config{ - Master: "", - KubeConfig: "", - RequestTimeout: time.Second * 30, - IstioIngressGateway: "istio-system/istio-ingressgateway", - Sources: []string{"service"}, - Namespace: "", - FQDNTemplate: "", - Compatibility: "", - Provider: "google", - GoogleProject: "", - DomainFilter: []string{""}, - ZoneIDFilter: []string{""}, - AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json", - AWSZoneType: "", - AWSZoneTagFilter: []string{""}, - AWSAssumeRole: "", - AWSBatchChangeSize: 1000, - AWSBatchChangeInterval: time.Second, - AWSEvaluateTargetHealth: true, - AWSAPIRetries: 3, - AzureConfigFile: "/etc/kubernetes/azure.json", - AzureResourceGroup: "", - CloudflareProxied: false, - CloudflareZonesPerPage: 50, - InfobloxGridHost: "", - InfobloxWapiPort: 443, - InfobloxWapiUsername: "admin", - InfobloxWapiPassword: "", - InfobloxWapiVersion: "2.3.1", - InfobloxView: "", - InfobloxSSLVerify: true, - OCIConfigFile: "/etc/kubernetes/oci.yaml", - InMemoryZones: []string{""}, - PDNSServer: "http://localhost:8081", - PDNSAPIKey: "", - Policy: "sync", - Registry: "txt", - TXTOwnerID: "default", - TXTPrefix: "", - TXTCacheInterval: 0, - Interval: time.Minute, - Once: false, - DryRun: false, - LogFormat: "text", - MetricsAddress: ":7979", - LogLevel: logrus.InfoLevel.String(), - ConnectorSourceServer: "localhost:8080", - ExoscaleEndpoint: "https://api.exoscale.ch/dns", - ExoscaleAPIKey: "", - ExoscaleAPISecret: "", - CRDSourceAPIVersion: "externaldns.k8s.io/v1alpha1", - CRDSourceKind: "DNSEndpoint", - RcodezeroTXTEncrypt: false, + Master: "", + KubeConfig: "", + RequestTimeout: time.Second * 30, + IstioIngressGatewayServices: []string{"istio-system/istio-ingressgateway"}, + Sources: []string{"service"}, + Namespace: "", + FQDNTemplate: "", + Compatibility: "", + Provider: "google", + GoogleProject: "", + DomainFilter: []string{""}, + ZoneIDFilter: []string{""}, + AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json", + AWSZoneType: "", + AWSZoneTagFilter: []string{""}, + AWSAssumeRole: "", + AWSBatchChangeSize: 1000, + AWSBatchChangeInterval: time.Second, + AWSEvaluateTargetHealth: true, + AWSAPIRetries: 3, + AzureConfigFile: "/etc/kubernetes/azure.json", + AzureResourceGroup: "", + CloudflareProxied: false, + CloudflareZonesPerPage: 50, + InfobloxGridHost: "", + InfobloxWapiPort: 443, + InfobloxWapiUsername: "admin", + InfobloxWapiPassword: "", + InfobloxWapiVersion: "2.3.1", + InfobloxView: "", + InfobloxSSLVerify: true, + OCIConfigFile: "/etc/kubernetes/oci.yaml", + InMemoryZones: []string{""}, + PDNSServer: "http://localhost:8081", + PDNSAPIKey: "", + Policy: "sync", + Registry: "txt", + TXTOwnerID: "default", + TXTPrefix: "", + TXTCacheInterval: 0, + Interval: time.Minute, + Once: false, + DryRun: false, + LogFormat: "text", + MetricsAddress: ":7979", + LogLevel: logrus.InfoLevel.String(), + ConnectorSourceServer: "localhost:8080", + ExoscaleEndpoint: "https://api.exoscale.ch/dns", + ExoscaleAPIKey: "", + ExoscaleAPISecret: "", + CRDSourceAPIVersion: "externaldns.k8s.io/v1alpha1", + CRDSourceKind: "DNSEndpoint", + RcodezeroTXTEncrypt: false, } overriddenConfig = &Config{ - Master: "http://127.0.0.1:8080", - KubeConfig: "/some/path", - RequestTimeout: time.Second * 77, - IstioIngressGateway: "istio-other/istio-otheringressgateway", - Sources: []string{"service", "ingress", "connector"}, - Namespace: "namespace", - IgnoreHostnameAnnotation: true, - FQDNTemplate: "{{.Name}}.service.example.com", - Compatibility: "mate", - Provider: "google", - GoogleProject: "project", - DomainFilter: []string{"example.org", "company.com"}, - ZoneIDFilter: []string{"/hostedzone/ZTST1", "/hostedzone/ZTST2"}, - AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json", - AWSZoneType: "private", - AWSZoneTagFilter: []string{"tag=foo"}, - AWSAssumeRole: "some-other-role", - AWSBatchChangeSize: 100, - AWSBatchChangeInterval: time.Second * 2, - AWSEvaluateTargetHealth: false, - AWSAPIRetries: 13, - AzureConfigFile: "azure.json", - AzureResourceGroup: "arg", - CloudflareProxied: true, - CloudflareZonesPerPage: 20, - InfobloxGridHost: "127.0.0.1", - InfobloxWapiPort: 8443, - InfobloxWapiUsername: "infoblox", - InfobloxWapiPassword: "infoblox", - InfobloxWapiVersion: "2.6.1", - InfobloxView: "internal", - InfobloxSSLVerify: false, - OCIConfigFile: "oci.yaml", - InMemoryZones: []string{"example.org", "company.com"}, - PDNSServer: "http://ns.example.com:8081", - PDNSAPIKey: "some-secret-key", - PDNSTLSEnabled: true, - TLSCA: "/path/to/ca.crt", - TLSClientCert: "/path/to/cert.pem", - TLSClientCertKey: "/path/to/key.pem", - Policy: "upsert-only", - Registry: "noop", - TXTOwnerID: "owner-1", - TXTPrefix: "associated-txt-record", - TXTCacheInterval: 12 * time.Hour, - Interval: 10 * time.Minute, - Once: true, - DryRun: true, - LogFormat: "json", - MetricsAddress: "127.0.0.1:9099", - LogLevel: logrus.DebugLevel.String(), - ConnectorSourceServer: "localhost:8081", - ExoscaleEndpoint: "https://api.foo.ch/dns", - ExoscaleAPIKey: "1", - ExoscaleAPISecret: "2", - CRDSourceAPIVersion: "test.k8s.io/v1alpha1", - CRDSourceKind: "Endpoint", - RcodezeroTXTEncrypt: true, + Master: "http://127.0.0.1:8080", + KubeConfig: "/some/path", + RequestTimeout: time.Second * 77, + IstioIngressGatewayServices: []string{"istio-other/istio-otheringressgateway"}, + Sources: []string{"service", "ingress", "connector"}, + Namespace: "namespace", + IgnoreHostnameAnnotation: true, + FQDNTemplate: "{{.Name}}.service.example.com", + Compatibility: "mate", + Provider: "google", + GoogleProject: "project", + DomainFilter: []string{"example.org", "company.com"}, + ZoneIDFilter: []string{"/hostedzone/ZTST1", "/hostedzone/ZTST2"}, + AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json", + AWSZoneType: "private", + AWSZoneTagFilter: []string{"tag=foo"}, + AWSAssumeRole: "some-other-role", + AWSBatchChangeSize: 100, + AWSBatchChangeInterval: time.Second * 2, + AWSEvaluateTargetHealth: false, + AWSAPIRetries: 13, + AzureConfigFile: "azure.json", + AzureResourceGroup: "arg", + CloudflareProxied: true, + CloudflareZonesPerPage: 20, + InfobloxGridHost: "127.0.0.1", + InfobloxWapiPort: 8443, + InfobloxWapiUsername: "infoblox", + InfobloxWapiPassword: "infoblox", + InfobloxWapiVersion: "2.6.1", + InfobloxView: "internal", + InfobloxSSLVerify: false, + OCIConfigFile: "oci.yaml", + InMemoryZones: []string{"example.org", "company.com"}, + PDNSServer: "http://ns.example.com:8081", + PDNSAPIKey: "some-secret-key", + PDNSTLSEnabled: true, + TLSCA: "/path/to/ca.crt", + TLSClientCert: "/path/to/cert.pem", + TLSClientCertKey: "/path/to/key.pem", + Policy: "upsert-only", + Registry: "noop", + TXTOwnerID: "owner-1", + TXTPrefix: "associated-txt-record", + TXTCacheInterval: 12 * time.Hour, + Interval: 10 * time.Minute, + Once: true, + DryRun: true, + LogFormat: "json", + MetricsAddress: "127.0.0.1:9099", + LogLevel: logrus.DebugLevel.String(), + ConnectorSourceServer: "localhost:8081", + ExoscaleEndpoint: "https://api.foo.ch/dns", + ExoscaleAPIKey: "1", + ExoscaleAPISecret: "2", + CRDSourceAPIVersion: "test.k8s.io/v1alpha1", + CRDSourceKind: "Endpoint", + RcodezeroTXTEncrypt: true, + } + + // minimal config with istio gateway source and multiple ingressgateway load balancer services + multipleIstioIngressGatewaysConfig = &Config{ + Master: "", + KubeConfig: "", + RequestTimeout: time.Second * 30, + IstioIngressGatewayServices: []string{"istio-system/istio-ingressgateway", "istio-other/istio-otheringressgateway"}, + Sources: []string{"istio-gateway"}, + Namespace: "", + FQDNTemplate: "", + Compatibility: "", + Provider: "google", + GoogleProject: "", + DomainFilter: []string{""}, + ZoneIDFilter: []string{""}, + AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json", + AWSZoneType: "", + AWSZoneTagFilter: []string{""}, + AWSAssumeRole: "", + AWSBatchChangeSize: 1000, + AWSBatchChangeInterval: time.Second, + AWSEvaluateTargetHealth: true, + AWSAPIRetries: 3, + AzureConfigFile: "/etc/kubernetes/azure.json", + AzureResourceGroup: "", + CloudflareProxied: false, + CloudflareZonesPerPage: 50, + InfobloxGridHost: "", + InfobloxWapiPort: 443, + InfobloxWapiUsername: "admin", + InfobloxWapiPassword: "", + InfobloxWapiVersion: "2.3.1", + InfobloxView: "", + InfobloxSSLVerify: true, + OCIConfigFile: "/etc/kubernetes/oci.yaml", + InMemoryZones: []string{""}, + PDNSServer: "http://localhost:8081", + PDNSAPIKey: "", + Policy: "sync", + Registry: "txt", + TXTOwnerID: "default", + TXTPrefix: "", + TXTCacheInterval: 0, + Interval: time.Minute, + Once: false, + DryRun: false, + LogFormat: "text", + MetricsAddress: ":7979", + LogLevel: logrus.InfoLevel.String(), + ConnectorSourceServer: "localhost:8080", + ExoscaleEndpoint: "https://api.exoscale.ch/dns", + ExoscaleAPIKey: "", + ExoscaleAPISecret: "", + CRDSourceAPIVersion: "externaldns.k8s.io/v1alpha1", + CRDSourceKind: "DNSEndpoint", + RcodezeroTXTEncrypt: false, } ) @@ -295,6 +352,27 @@ func TestParseFlags(t *testing.T) { }, expected: overriddenConfig, }, + { + title: "istio config with 2 ingressgateways", + args: []string{ + "--provider=google", + "--source=istio-gateway", + "--istio-ingress-gateway=istio-system/istio-ingressgateway", + "--istio-ingress-gateway=istio-other/istio-otheringressgateway", + }, + envVars: map[string]string{}, + expected: multipleIstioIngressGatewaysConfig, + }, + { + title: "override everything via environment variables with multiple istio ingress gateway load balancer services", + args: []string{}, + envVars: map[string]string{ + "EXTERNAL_DNS_PROVIDER": "google", + "EXTERNAL_DNS_SOURCE": "istio-gateway", + "EXTERNAL_DNS_ISTIO_INGRESS_GATEWAY": "istio-system/istio-ingressgateway\nistio-other/istio-otheringressgateway", + }, + expected: multipleIstioIngressGatewaysConfig, + }, } { t.Run(ti.title, func(t *testing.T) { originalEnv := setEnv(t, ti.envVars) diff --git a/source/gateway.go b/source/gateway.go index 9d34c16ca..751698da1 100644 --- a/source/gateway.go +++ b/source/gateway.go @@ -38,22 +38,21 @@ import ( // The gateway implementation uses the spec.servers.hosts values for the hostnames. // Use targetAnnotationKey to explicitly set Endpoint. type gatewaySource struct { - kubeClient kubernetes.Interface - istioClient istiomodel.ConfigStore - istioNamespace string - istioIngressGatewayName string - namespace string - annotationFilter string - fqdnTemplate *template.Template - combineFQDNAnnotation bool - ignoreHostnameAnnotation bool + kubeClient kubernetes.Interface + istioClient istiomodel.ConfigStore + istioIngressGatewayServices []string + namespace string + annotationFilter string + fqdnTemplate *template.Template + combineFQDNAnnotation bool + ignoreHostnameAnnotation bool } // NewIstioGatewaySource creates a new gatewaySource with the given config. func NewIstioGatewaySource( kubeClient kubernetes.Interface, istioClient istiomodel.ConfigStore, - istioIngressGateway string, + istioIngressGatewayServices []string, namespace string, annotationFilter string, fqdnTemplate string, @@ -64,9 +63,10 @@ func NewIstioGatewaySource( tmpl *template.Template err error ) - istioNamespace, istioIngressGatewayName, err := parseIngressGateway(istioIngressGateway) - if err != nil { - return nil, err + for _, lbService := range istioIngressGatewayServices { + if _, _, err = parseIngressGateway(lbService); err != nil { + return nil, err + } } if fqdnTemplate != "" { @@ -79,15 +79,14 @@ func NewIstioGatewaySource( } return &gatewaySource{ - kubeClient: kubeClient, - istioClient: istioClient, - istioNamespace: istioNamespace, - istioIngressGatewayName: istioIngressGatewayName, - namespace: namespace, - annotationFilter: annotationFilter, - fqdnTemplate: tmpl, - combineFQDNAnnotation: combineFqdnAnnotation, - ignoreHostnameAnnotation: ignoreHostnameAnnotation, + kubeClient: kubeClient, + istioClient: istioClient, + istioIngressGatewayServices: istioIngressGatewayServices, + namespace: namespace, + annotationFilter: annotationFilter, + fqdnTemplate: tmpl, + combineFQDNAnnotation: combineFqdnAnnotation, + ignoreHostnameAnnotation: ignoreHostnameAnnotation, }, nil } @@ -169,7 +168,7 @@ func (sc *gatewaySource) endpointsFromTemplate(config *istiomodel.Config) ([]*en targets := getTargetsFromTargetAnnotation(config.Annotations) if len(targets) == 0 { - targets, err = sc.targetsFromIstioIngressStatus() + targets, err = sc.targetsFromIstioIngressGatewayServices() if err != nil { return nil, err } @@ -224,16 +223,22 @@ func (sc *gatewaySource) setResourceLabel(config istiomodel.Config, endpoints [] } } -func (sc *gatewaySource) targetsFromIstioIngressStatus() (targets endpoint.Targets, err error) { - if svc, e := sc.kubeClient.CoreV1().Services(sc.istioNamespace).Get(sc.istioIngressGatewayName, metav1.GetOptions{}); e != nil { - err = e - } else { - for _, lb := range svc.Status.LoadBalancer.Ingress { - if lb.IP != "" { - targets = append(targets, lb.IP) - } - if lb.Hostname != "" { - targets = append(targets, lb.Hostname) +func (sc *gatewaySource) targetsFromIstioIngressGatewayServices() (targets endpoint.Targets, err error) { + for _, lbService := range sc.istioIngressGatewayServices { + lbNamespace, lbName, err := parseIngressGateway(lbService) + if err != nil { + return nil, err + } + if svc, err := sc.kubeClient.CoreV1().Services(lbNamespace).Get(lbName, metav1.GetOptions{}); err != nil { + log.Warn(err) + } else { + for _, lb := range svc.Status.LoadBalancer.Ingress { + if lb.IP != "" { + targets = append(targets, lb.IP) + } + if lb.Hostname != "" { + targets = append(targets, lb.Hostname) + } } } } @@ -253,7 +258,7 @@ func (sc *gatewaySource) endpointsFromGatewayConfig(config istiomodel.Config) ([ targets := getTargetsFromTargetAnnotation(config.Annotations) if len(targets) == 0 { - targets, err = sc.targetsFromIstioIngressStatus() + targets, err = sc.targetsFromIstioIngressGatewayServices() if err != nil { return nil, err } diff --git a/source/gateway_test.go b/source/gateway_test.go index c12a8389b..2d702f6eb 100644 --- a/source/gateway_test.go +++ b/source/gateway_test.go @@ -43,9 +43,9 @@ var gatewayType = istiomodel.Gateway.Type type GatewaySuite struct { suite.Suite - source Source - ingress *v1.Service - config istiomodel.Config + source Source + lbServices []*v1.Service + config istiomodel.Config } func (suite *GatewaySuite) SetupTest() { @@ -53,17 +53,30 @@ func (suite *GatewaySuite) SetupTest() { fakeIstioClient := NewFakeConfigStore() var err error - suite.ingress = (fakeIngressGateway{ - ips: []string{"8.8.8.8"}, - hostnames: []string{"v1"}, - }).Service() - _, err = fakeKubernetesClient.CoreV1().Services(suite.ingress.Namespace).Create(suite.ingress) - suite.NoError(err, "should succeed") + suite.lbServices = []*v1.Service{ + (fakeIngressGatewayService{ + ips: []string{"8.8.8.8"}, + hostnames: []string{"v1"}, + namespace: "istio-system", + name: "istio-gateway1", + }).Service(), + (fakeIngressGatewayService{ + ips: []string{"1.1.1.1"}, + hostnames: []string{"v42"}, + namespace: "istio-other", + name: "istio-gateway2", + }).Service(), + } + + for _, loadBalancer := range suite.lbServices { + _, err = fakeKubernetesClient.CoreV1().Services(loadBalancer.Namespace).Create(loadBalancer) + suite.NoError(err, "should succeed") + } suite.source, err = NewIstioGatewaySource( fakeKubernetesClient, fakeIstioClient, - "istio-system/istio-ingressgateway", + []string{"istio-system/istio-ingressgateway"}, "default", "", "{{.Name}}", @@ -137,7 +150,7 @@ func TestNewIstioGatewaySource(t *testing.T) { _, err := NewIstioGatewaySource( fake.NewSimpleClientset(), NewFakeConfigStore(), - "istio-system/istio-ingressgateway", + []string{"istio-system/istio-ingressgateway"}, "", ti.annotationFilter, ti.fqdnTemplate, @@ -155,15 +168,17 @@ func TestNewIstioGatewaySource(t *testing.T) { func testEndpointsFromGatewayConfig(t *testing.T) { for _, ti := range []struct { - title string - ingress fakeIngressGateway - config fakeGatewayConfig - expected []*endpoint.Endpoint + title string + lbServices []fakeIngressGatewayService + config fakeGatewayConfig + expected []*endpoint.Endpoint }{ { title: "one rule.host one lb.hostname", - ingress: fakeIngressGateway{ - hostnames: []string{"lb.com"}, // Kubernetes omits the trailing dot + lbServices: []fakeIngressGatewayService{ + { + hostnames: []string{"lb.com"}, // Kubernetes omits the trailing dot + }, }, config: fakeGatewayConfig{ dnsnames: [][]string{ @@ -179,8 +194,10 @@ func testEndpointsFromGatewayConfig(t *testing.T) { }, { title: "one rule.host one lb.IP", - ingress: fakeIngressGateway{ - ips: []string{"8.8.8.8"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8"}, + }, }, config: fakeGatewayConfig{ dnsnames: [][]string{ @@ -196,9 +213,11 @@ func testEndpointsFromGatewayConfig(t *testing.T) { }, { title: "one rule.host two lb.IP and two lb.Hostname", - ingress: fakeIngressGateway{ - ips: []string{"8.8.8.8", "127.0.0.1"}, - hostnames: []string{"elb.com", "alb.com"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8", "127.0.0.1"}, + hostnames: []string{"elb.com", "alb.com"}, + }, }, config: fakeGatewayConfig{ dnsnames: [][]string{ @@ -218,9 +237,11 @@ func testEndpointsFromGatewayConfig(t *testing.T) { }, { title: "no rule.host", - ingress: fakeIngressGateway{ - ips: []string{"8.8.8.8", "127.0.0.1"}, - hostnames: []string{"elb.com", "alb.com"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8", "127.0.0.1"}, + hostnames: []string{"elb.com", "alb.com"}, + }, }, config: fakeGatewayConfig{ dnsnames: [][]string{}, @@ -229,9 +250,11 @@ func testEndpointsFromGatewayConfig(t *testing.T) { }, { title: "one empty rule.host", - ingress: fakeIngressGateway{ - ips: []string{"8.8.8.8", "127.0.0.1"}, - hostnames: []string{"elb.com", "alb.com"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8", "127.0.0.1"}, + hostnames: []string{"elb.com", "alb.com"}, + }, }, config: fakeGatewayConfig{ dnsnames: [][]string{ @@ -241,8 +264,8 @@ func testEndpointsFromGatewayConfig(t *testing.T) { expected: []*endpoint.Endpoint{}, }, { - title: "no targets", - ingress: fakeIngressGateway{}, + title: "no targets", + lbServices: []fakeIngressGatewayService{{}}, config: fakeGatewayConfig{ dnsnames: [][]string{ {""}, @@ -250,9 +273,35 @@ func testEndpointsFromGatewayConfig(t *testing.T) { }, expected: []*endpoint.Endpoint{}, }, + { + title: "one gateway, two ingressgateway loadbalancer hostnames", + lbServices: []fakeIngressGatewayService{ + { + hostnames: []string{"lb.com"}, + namespace: "istio-other", + name: "gateway1", + }, + { + hostnames: []string{"lb2.com"}, + namespace: "istio-other", + name: "gateway2", + }, + }, + config: fakeGatewayConfig{ + dnsnames: [][]string{ + {"foo.bar"}, // Kubernetes requires removal of trailing dot + }, + }, + expected: []*endpoint.Endpoint{ + { + DNSName: "foo.bar", + Targets: endpoint.Targets{"lb.com", "lb2.com"}, + }, + }, + }, } { t.Run(ti.title, func(t *testing.T) { - if source, err := newTestGatewaySource(ti.ingress.Service()); err != nil { + if source, err := newTestGatewaySource(ti.lbServices); err != nil { require.NoError(t, err) } else if endpoints, err := source.endpointsFromGatewayConfig(ti.config.Config()); err != nil { require.NoError(t, err) @@ -269,7 +318,7 @@ func testGatewayEndpoints(t *testing.T) { title string targetNamespace string annotationFilter string - ingressGateway fakeIngressGateway + lbServices []fakeIngressGatewayService configItems []fakeGatewayConfig expected []*endpoint.Endpoint expectError bool @@ -282,11 +331,13 @@ func testGatewayEndpoints(t *testing.T) { targetNamespace: "", }, { - title: "two simple gateways", + title: "two simple gateways, one ingressgateway loadbalancer service", targetNamespace: "", - ingressGateway: fakeIngressGateway{ - ips: []string{"8.8.8.8"}, - hostnames: []string{"lb.com"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8"}, + hostnames: []string{"lb.com"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -320,11 +371,13 @@ func testGatewayEndpoints(t *testing.T) { }, }, { - title: "two simple gateways on different namespaces", + title: "two simple gateways on different namespaces, one ingressgateway loadbalancer service", targetNamespace: "", - ingressGateway: fakeIngressGateway{ - ips: []string{"8.8.8.8"}, - hostnames: []string{"lb.com"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8"}, + hostnames: []string{"lb.com"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -358,11 +411,13 @@ func testGatewayEndpoints(t *testing.T) { }, }, { - title: "two simple gateways on different namespaces with target namespace", + title: "two simple gateways on different namespaces and a target namespace, one ingressgateway loadbalancer service", targetNamespace: "testing1", - ingressGateway: fakeIngressGateway{ - ips: []string{"8.8.8.8"}, - hostnames: []string{"lb.com"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8"}, + hostnames: []string{"lb.com"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -391,8 +446,10 @@ func testGatewayEndpoints(t *testing.T) { title: "valid matching annotation filter expression", targetNamespace: "", annotationFilter: "kubernetes.io/gateway.class in (alb, nginx)", - ingressGateway: fakeIngressGateway{ - ips: []string{"8.8.8.8"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -415,8 +472,10 @@ func testGatewayEndpoints(t *testing.T) { title: "valid non-matching annotation filter expression", targetNamespace: "", annotationFilter: "kubernetes.io/gateway.class in (alb, nginx)", - ingressGateway: fakeIngressGateway{ - ips: []string{"8.8.8.8"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -434,8 +493,10 @@ func testGatewayEndpoints(t *testing.T) { title: "invalid annotation filter expression", targetNamespace: "", annotationFilter: "kubernetes.io/gateway.name in (a b)", - ingressGateway: fakeIngressGateway{ - ips: []string{"8.8.8.8"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -454,8 +515,10 @@ func testGatewayEndpoints(t *testing.T) { title: "valid matching annotation filter label", targetNamespace: "", annotationFilter: "kubernetes.io/gateway.class=nginx", - ingressGateway: fakeIngressGateway{ - ips: []string{"8.8.8.8"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -478,8 +541,10 @@ func testGatewayEndpoints(t *testing.T) { title: "valid non-matching annotation filter label", targetNamespace: "", annotationFilter: "kubernetes.io/gateway.class=nginx", - ingressGateway: fakeIngressGateway{ - ips: []string{"8.8.8.8"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -496,8 +561,10 @@ func testGatewayEndpoints(t *testing.T) { { title: "our controller type is dns-controller", targetNamespace: "", - ingressGateway: fakeIngressGateway{ - ips: []string{"8.8.8.8"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -519,8 +586,10 @@ func testGatewayEndpoints(t *testing.T) { { title: "different controller types are ignored", targetNamespace: "", - ingressGateway: fakeIngressGateway{ - ips: []string{"8.8.8.8"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -537,9 +606,11 @@ func testGatewayEndpoints(t *testing.T) { { title: "template for gateway if host is missing", targetNamespace: "", - ingressGateway: fakeIngressGateway{ - ips: []string{"8.8.8.8"}, - hostnames: []string{"elb.com"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8"}, + hostnames: []string{"elb.com"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -566,8 +637,10 @@ func testGatewayEndpoints(t *testing.T) { { title: "another controller annotation skipped even with template", targetNamespace: "", - ingressGateway: fakeIngressGateway{ - ips: []string{"8.8.8.8"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -585,8 +658,10 @@ func testGatewayEndpoints(t *testing.T) { { title: "multiple FQDN template hostnames", targetNamespace: "", - ingressGateway: fakeIngressGateway{ - ips: []string{"8.8.8.8"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -613,8 +688,10 @@ func testGatewayEndpoints(t *testing.T) { { title: "multiple FQDN template hostnames", targetNamespace: "", - ingressGateway: fakeIngressGateway{ - ips: []string{"8.8.8.8"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -665,8 +742,10 @@ func testGatewayEndpoints(t *testing.T) { { title: "gateway rules with annotation", targetNamespace: "", - ingressGateway: fakeIngressGateway{ - ips: []string{"8.8.8.8"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -715,8 +794,10 @@ func testGatewayEndpoints(t *testing.T) { { title: "gateway rules with hostname annotation", targetNamespace: "", - ingressGateway: fakeIngressGateway{ - ips: []string{"1.2.3.4"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"1.2.3.4"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -744,8 +825,10 @@ func testGatewayEndpoints(t *testing.T) { { title: "gateway rules with hostname annotation having multiple hostnames", targetNamespace: "", - ingressGateway: fakeIngressGateway{ - ips: []string{"1.2.3.4"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"1.2.3.4"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -778,8 +861,10 @@ func testGatewayEndpoints(t *testing.T) { { title: "gateway rules with hostname and target annotation", targetNamespace: "", - ingressGateway: fakeIngressGateway{ - ips: []string{}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{}, + }, }, configItems: []fakeGatewayConfig{ { @@ -808,8 +893,10 @@ func testGatewayEndpoints(t *testing.T) { { title: "gateway rules with annotation and custom TTL", targetNamespace: "", - ingressGateway: fakeIngressGateway{ - ips: []string{"8.8.8.8"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -847,9 +934,11 @@ func testGatewayEndpoints(t *testing.T) { { title: "template for gateway with annotation", targetNamespace: "", - ingressGateway: fakeIngressGateway{ - ips: []string{}, - hostnames: []string{}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{}, + hostnames: []string{}, + }, }, configItems: []fakeGatewayConfig{ { @@ -899,9 +988,11 @@ func testGatewayEndpoints(t *testing.T) { { title: "Ingress with empty annotation", targetNamespace: "", - ingressGateway: fakeIngressGateway{ - ips: []string{}, - hostnames: []string{}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{}, + hostnames: []string{}, + }, }, configItems: []fakeGatewayConfig{ { @@ -920,9 +1011,11 @@ func testGatewayEndpoints(t *testing.T) { title: "ignore hostname annotations", ignoreHostnameAnnotation: true, targetNamespace: "", - ingressGateway: fakeIngressGateway{ - ips: []string{"8.8.8.8"}, - hostnames: []string{"lb.com"}, + lbServices: []fakeIngressGatewayService{ + { + ips: []string{"8.8.8.8"}, + hostnames: []string{"lb.com"}, + }, }, configItems: []fakeGatewayConfig{ { @@ -969,10 +1062,17 @@ func testGatewayEndpoints(t *testing.T) { } fakeKubernetesClient := fake.NewSimpleClientset() - ingressGatewayService := ti.ingressGateway.Service() - if _, err := fakeKubernetesClient.CoreV1().Services(ingressGatewayService.Namespace).Create(ingressGatewayService); err != nil { - require.NoError(t, err) + + var fakeLoadBalancerList []string + for _, lb := range ti.lbServices { + lbService := lb.Service() + _, err := fakeKubernetesClient.CoreV1().Services(lbService.Namespace).Create(lbService) + if err != nil { + require.NoError(t, err) + } + fakeLoadBalancerList = append(fakeLoadBalancerList, lbService.Namespace+"/"+lbService.Name) } + fakeIstioClient := NewFakeConfigStore() for _, config := range configs { _, err := fakeIstioClient.Create(config) @@ -982,7 +1082,7 @@ func testGatewayEndpoints(t *testing.T) { gatewaySource, err := NewIstioGatewaySource( fakeKubernetesClient, fakeIstioClient, - ingressGatewayService.Namespace+"/"+ingressGatewayService.Name, + fakeLoadBalancerList, ti.targetNamespace, ti.annotationFilter, ti.fqdnTemplate, @@ -1004,19 +1104,24 @@ func testGatewayEndpoints(t *testing.T) { } // gateway specific helper functions -func newTestGatewaySource(ingress *v1.Service) (*gatewaySource, error) { +func newTestGatewaySource(loadBalancerList []fakeIngressGatewayService) (*gatewaySource, error) { fakeKubernetesClient := fake.NewSimpleClientset() fakeIstioClient := NewFakeConfigStore() - _, err := fakeKubernetesClient.CoreV1().Services(ingress.Namespace).Create(ingress) - if err != nil { - return nil, err + var lbList []string + for _, lb := range loadBalancerList { + lbService := lb.Service() + _, err := fakeKubernetesClient.CoreV1().Services(lbService.Namespace).Create(lbService) + if err != nil { + return nil, err + } + lbList = append(lbList, lbService.Namespace+"/"+lbService.Name) } src, err := NewIstioGatewaySource( fakeKubernetesClient, fakeIstioClient, - "istio-system/istio-ingressgateway", + lbList, "default", "", "{{.Name}}", @@ -1035,16 +1140,18 @@ func newTestGatewaySource(ingress *v1.Service) (*gatewaySource, error) { return gwsrc, nil } -type fakeIngressGateway struct { +type fakeIngressGatewayService struct { ips []string hostnames []string + namespace string + name string } -func (ig fakeIngressGateway) Service() *v1.Service { +func (ig fakeIngressGatewayService) Service() *v1.Service { svc := &v1.Service{ ObjectMeta: metav1.ObjectMeta{ - Namespace: "istio-system", - Name: "istio-ingressgateway", + Namespace: ig.namespace, + Name: ig.name, }, Status: v1.ServiceStatus{ LoadBalancer: v1.LoadBalancerStatus{ diff --git a/source/store.go b/source/store.go index 01d3ef76f..4a143e13d 100644 --- a/source/store.go +++ b/source/store.go @@ -38,21 +38,21 @@ var ErrSourceNotFound = errors.New("source not found") // Config holds shared configuration options for all Sources. type Config struct { - Namespace string - AnnotationFilter string - FQDNTemplate string - CombineFQDNAndAnnotation bool - IgnoreHostnameAnnotation bool - Compatibility string - PublishInternal bool - PublishHostIP bool - ConnectorServer string - CRDSourceAPIVersion string - CRDSourceKind string - KubeConfig string - KubeMaster string - ServiceTypeFilter []string - IstioIngressGateway string + Namespace string + AnnotationFilter string + FQDNTemplate string + CombineFQDNAndAnnotation bool + IgnoreHostnameAnnotation bool + Compatibility string + PublishInternal bool + PublishHostIP bool + ConnectorServer string + CRDSourceAPIVersion string + CRDSourceKind string + KubeConfig string + KubeMaster string + ServiceTypeFilter []string + IstioIngressGatewayServices []string } // ClientGenerator provides clients @@ -129,7 +129,7 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err if err != nil { return nil, err } - return NewIstioGatewaySource(kubernetesClient, istioClient, cfg.IstioIngressGateway, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation) + return NewIstioGatewaySource(kubernetesClient, istioClient, cfg.IstioIngressGatewayServices, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation) case "fake": return NewFakeSource(cfg.FQDNTemplate) case "connector": diff --git a/source/store_test.go b/source/store_test.go index 1ba8ad115..08045a0a8 100644 --- a/source/store_test.go +++ b/source/store_test.go @@ -113,5 +113,5 @@ func TestByNames(t *testing.T) { } var minimalConfig = &Config{ - IstioIngressGateway: "istio-system/istio-ingressgateway", + IstioIngressGatewayServices: []string{"istio-system/istio-ingressgateway"}, } From 8990be05dc4ef463e0a7ec1348ec7d3efaada55c Mon Sep 17 00:00:00 2001 From: Arief Hidayat Date: Tue, 19 Mar 2019 13:32:17 +0800 Subject: [PATCH 070/136] set log level to debug when axfr is disabled --- provider/rfc2136.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/rfc2136.go b/provider/rfc2136.go index 22356861c..b2e4ca01e 100644 --- a/provider/rfc2136.go +++ b/provider/rfc2136.go @@ -161,7 +161,7 @@ func (r rfc2136Provider) IncomeTransfer(m *dns.Msg, a string) (env chan *dns.Env func (r rfc2136Provider) List() ([]dns.RR, error) { if !r.axfr { - log.Info("axfr is disabled") + log.Debug("axfr is disabled") return make([]dns.RR, 0), nil } From e5cc15c8085db72e052a458e85a78b7d9cd4fcfd Mon Sep 17 00:00:00 2001 From: Raffaele Di Fazio Date: Fri, 22 Mar 2019 10:20:20 +0100 Subject: [PATCH 071/136] Added stability matrix and minor improvements to README --- README.md | 47 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 5c77c019d..4fdd87e83 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ ExternalDNS' current release is `v0.5`. This version allows you to keep selected * [Exoscale](https://www.exoscale.com/dns/) * [Oracle Cloud Infrastructure DNS](https://docs.cloud.oracle.com/iaas/Content/DNS/Concepts/dnszonemanagement.htm) * [Linode DNS](https://www.linode.com/docs/networking/dns/) -* [RFC2136](https://tools.ietf.org/html/rfc2136) +* [RFC2136](https://tools.ietf.org/html/rfc2136) From this release, ExternalDNS can become aware of the records it is managing (enabled via `--registry=txt`), therefore ExternalDNS can safely manage non-empty hosted zones. We strongly encourage you to use `v0.5` (or greater) with `--registry=txt` enabled and `--txt-owner-id` set to a unique value that doesn't change for the lifetime of your cluster. You might also want to run ExternalDNS in a dry run mode (`--dry-run` flag) to see the changes to be submitted to your DNS Provider API. @@ -48,11 +48,46 @@ Note that all flags can be replaced with environment variables; for instance, `--dry-run` could be replaced with `EXTERNAL_DNS_DRY_RUN=1`, or `--registry txt` could be replaced with `EXTERNAL_DNS_REGISTRY=txt`. -## There are two ways of running ExternalDNS: +## Status of providers + +ExternalDNS supports multiple DNS providers which have been implemented by the [ExternalDNS contributors](https://github.com/kubernetes-incubator/external-dns/graphs/contributors). Maintaining all of those in a central repository is a challenge and we have limited resources to test changes. This means that it is very hard to test all providers for possible regressions and, as written in the [Contributing](## Contributing) section, we encourage contributors to step in as maintainers for the individual providers and help by testing the integrations. +We define the following stability levels for providers: + +- **Stable**: Used for smoke tests before a release, used in production and maintainers are active. +- **Beta**: Community supported, well tested, but maintainers have no access to resources to execute integration tests on the real platform and/or are not using it in production. +- **Alpha**: Community provided with no support from the maintainers apart from reviewing PRs. + +The following table clarifies the current status of the providers according to the aforementioned stability levels: + +| Provider | Status | +| -------- | ------ | +| Google Cloud DNS | Stable | +| AWS Route 53 | Stable | +| AWS Service Discovery | Beta | +| AzureDNS | Beta | +| CloudFlare | Beta +| RcodeZero | Alpha | +| DigitalOcean | Alpha | +| DNSimple | Alpha | +| Infoblox | Alpha | +| Dyn | Alpha | +| OpenStack Designate | Alpha | +| PowerDNS | Alpha | +| CoreDNS | Alpha | +| Exoscale | Alpha | +| Oracle Cloud Infrastructure DNS | Alpha | +| Linode DNS | Alpha | +| RFC2136 | Alpha | + + +## Running ExternalDNS: + +The are two ways of running ExternalDNS: + * Deploying to a Cluster * Running Locally -## Deploying to a Cluster +### Deploying to a Cluster The following tutorials are provided: @@ -74,16 +109,16 @@ The following tutorials are provided: * [Linode](docs/tutorials/linode.md) * [RFC2136](docs/tutorials/rfc2136.md) -## Running Locally +### Running Locally -### Technical Requirements +#### Technical Requirements Make sure you have the following prerequisites: * A local Go 1.7+ development environment. * Access to a Google/AWS account with the DNS API enabled. * Access to a Kubernetes cluster that supports exposing Services, e.g. GKE. -### Setup Steps +#### Setup Steps First, get ExternalDNS: From 756c66066d1ad9bace1835afcb2746f42fef4389 Mon Sep 17 00:00:00 2001 From: Michael Venezia Date: Mon, 25 Mar 2019 14:05:48 -0400 Subject: [PATCH 072/136] Bumping istio to 1.1.0, updating fake GatewayConfigStore Get method to work with 1.1.0 --- Gopkg.lock | 158 +++++++++++++++++++++++++++++++++-------- Gopkg.toml | 2 +- source/gateway_test.go | 3 +- 3 files changed, 132 insertions(+), 31 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index dab7e4a09..b412e2367 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -196,6 +196,24 @@ revision = "d1105abc03b313d7b8d9b04364f6bd053b346e59" version = "v0.14.0" +[[projects]] + digest = "1:a2d7fd2421a8578c4adea9830bef9d17366e4640edb57735dff2d1dc68815a9e" + name = "github.com/envoyproxy/go-control-plane" + packages = [ + "envoy/api/v2", + "envoy/api/v2/auth", + "envoy/api/v2/cluster", + "envoy/api/v2/core", + "envoy/api/v2/endpoint", + "envoy/api/v2/listener", + "envoy/api/v2/route", + "envoy/config/grpc_credential/v2alpha", + "envoy/type", + ] + pruneopts = "" + revision = "0ad6fa1cf0b9b6ca8f3617a7188a568e81f40b87" + version = "v0.6.9" + [[projects]] digest = "1:e17d18b233f506404061c27ac4a08624dad38dcd0d49f9cfdae67a7772a4fb8c" name = "github.com/exoscale/egoscale" @@ -239,7 +257,10 @@ [[projects]] digest = "1:54d5c6a784a9de9c836fc070d51d0689c3e99ee6d24dba8a36f0762039dae830" name = "github.com/gogo/googleapis" - packages = ["google/rpc"] + packages = [ + "google/api", + "google/rpc", + ] pruneopts = "" revision = "8558fb44d2f1fc223118afc694129d2c2d2924d1" version = "v1.1.0" @@ -272,13 +293,11 @@ digest = "1:3dd078fda7500c341bc26cfbc6c6a34614f295a2457149fc1045cab767cbcf18" name = "github.com/golang/protobuf" packages = [ - "jsonpb", "proto", "protoc-gen-go/descriptor", "ptypes", "ptypes/any", "ptypes/duration", - "ptypes/struct", "ptypes/timestamp", ] pruneopts = "" @@ -293,19 +312,6 @@ pruneopts = "" revision = "e89373fe6b4a7413d7acd6da1725b83ef713e6e4" -[[projects]] - digest = "1:f9f45f75f332e03fc7e9fe9188ea4e1ce4d14779ef34fa1b023da67518e36327" - name = "github.com/google/go-cmp" - packages = [ - "cmp", - "cmp/internal/diff", - "cmp/internal/function", - "cmp/internal/value", - ] - pruneopts = "" - revision = "3af367b6b30c263d47e8895973edcca9a49cf029" - version = "v0.2.0" - [[projects]] branch = "master" digest = "1:9abc49f39e3e23e262594bb4fb70abf74c0c99e94f99153f43b143805e850719" @@ -467,6 +473,14 @@ revision = "7edbc87f0140b7561dbc20458877a56bdded5eb8" version = "v0.3.0" +[[projects]] + digest = "1:a404c6713d3e7fb61746c8bc355c6009ddb8fbb2e376ce33c03881645217f1a1" + name = "github.com/lyft/protoc-gen-validate" + packages = ["validate"] + pruneopts = "" + revision = "ff6f7a9bc2e5fe006509b9f8c7594c41a953d50f" + version = "v0.0.14" + [[projects]] branch = "master" digest = "1:63722a4b1e1717be7b98fc686e0b30d5e7f734b9e93d7dee86293b6deab7ea28" @@ -574,15 +588,16 @@ version = "v1.0.0" [[projects]] - digest = "1:2f69dc6b2685b31a1a410ef697410aa8a669704fb201d45dbd8c1911728afa75" + digest = "1:6f218995d6a74636cfcab45ce03005371e682b4b9bee0e5eb0ccfd83ef85364f" name = "github.com/prometheus/client_golang" packages = [ "prometheus", + "prometheus/internal", "prometheus/promhttp", ] pruneopts = "" - revision = "967789050ba94deca04a5e84cce8ad472ce313c1" - version = "v0.9.0-pre1" + revision = "505eaef017263e299324067d40ca2c48f6a2cf50" + version = "v0.9.2" [[projects]] branch = "master" @@ -683,6 +698,17 @@ pruneopts = "" revision = "ac974c61c2f990f4115b119354b5e0b47550e888" +[[projects]] + branch = "master" + digest = "1:cd1c3692d12b8c8e4a2d8af87de070b21a1c8e67fdd1d8dc31aa14791394fa17" + name = "github.com/yl2chen/cidranger" + packages = [ + ".", + "net", + ] + pruneopts = "" + revision = "928b519e5268fe386d0f7decce320205cc09ca95" + [[projects]] digest = "1:74f86c458e82e1c4efbab95233e0cf51b7cc02dc03193be9f62cd81224e10401" name = "go.uber.org/atomic" @@ -905,8 +931,8 @@ version = "v2.2.1" [[projects]] - branch = "release-1.0" - digest = "1:bc43af6616d8ca12a7b8e806874387f0f1e181c08f547e9cd77f6cbac8cefd86" + branch = "release-1.1" + digest = "1:17f75d4a08eab0d4bb4a862a6a0e6789c9065063b450c7850c835ccde85e0981" name = "istio.io/api" packages = [ "authentication/v1alpha1", @@ -914,26 +940,31 @@ "mixer/v1", "mixer/v1/config/client", "networking/v1alpha3", + "policy/v1beta1", "rbac/v1alpha1", ] pruneopts = "" - revision = "76349c53b87f03f1e610b3aa3843dba3c38138d7" + revision = "db16d82d3672586b4beec10c1af0643d518cb454" [[projects]] - digest = "1:7eb25280e1f610470bb0c43ab6c91573cfc78672a58542106b9b71705581429a" + digest = "1:901e05af6afa9e7b49028d97c5f39c86470bec561153e0cc931dfd6c9c5ec2ed" name = "istio.io/istio" packages = [ + "galley/pkg/metadata", + "galley/pkg/runtime/resource", "pilot/pkg/config/kube/crd", "pilot/pkg/model", "pilot/pkg/model/test", "pilot/pkg/serviceregistry/kube", "pkg/cache", + "pkg/features/pilot", "pkg/kube", "pkg/log", + "pkg/spiffe", ] pruneopts = "" - revision = "42773aacced474d97159902d20579a25b1f98106" - version = "1.0.1" + revision = "2b1331886076df103179e3da5dc9077fed59c989" + version = "1.1.1" [[projects]] digest = "1:f420c8548c93242d8e5dcfa5b34e0243883b4e660f65076e869daafac877144d" @@ -974,7 +1005,8 @@ version = "kubernetes-1.11.0" [[projects]] - digest = "1:66d1421ecff35bc48ee0b11a3f891f3af6f775ed6bb1d3e0deeaba221bf42490" + branch = "release-1.11" + digest = "1:545b132408a3b9c330cd062bb336b91d9578576623b440c753d81b45c968d7d3" name = "k8s.io/apiextensions-apiserver" packages = [ "pkg/apis/apiextensions", @@ -984,8 +1016,7 @@ "pkg/client/clientset/clientset/typed/apiextensions/v1beta1", ] pruneopts = "" - revision = "8e7f43002fec5394a8d96ebca781aa9d4b37aaef" - version = "kubernetes-1.10.4" + revision = "57c81b676ab11a0924f53d62f3fdccb8e784df03" [[projects]] digest = "1:b6b2fb7b4da1ac973b64534ace2299a02504f16bc7820cb48edb8ca4077183e1" @@ -1043,6 +1074,47 @@ packages = [ "discovery", "discovery/fake", + "informers", + "informers/admissionregistration", + "informers/admissionregistration/v1alpha1", + "informers/admissionregistration/v1beta1", + "informers/apps", + "informers/apps/v1", + "informers/apps/v1beta1", + "informers/apps/v1beta2", + "informers/autoscaling", + "informers/autoscaling/v1", + "informers/autoscaling/v2beta1", + "informers/batch", + "informers/batch/v1", + "informers/batch/v1beta1", + "informers/batch/v2alpha1", + "informers/certificates", + "informers/certificates/v1beta1", + "informers/core", + "informers/core/v1", + "informers/events", + "informers/events/v1beta1", + "informers/extensions", + "informers/extensions/v1beta1", + "informers/internalinterfaces", + "informers/networking", + "informers/networking/v1", + "informers/policy", + "informers/policy/v1beta1", + "informers/rbac", + "informers/rbac/v1", + "informers/rbac/v1alpha1", + "informers/rbac/v1beta1", + "informers/scheduling", + "informers/scheduling/v1alpha1", + "informers/scheduling/v1beta1", + "informers/settings", + "informers/settings/v1alpha1", + "informers/storage", + "informers/storage/v1", + "informers/storage/v1alpha1", + "informers/storage/v1beta1", "kubernetes", "kubernetes/fake", "kubernetes/scheme", @@ -1104,6 +1176,31 @@ "kubernetes/typed/storage/v1alpha1/fake", "kubernetes/typed/storage/v1beta1", "kubernetes/typed/storage/v1beta1/fake", + "listers/admissionregistration/v1alpha1", + "listers/admissionregistration/v1beta1", + "listers/apps/v1", + "listers/apps/v1beta1", + "listers/apps/v1beta2", + "listers/autoscaling/v1", + "listers/autoscaling/v2beta1", + "listers/batch/v1", + "listers/batch/v1beta1", + "listers/batch/v2alpha1", + "listers/certificates/v1beta1", + "listers/core/v1", + "listers/events/v1beta1", + "listers/extensions/v1beta1", + "listers/networking/v1", + "listers/policy/v1beta1", + "listers/rbac/v1", + "listers/rbac/v1alpha1", + "listers/rbac/v1beta1", + "listers/scheduling/v1alpha1", + "listers/scheduling/v1beta1", + "listers/settings/v1alpha1", + "listers/storage/v1", + "listers/storage/v1alpha1", + "listers/storage/v1beta1", "pkg/apis/clientauthentication", "pkg/apis/clientauthentication/v1alpha1", "pkg/apis/clientauthentication/v1beta1", @@ -1218,11 +1315,16 @@ "k8s.io/apimachinery/pkg/runtime", "k8s.io/apimachinery/pkg/runtime/schema", "k8s.io/apimachinery/pkg/runtime/serializer", + "k8s.io/apimachinery/pkg/util/wait", + "k8s.io/client-go/informers", + "k8s.io/client-go/informers/core/v1", + "k8s.io/client-go/informers/extensions/v1beta1", "k8s.io/client-go/kubernetes", "k8s.io/client-go/kubernetes/fake", "k8s.io/client-go/plugin/pkg/client/auth", "k8s.io/client-go/rest", "k8s.io/client-go/rest/fake", + "k8s.io/client-go/tools/cache", "k8s.io/client-go/tools/clientcmd", ] solver-name = "gps-cdcl" diff --git a/Gopkg.toml b/Gopkg.toml index 05fcda662..0489ac11d 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -78,7 +78,7 @@ ignored = ["github.com/kubernetes/repo-infra/kazel"] [[constraint]] name = "istio.io/istio" - version = "1.0.0" + version = "1.1.0" [[override]] name = "github.com/golang/glog" diff --git a/source/gateway_test.go b/source/gateway_test.go index 2d702f6eb..763aedfb3 100644 --- a/source/gateway_test.go +++ b/source/gateway_test.go @@ -1224,13 +1224,12 @@ func (f *fakeConfigStore) ConfigDescriptor() istiomodel.ConfigDescriptor { return f.descriptor } -func (f *fakeConfigStore) Get(typ, name, namespace string) (config *istiomodel.Config, exists bool) { +func (f *fakeConfigStore) Get(typ, name, namespace string) (config *istiomodel.Config) { f.RLock() defer f.RUnlock() if cfg, _ := f.get(typ, name, namespace); cfg != nil { config = cfg - exists = true } return From 94ea610597f4876832b5e1903e98aa0dbf187a7d Mon Sep 17 00:00:00 2001 From: njuettner Date: Tue, 26 Mar 2019 15:24:27 +0100 Subject: [PATCH 073/136] Release v0.5.12 --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfc935ef1..63c0371ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +## v0.5.12 - 2019-03-26 + + - Bumping istio to 1.1.0 (#942) @venezia + - Docs: Added stability matrix and minor improvements to README (#938) @Raffo + - Docs: Added a reference to a blogpost which uses ExternalDNS in a CI/CD setup (#928) @vanhumbeecka + - Use k8s informer cache instead of making active API GET requests (#917) @jlamillan + - Docs: Tiny clarification about two available deployment methods (#935) @przemolb + - Add support for multiple Istio IngressGateway LoadBalancer Services (#907) @LorbusChris + - Set log level to debug when axfr is disabled (#932) @arief-hidayat + - Infoblox provider support for DNS view (#895) @dsbrng25b + - Add RcodeZero Anycast DNS provider (#874) @dklesev + - Docs: Dropping owners (#929) @njuettner + - Docs: Added description for multiple dns name (#911) @st1t + - Docs: Clarify that hosted zone identifier is to be used (#915) @dirkgomez + - Docs: Make dep step which may be needed to run make build (#913) @dirkgomez + - PowerDNS: Fixed Domain Filter Bug (#827) @anandsinghkunwar + - Allow hostname annotations to be ignored (#745) @anandkumarpatel + - RFC2136: Fixed typo in debug output (#899) @hpandeycodeit + ## v0.5.11 - 2019-02-11 - Fix constant updating issue introduced with v0.5.10 (#886) @jhohertz From 2a91cce25d2d6a26a16fd6f562684e311b20affa Mon Sep 17 00:00:00 2001 From: njuettner Date: Tue, 26 Mar 2019 15:39:10 +0100 Subject: [PATCH 074/136] Release v0.5.12 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9b68de1d6..bc0f5581d 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,7 @@ Here's a rough outline on what is to come (subject to change): - [x] Support for OpenStack Designate - [x] Support for PowerDNS - [x] Support for Linode +- [x] Support for RcodeZero ### v0.6 From fb8b52fbfdc9396dee3343e054c5a616a2cb38c2 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Wed, 27 Mar 2019 12:35:39 +0100 Subject: [PATCH 075/136] Reduce verbosity of infoblox provider logs --- provider/infoblox.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/infoblox.go b/provider/infoblox.go index 65913410d..5d2038061 100644 --- a/provider/infoblox.go +++ b/provider/infoblox.go @@ -222,7 +222,7 @@ func (p *InfobloxProvider) mapChanges(zones []ibclient.ZoneAuth, changes *plan.C mapChange := func(changeMap infobloxChangeMap, change *endpoint.Endpoint) { zone := p.findZone(zones, change.DNSName) if zone == nil { - logrus.Infof("Ignoring changes to '%s' because a suitable Infoblox DNS zone was not found.", change.DNSName) + logrus.Debugf("Ignoring changes to '%s' because a suitable Infoblox DNS zone was not found.", change.DNSName) return } // Ensure the record type is suitable From 272bfd1e1262e28efbc459e790ac0440d7834d01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=B6sl?= Date: Wed, 27 Mar 2019 17:37:13 +0100 Subject: [PATCH 076/136] remove unnecessary parameter check when started with insecure flag --- provider/rfc2136.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/rfc2136.go b/provider/rfc2136.go index b2e4ca01e..0943864e2 100644 --- a/provider/rfc2136.go +++ b/provider/rfc2136.go @@ -64,7 +64,7 @@ type rfc2136Actions interface { // NewRfc2136Provider is a factory function for OpenStack rfc2136 providers func NewRfc2136Provider(host string, port int, zoneName string, insecure bool, keyName string, secret string, secretAlg string, axfr bool, domainFilter DomainFilter, dryRun bool, actions rfc2136Actions) (Provider, error) { secretAlgChecked, ok := tsigAlgs[secretAlg] - if !ok { + if !ok && !insecure { return nil, errors.Errorf("%s is not supported TSIG algorithm", secretAlg) } From d190510a627cb1ebbd64212fc4eff2067d7ade50 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 28 Mar 2019 21:27:32 +0100 Subject: [PATCH 077/136] Remove passwords from config output based on tag --- pkg/apis/externaldns/types.go | 34 ++++++++++++++++++------------ pkg/apis/externaldns/types_test.go | 2 ++ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index a3257fc1d..ad497fb87 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -18,6 +18,7 @@ package externaldns import ( "fmt" + "reflect" "strconv" "time" @@ -71,18 +72,18 @@ type Config struct { InfobloxGridHost string InfobloxWapiPort int InfobloxWapiUsername string - InfobloxWapiPassword string + InfobloxWapiPassword string `secure:"yes"` InfobloxWapiVersion string InfobloxSSLVerify bool InfobloxView string DynCustomerName string DynUsername string - DynPassword string + DynPassword string `secure:"yes"` DynMinTTLSeconds int OCIConfigFile string InMemoryZones []string PDNSServer string - PDNSAPIKey string + PDNSAPIKey string `secure:"yes"` PDNSTLSEnabled bool TLSCA string TLSClientCert string @@ -99,8 +100,8 @@ type Config struct { LogLevel string TXTCacheInterval time.Duration ExoscaleEndpoint string - ExoscaleAPIKey string - ExoscaleAPISecret string + ExoscaleAPIKey string `secure:"yes"` + ExoscaleAPISecret string `secure:"yes"` CRDSourceAPIVersion string CRDSourceKind string ServiceTypeFilter []string @@ -109,7 +110,7 @@ type Config struct { RFC2136Zone string RFC2136Insecure bool RFC2136TSIGKeyName string - RFC2136TSIGSecret string + RFC2136TSIGSecret string `secure:"yes"` RFC2136TSIGSecretAlg string RFC2136TAXFR bool } @@ -195,14 +196,19 @@ func NewConfig() *Config { func (cfg *Config) String() string { // prevent logging of sensitive information temp := *cfg - if temp.DynPassword != "" { - temp.DynPassword = passwordMask - } - if temp.InfobloxWapiPassword != "" { - temp.InfobloxWapiPassword = passwordMask - } - if temp.PDNSAPIKey != "" { - temp.PDNSAPIKey = "" + + t := reflect.TypeOf(temp) + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + if val, ok := f.Tag.Lookup("secure"); ok && val == "yes" { + if f.Type.Kind() != reflect.String { + continue + } + v := reflect.ValueOf(&temp).Elem().Field(i) + if v.String() != "" { + v.SetString(passwordMask) + } + } } return fmt.Sprintf("%+v", temp) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index 24b3eddc4..ab474b498 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -409,6 +409,7 @@ func TestPasswordsNotLogged(t *testing.T) { DynPassword: "dyn-pass", InfobloxWapiPassword: "infoblox-pass", PDNSAPIKey: "pdns-api-key", + RFC2136TSIGSecret: "tsig-secret", } s := cfg.String() @@ -416,4 +417,5 @@ func TestPasswordsNotLogged(t *testing.T) { assert.False(t, strings.Contains(s, "dyn-pass")) assert.False(t, strings.Contains(s, "infoblox-pass")) assert.False(t, strings.Contains(s, "pdns-api-key")) + assert.False(t, strings.Contains(s, "tsig-secret")) } From 252dc2b0f5958033d559527f716dec25984c8f79 Mon Sep 17 00:00:00 2001 From: Tim Hobbs Date: Fri, 29 Mar 2019 11:37:59 +0100 Subject: [PATCH 078/136] Remove superfluous trailing period from hostname Tutorial specifies version >0.4 which also removed the requirement for a trailing period. New users could misunderstand the trailing dot as a significant syntax. Removing the dot simplifies the configuration of the annotation. --- docs/tutorials/aws.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorials/aws.md b/docs/tutorials/aws.md index 208d77092..807b9a163 100644 --- a/docs/tutorials/aws.md +++ b/docs/tutorials/aws.md @@ -216,7 +216,7 @@ kind: Service metadata: name: nginx annotations: - external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.my-org.com. + external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.my-org.com spec: type: LoadBalancer ports: @@ -312,7 +312,7 @@ kind: Service metadata: name: nginx annotations: - external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.my-org.com. + external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.my-org.com external-dns.alpha.kubernetes.io/ttl: 60 spec: ... From a70fff1560909c70a369ded30717646fdacc0941 Mon Sep 17 00:00:00 2001 From: igork Date: Tue, 2 Apr 2019 13:22:30 +0200 Subject: [PATCH 079/136] describe how to check if your cluster has a RBAC --- docs/tutorials/aws.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/aws.md b/docs/tutorials/aws.md index 208d77092..5eab9fa38 100644 --- a/docs/tutorials/aws.md +++ b/docs/tutorials/aws.md @@ -65,7 +65,7 @@ In this case it's the ones shown above but your's will differ. ## Deploy ExternalDNS Connect your `kubectl` client to the cluster you want to test ExternalDNS with. -Then apply one of the following manifests file to deploy ExternalDNS. +Then apply one of the following manifests file to deploy ExternalDNS. You can check if your cluster has RBAC by `kubectl api-versions | grep rbac.authorization.k8s.io`. ### Manifest (for clusters without RBAC enabled) ```yaml From 2f34d2d2018404ff2d396d32eec2363f4dd1df86 Mon Sep 17 00:00:00 2001 From: Jason Fillo Date: Tue, 9 Apr 2019 09:25:21 -0500 Subject: [PATCH 080/136] aws-r53: adding china ELB endpoints and hosted zone id's --- provider/aws.go | 72 +++++++++++++++++++++++--------------------- provider/aws_test.go | 4 +++ 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/provider/aws.go b/provider/aws.go index 7bfd17ac0..a9419e6e9 100644 --- a/provider/aws.go +++ b/provider/aws.go @@ -43,40 +43,44 @@ var ( // see: https://docs.aws.amazon.com/general/latest/gr/rande.html#elb_region canonicalHostedZones = map[string]string{ // Application Load Balancers and Classic Load Balancers - "us-east-2.elb.amazonaws.com": "Z3AADJGX6KTTL2", - "us-east-1.elb.amazonaws.com": "Z35SXDOTRQ7X7K", - "us-west-1.elb.amazonaws.com": "Z368ELLRRE2KJ0", - "us-west-2.elb.amazonaws.com": "Z1H1FL5HABSF5", - "ca-central-1.elb.amazonaws.com": "ZQSVJUPU6J1EY", - "ap-south-1.elb.amazonaws.com": "ZP97RAFLXTNZK", - "ap-northeast-2.elb.amazonaws.com": "ZWKZPGTI48KDX", - "ap-northeast-3.elb.amazonaws.com": "Z5LXEXXYW11ES", - "ap-southeast-1.elb.amazonaws.com": "Z1LMS91P8CMLE5", - "ap-southeast-2.elb.amazonaws.com": "Z1GM3OXH4ZPM65", - "ap-northeast-1.elb.amazonaws.com": "Z14GRHDCWA56QT", - "eu-central-1.elb.amazonaws.com": "Z215JYRZR1TBD5", - "eu-west-1.elb.amazonaws.com": "Z32O12XQLNTSW2", - "eu-west-2.elb.amazonaws.com": "ZHURV8PSTC4K8", - "eu-west-3.elb.amazonaws.com": "Z3Q77PNBQS71R4", - "eu-north-1.elb.amazonaws.com": "Z23TAZ6LKFMNIO", - "sa-east-1.elb.amazonaws.com": "Z2P70J7HTTTPLU", - // Network Load Balancers - "elb.us-east-2.amazonaws.com": "ZLMOA37VPKANP", - "elb.us-east-1.amazonaws.com": "Z26RNL4JYFTOTI", - "elb.us-west-1.amazonaws.com": "Z24FKFUX50B4VW", - "elb.us-west-2.amazonaws.com": "Z18D5FSROUN65G", - "elb.ca-central-1.amazonaws.com": "Z2EPGBW3API2WT", - "elb.ap-south-1.amazonaws.com": "ZVDDRBQ08TROA", - "elb.ap-northeast-2.amazonaws.com": "ZIBE1TIR4HY56", - "elb.ap-southeast-1.amazonaws.com": "ZKVM4W9LS7TM", - "elb.ap-southeast-2.amazonaws.com": "ZCT6FZBF4DROD", - "elb.ap-northeast-1.amazonaws.com": "Z31USIVHYNEOWT", - "elb.eu-central-1.amazonaws.com": "Z3F0SRJ5LGBH90", - "elb.eu-west-1.amazonaws.com": "Z2IFOLAFXWLO4F", - "elb.eu-west-2.amazonaws.com": "ZD4D7Y8KGAS4G", - "elb.eu-west-3.amazonaws.com": "Z1CMS0P5QUZ6D5", - "elb.eu-north-1.amazonaws.com": "Z1UDT6IFJ4EJM", - "elb.sa-east-1.amazonaws.com": "ZTK26PT1VY4CU", + "us-east-2.elb.amazonaws.com": "Z3AADJGX6KTTL2", + "us-east-1.elb.amazonaws.com": "Z35SXDOTRQ7X7K", + "us-west-1.elb.amazonaws.com": "Z368ELLRRE2KJ0", + "us-west-2.elb.amazonaws.com": "Z1H1FL5HABSF5", + "ca-central-1.elb.amazonaws.com": "ZQSVJUPU6J1EY", + "ap-south-1.elb.amazonaws.com": "ZP97RAFLXTNZK", + "ap-northeast-2.elb.amazonaws.com": "ZWKZPGTI48KDX", + "ap-northeast-3.elb.amazonaws.com": "Z5LXEXXYW11ES", + "ap-southeast-1.elb.amazonaws.com": "Z1LMS91P8CMLE5", + "ap-southeast-2.elb.amazonaws.com": "Z1GM3OXH4ZPM65", + "ap-northeast-1.elb.amazonaws.com": "Z14GRHDCWA56QT", + "eu-central-1.elb.amazonaws.com": "Z215JYRZR1TBD5", + "eu-west-1.elb.amazonaws.com": "Z32O12XQLNTSW2", + "eu-west-2.elb.amazonaws.com": "ZHURV8PSTC4K8", + "eu-west-3.elb.amazonaws.com": "Z3Q77PNBQS71R4", + "eu-north-1.elb.amazonaws.com": "Z23TAZ6LKFMNIO", + "sa-east-1.elb.amazonaws.com": "Z2P70J7HTTTPLU", + "cn-north-1.elb.amazonaws.com.cn" "Z3BX2TMKNYI13Y", + "cn-northwest-1.elb.amazonaws.com.cn" "Z3BX2TMKNYI13Y", + // Network Load Balance + "elb.us-east-2.amazonaws.com": "ZLMOA37VPKANP", + "elb.us-east-1.amazonaws.com": "Z26RNL4JYFTOTI", + "elb.us-west-1.amazonaws.com": "Z24FKFUX50B4VW", + "elb.us-west-2.amazonaws.com": "Z18D5FSROUN65G", + "elb.ca-central-1.amazonaws.com": "Z2EPGBW3API2WT", + "elb.ap-south-1.amazonaws.com": "ZVDDRBQ08TROA", + "elb.ap-northeast-2.amazonaws.com": "ZIBE1TIR4HY56", + "elb.ap-southeast-1.amazonaws.com": "ZKVM4W9LS7TM", + "elb.ap-southeast-2.amazonaws.com": "ZCT6FZBF4DROD", + "elb.ap-northeast-1.amazonaws.com": "Z31USIVHYNEOWT", + "elb.eu-central-1.amazonaws.com": "Z3F0SRJ5LGBH90", + "elb.eu-west-1.amazonaws.com": "Z2IFOLAFXWLO4F", + "elb.eu-west-2.amazonaws.com": "ZD4D7Y8KGAS4G", + "elb.eu-west-3.amazonaws.com": "Z1CMS0P5QUZ6D5", + "elb.eu-north-1.amazonaws.com": "Z1UDT6IFJ4EJM", + "elb.sa-east-1.amazonaws.com": "ZTK26PT1VY4CU", + "elb.cn-north-1.amazonaws.com.cn": "Z3QFB96KMJ7ED6", + "elb.cn-northwest-1.amazonaws.com.cn": "ZQEIKTCZ8352D", } ) diff --git a/provider/aws_test.go b/provider/aws_test.go index f4ca41f6e..a1e626a77 100644 --- a/provider/aws_test.go +++ b/provider/aws_test.go @@ -898,6 +898,8 @@ func TestAWSCanonicalHostedZone(t *testing.T) { {"foo.eu-west-2.elb.amazonaws.com", "ZHURV8PSTC4K8"}, {"foo.eu-west-3.elb.amazonaws.com", "Z3Q77PNBQS71R4"}, {"foo.sa-east-1.elb.amazonaws.com", "Z2P70J7HTTTPLU"}, + {"foo.cn-north-1.elb.amazonaws.com.cn", "Z3BX2TMKNYI13Y"}, + {"foo.cn-northwest-1.elb.amazonaws.com.cn", "Z3BX2TMKNYI13Y"}, // Network Load Balancers {"foo.elb.us-east-2.amazonaws.com", "ZLMOA37VPKANP"}, {"foo.elb.us-east-1.amazonaws.com", "Z26RNL4JYFTOTI"}, @@ -914,6 +916,8 @@ func TestAWSCanonicalHostedZone(t *testing.T) { {"foo.elb.eu-west-2.amazonaws.com", "ZD4D7Y8KGAS4G"}, {"foo.elb.eu-west-3.amazonaws.com", "Z1CMS0P5QUZ6D5"}, {"foo.elb.sa-east-1.amazonaws.com", "ZTK26PT1VY4CU"}, + {"foo.elb.cn-north-1.amazonaws.com.cn", "Z3QFB96KMJ7ED6"}, + {"foo.elb.cn-northwest-1.amazonaws.com.cn". "ZQEIKTCZ8352D"}, // No Load Balancer {"foo.example.org", ""}, } { From 86bd0108ef0f62c117216937a28cb8f43670b84d Mon Sep 17 00:00:00 2001 From: Jason Fillo Date: Tue, 9 Apr 2019 09:24:16 -0500 Subject: [PATCH 081/136] aws-r53: adding china ELB endpoints and hosted zone id's. fixed spacing --- provider/aws_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/provider/aws_test.go b/provider/aws_test.go index a1e626a77..5a591739e 100644 --- a/provider/aws_test.go +++ b/provider/aws_test.go @@ -899,7 +899,7 @@ func TestAWSCanonicalHostedZone(t *testing.T) { {"foo.eu-west-3.elb.amazonaws.com", "Z3Q77PNBQS71R4"}, {"foo.sa-east-1.elb.amazonaws.com", "Z2P70J7HTTTPLU"}, {"foo.cn-north-1.elb.amazonaws.com.cn", "Z3BX2TMKNYI13Y"}, - {"foo.cn-northwest-1.elb.amazonaws.com.cn", "Z3BX2TMKNYI13Y"}, + {"foo.cn-northwest-1.elb.amazonaws.com.cn", "Z3BX2TMKNYI13Y"}, // Network Load Balancers {"foo.elb.us-east-2.amazonaws.com", "ZLMOA37VPKANP"}, {"foo.elb.us-east-1.amazonaws.com", "Z26RNL4JYFTOTI"}, @@ -917,7 +917,7 @@ func TestAWSCanonicalHostedZone(t *testing.T) { {"foo.elb.eu-west-3.amazonaws.com", "Z1CMS0P5QUZ6D5"}, {"foo.elb.sa-east-1.amazonaws.com", "ZTK26PT1VY4CU"}, {"foo.elb.cn-north-1.amazonaws.com.cn", "Z3QFB96KMJ7ED6"}, - {"foo.elb.cn-northwest-1.amazonaws.com.cn". "ZQEIKTCZ8352D"}, + {"foo.elb.cn-northwest-1.amazonaws.com.cn". "ZQEIKTCZ8352D"}, // No Load Balancer {"foo.example.org", ""}, } { From 388119206758d4f4da5b40fd5f6e18524b037a89 Mon Sep 17 00:00:00 2001 From: Jason Fillo Date: Tue, 9 Apr 2019 09:58:10 -0500 Subject: [PATCH 082/136] aws-r53: adding china ELB endpoints and hosted zone id's. corrected formatting --- provider/aws.go | 38 +++++++++++++++++++------------------- provider/aws_test.go | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/provider/aws.go b/provider/aws.go index a9419e6e9..c42d460d2 100644 --- a/provider/aws.go +++ b/provider/aws.go @@ -43,25 +43,25 @@ var ( // see: https://docs.aws.amazon.com/general/latest/gr/rande.html#elb_region canonicalHostedZones = map[string]string{ // Application Load Balancers and Classic Load Balancers - "us-east-2.elb.amazonaws.com": "Z3AADJGX6KTTL2", - "us-east-1.elb.amazonaws.com": "Z35SXDOTRQ7X7K", - "us-west-1.elb.amazonaws.com": "Z368ELLRRE2KJ0", - "us-west-2.elb.amazonaws.com": "Z1H1FL5HABSF5", - "ca-central-1.elb.amazonaws.com": "ZQSVJUPU6J1EY", - "ap-south-1.elb.amazonaws.com": "ZP97RAFLXTNZK", - "ap-northeast-2.elb.amazonaws.com": "ZWKZPGTI48KDX", - "ap-northeast-3.elb.amazonaws.com": "Z5LXEXXYW11ES", - "ap-southeast-1.elb.amazonaws.com": "Z1LMS91P8CMLE5", - "ap-southeast-2.elb.amazonaws.com": "Z1GM3OXH4ZPM65", - "ap-northeast-1.elb.amazonaws.com": "Z14GRHDCWA56QT", - "eu-central-1.elb.amazonaws.com": "Z215JYRZR1TBD5", - "eu-west-1.elb.amazonaws.com": "Z32O12XQLNTSW2", - "eu-west-2.elb.amazonaws.com": "ZHURV8PSTC4K8", - "eu-west-3.elb.amazonaws.com": "Z3Q77PNBQS71R4", - "eu-north-1.elb.amazonaws.com": "Z23TAZ6LKFMNIO", - "sa-east-1.elb.amazonaws.com": "Z2P70J7HTTTPLU", - "cn-north-1.elb.amazonaws.com.cn" "Z3BX2TMKNYI13Y", - "cn-northwest-1.elb.amazonaws.com.cn" "Z3BX2TMKNYI13Y", + "us-east-2.elb.amazonaws.com": "Z3AADJGX6KTTL2", + "us-east-1.elb.amazonaws.com": "Z35SXDOTRQ7X7K", + "us-west-1.elb.amazonaws.com": "Z368ELLRRE2KJ0", + "us-west-2.elb.amazonaws.com": "Z1H1FL5HABSF5", + "ca-central-1.elb.amazonaws.com": "ZQSVJUPU6J1EY", + "ap-south-1.elb.amazonaws.com": "ZP97RAFLXTNZK", + "ap-northeast-2.elb.amazonaws.com": "ZWKZPGTI48KDX", + "ap-northeast-3.elb.amazonaws.com": "Z5LXEXXYW11ES", + "ap-southeast-1.elb.amazonaws.com": "Z1LMS91P8CMLE5", + "ap-southeast-2.elb.amazonaws.com": "Z1GM3OXH4ZPM65", + "ap-northeast-1.elb.amazonaws.com": "Z14GRHDCWA56QT", + "eu-central-1.elb.amazonaws.com": "Z215JYRZR1TBD5", + "eu-west-1.elb.amazonaws.com": "Z32O12XQLNTSW2", + "eu-west-2.elb.amazonaws.com": "ZHURV8PSTC4K8", + "eu-west-3.elb.amazonaws.com": "Z3Q77PNBQS71R4", + "eu-north-1.elb.amazonaws.com": "Z23TAZ6LKFMNIO", + "sa-east-1.elb.amazonaws.com": "Z2P70J7HTTTPLU", + "cn-north-1.elb.amazonaws.com.cn": "Z3BX2TMKNYI13Y", + "cn-northwest-1.elb.amazonaws.com.cn": "Z3BX2TMKNYI13Y", // Network Load Balance "elb.us-east-2.amazonaws.com": "ZLMOA37VPKANP", "elb.us-east-1.amazonaws.com": "Z26RNL4JYFTOTI", diff --git a/provider/aws_test.go b/provider/aws_test.go index 5a591739e..766014b99 100644 --- a/provider/aws_test.go +++ b/provider/aws_test.go @@ -917,7 +917,7 @@ func TestAWSCanonicalHostedZone(t *testing.T) { {"foo.elb.eu-west-3.amazonaws.com", "Z1CMS0P5QUZ6D5"}, {"foo.elb.sa-east-1.amazonaws.com", "ZTK26PT1VY4CU"}, {"foo.elb.cn-north-1.amazonaws.com.cn", "Z3QFB96KMJ7ED6"}, - {"foo.elb.cn-northwest-1.amazonaws.com.cn". "ZQEIKTCZ8352D"}, + {"foo.elb.cn-northwest-1.amazonaws.com.cn", "ZQEIKTCZ8352D"}, // No Load Balancer {"foo.example.org", ""}, } { From 76c09dab1feedde1f0a3f7bd0703d54a1d56d5ca Mon Sep 17 00:00:00 2001 From: njuettner Date: Tue, 2 Apr 2019 17:25:17 +0200 Subject: [PATCH 083/136] Switch to go modules --- .golangci.yml | 19 + .travis.yml | 20 +- Dockerfile | 6 +- Gopkg.lock | 1331 -------------------------------- Gopkg.toml | 125 --- Makefile | 13 +- go.mod | 103 +++ go.sum | 406 ++++++++++ provider/alibaba_cloud.go | 26 +- provider/alibaba_cloud_test.go | 3 +- provider/aws_sd.go | 1 + provider/aws_test.go | 2 +- provider/designate_test.go | 4 +- provider/dnsimple_test.go | 4 +- provider/google.go | 2 +- provider/google_test.go | 2 +- provider/linode_test.go | 2 +- provider/pdns_test.go | 5 +- provider/rcode0.go | 9 +- provider/rcode0_test.go | 7 +- source/compatibility.go | 3 +- source/gateway_test.go | 8 +- source/ingress.go | 3 +- source/ingress_test.go | 2 +- source/service.go | 10 +- source/service_test.go | 3 +- source/shared_test.go | 3 +- 27 files changed, 600 insertions(+), 1522 deletions(-) create mode 100644 .golangci.yml delete mode 100644 Gopkg.lock delete mode 100644 Gopkg.toml create mode 100644 go.mod create mode 100644 go.sum diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 000000000..df6c42ad3 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,19 @@ +run: + concurrency: 4 + + modules-download-mode: readonly + +linters-settings: + golint: + min-confidence: 0.9 + + gocyclo: + min-complexity: 15 + +linters: + disable-all: true + enable: + - govet + - ineffassign + - golint + - goimports diff --git a/.travis.yml b/.travis.yml index c22f9041c..5b2f6c836 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -dist: trusty +dist: xenial os: - linux @@ -6,27 +6,23 @@ os: language: go go: -- 1.x +- "1.12.x" - tip matrix: allow_failures: - go: tip +env: +- GO111MODULE=on GOLANGCI_RELEASE="v1.16.0" + before_install: -- make dep - go get github.com/mattn/goveralls - go get github.com/lawrencewoodman/roveralls -- go get github.com/alecthomas/gometalinter - -install: -- gometalinter --install - -env: - - GOMETALINTER_DEADLINE="600s" +- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_RELEASE} script: -- vendor/github.com/kubernetes/repo-infra/verify/verify-boilerplate.sh --rootdir=$(pwd) -- vendor/github.com/kubernetes/repo-infra/verify/verify-go-src.sh -v --rootdir $(pwd) +- make test +- make lint - travis_wait 20 roveralls - goveralls -coverprofile=roveralls.coverprofile -service=travis-ci diff --git a/Dockerfile b/Dockerfile index 95dc63f1d..f7166647d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,9 +15,9 @@ # builder image FROM golang as builder -WORKDIR /go/src/github.com/kubernetes-incubator/external-dns +WORKDIR /github.com/kubernetes-incubator/external-dns COPY . . -RUN make dep +RUN go mod vendor RUN make test RUN make build @@ -25,7 +25,7 @@ RUN make build FROM registry.opensource.zalan.do/stups/alpine:latest LABEL maintainer="Team Teapot @ Zalando SE " -COPY --from=builder /go/src/github.com/kubernetes-incubator/external-dns/build/external-dns /bin/external-dns +COPY --from=builder /kubernetes-incubator/external-dns/build/external-dns /bin/external-dns USER nobody diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index b412e2367..000000000 --- a/Gopkg.lock +++ /dev/null @@ -1,1331 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - digest = "1:ae9d0182a5cf7dbb025a8fc5821234cc1f26ca342fc41d951a99f71b9adc1b87" - name = "cloud.google.com/go" - packages = [ - "compute/metadata", - "internal", - ] - pruneopts = "" - revision = "3b1ae45394a234c385be014e9a488f2bb6eef821" - -[[projects]] - digest = "1:fd38e3b8c27cab6561a7d2e8557205c3ca5c57cbb6d3a79e10f22e73e84fd776" - name = "github.com/Azure/azure-sdk-for-go" - packages = ["arm/dns"] - pruneopts = "" - revision = "2629e2dfcfeab50896230140542c3b9d89b35ae1" - version = "v10.0.4-beta" - -[[projects]] - digest = "1:f719ef698feb8da2923ebda9a8d553b977320b02182f3797e185202e588a94b1" - name = "github.com/Azure/go-autorest" - packages = [ - "autorest", - "autorest/adal", - "autorest/azure", - "autorest/date", - "autorest/to", - ] - pruneopts = "" - revision = "aa2a4534ab680e938d933870f58f23f77e0e208e" - version = "v10.9.0" - -[[projects]] - digest = "1:7dc69d1597e4773ec5f64e5c078d55f0f011bb05ec0435346d0649ad978a23fd" - name = "github.com/alecthomas/kingpin" - packages = ["."] - pruneopts = "" - revision = "1087e65c9441605df944fb12c33f0fe7072d18ca" - version = "v2.2.5" - -[[projects]] - branch = "master" - digest = "1:a74730e052a45a3fab1d310fdef2ec17ae3d6af16228421e238320846f2aaec8" - name = "github.com/alecthomas/template" - packages = [ - ".", - "parse", - ] - pruneopts = "" - revision = "a0175ee3bccc567396460bf5acd36800cb10c49c" - -[[projects]] - branch = "master" - digest = "1:8483994d21404c8a1d489f6be756e25bfccd3b45d65821f25695577791a08e68" - name = "github.com/alecthomas/units" - packages = ["."] - pruneopts = "" - revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" - -[[projects]] - digest = "1:d2dc5d0ccc137594ea6fb3870964967b96b43daac19b8093930c7b02873fd5ca" - name = "github.com/aliyun/alibaba-cloud-sdk-go" - packages = [ - "sdk", - "sdk/auth", - "sdk/auth/credentials", - "sdk/auth/signers", - "sdk/endpoints", - "sdk/errors", - "sdk/requests", - "sdk/responses", - "sdk/utils", - "services/alidns", - "services/pvtz", - ] - pruneopts = "" - revision = "cad214d7d71fba7883fcf3b7e550ba782c15b400" - version = "1.27.7" - -[[projects]] - digest = "1:1c82dd6a02941a3c4f23a32eca182717ab79691939e97d6b971466b780f06eea" - name = "github.com/aws/aws-sdk-go" - packages = [ - "aws", - "aws/awserr", - "aws/awsutil", - "aws/client", - "aws/client/metadata", - "aws/corehandlers", - "aws/credentials", - "aws/credentials/ec2rolecreds", - "aws/credentials/endpointcreds", - "aws/credentials/stscreds", - "aws/defaults", - "aws/ec2metadata", - "aws/endpoints", - "aws/request", - "aws/session", - "aws/signer/v4", - "internal/sdkio", - "internal/sdkrand", - "internal/shareddefaults", - "private/protocol", - "private/protocol/json/jsonutil", - "private/protocol/jsonrpc", - "private/protocol/query", - "private/protocol/query/queryutil", - "private/protocol/rest", - "private/protocol/restxml", - "private/protocol/xml/xmlutil", - "service/route53", - "service/servicediscovery", - "service/sts", - ] - pruneopts = "" - revision = "9b0098a71f6d4d473a26ec8ad3c2feaac6eb1da6" - version = "v1.13.32" - -[[projects]] - branch = "master" - digest = "1:0c5485088ce274fac2e931c1b979f2619345097b39d91af3239977114adf0320" - name = "github.com/beorn7/perks" - packages = ["quantile"] - pruneopts = "" - revision = "4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9" - -[[projects]] - branch = "master" - digest = "1:3e90c0d9954bf53a2061c4a0f193e6569c9ab2118c8f3a250871498f6b4645e5" - name = "github.com/cloudflare/cloudflare-go" - packages = ["."] - pruneopts = "" - revision = "0c85496d873009e53e64d391ade643e7ac02e0d4" - -[[projects]] - digest = "1:eaeede87b418b97f9dee473f8940fd9b65ca5cdac0503350c7c8f8965ea3cf4d" - name = "github.com/coreos/etcd" - packages = [ - "auth/authpb", - "clientv3", - "etcdserver/api/v3rpc/rpctypes", - "etcdserver/etcdserverpb", - "mvcc/mvccpb", - "pkg/types", - ] - pruneopts = "" - revision = "1b3ac99e8a431b381e633802cc42fe70e663baf5" - version = "v3.2.15" - -[[projects]] - digest = "1:56c130d885a4aacae1dd9c7b71cfe39912c7ebc1ff7d2b46083c8812996dc43b" - name = "github.com/davecgh/go-spew" - packages = ["spew"] - pruneopts = "" - revision = "346938d642f2ec3594ed81d874461961cd0faa76" - version = "v1.1.0" - -[[projects]] - branch = "master" - digest = "1:dc166ce7345c060c2153561130d6d49ac580c804226ac675e368d533b36eb169" - name = "github.com/denverdino/aliyungo" - packages = [ - "metadata", - "util", - ] - pruneopts = "" - revision = "69560d9530f5265ba00ffad2520d7ef01c2cddd4" - -[[projects]] - digest = "1:6098222470fe0172157ce9bbef5d2200df4edde17ee649c5d6e48330e4afa4c6" - name = "github.com/dgrijalva/jwt-go" - packages = ["."] - pruneopts = "" - revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e" - version = "v3.2.0" - -[[projects]] - digest = "1:32d1941b093bb945de75b0276348494be318d34f3df39c4413d61e002c800bc6" - name = "github.com/digitalocean/godo" - packages = [ - ".", - "context", - ] - pruneopts = "" - revision = "77ea48de76a7b31b234d854f15d003c68bb2fb90" - version = "v1.1.1" - -[[projects]] - digest = "1:5ffd39844bdd1259a6227d544f582c6686ce43c8c44399a46052fe3bd2bed93c" - name = "github.com/dnsimple/dnsimple-go" - packages = ["dnsimple"] - pruneopts = "" - revision = "d1105abc03b313d7b8d9b04364f6bd053b346e59" - version = "v0.14.0" - -[[projects]] - digest = "1:a2d7fd2421a8578c4adea9830bef9d17366e4640edb57735dff2d1dc68815a9e" - name = "github.com/envoyproxy/go-control-plane" - packages = [ - "envoy/api/v2", - "envoy/api/v2/auth", - "envoy/api/v2/cluster", - "envoy/api/v2/core", - "envoy/api/v2/endpoint", - "envoy/api/v2/listener", - "envoy/api/v2/route", - "envoy/config/grpc_credential/v2alpha", - "envoy/type", - ] - pruneopts = "" - revision = "0ad6fa1cf0b9b6ca8f3617a7188a568e81f40b87" - version = "v0.6.9" - -[[projects]] - digest = "1:e17d18b233f506404061c27ac4a08624dad38dcd0d49f9cfdae67a7772a4fb8c" - name = "github.com/exoscale/egoscale" - packages = ["."] - pruneopts = "" - revision = "c6d915cb993f1a54f604acefc0fc15cf6578a87a" - version = "v0.11.0" - -[[projects]] - branch = "master" - digest = "1:ae7fb2062735e966ab69d14d2a091f3778b0d676dc8d1f01d092bcb0fb8ed45b" - name = "github.com/ffledgling/pdns-go" - packages = ["."] - pruneopts = "" - revision = "524e7daccd99651cdb56426eb15b7d61f9597a5c" - -[[projects]] - digest = "1:b13707423743d41665fd23f0c36b2f37bb49c30e94adb813319c44188a51ba22" - name = "github.com/ghodss/yaml" - packages = ["."] - pruneopts = "" - revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7" - version = "v1.0.0" - -[[projects]] - digest = "1:a00483fe4106b86fb1187a92b5cf6915c85f294ed4c129ccbe7cb1f1a06abd46" - name = "github.com/go-ini/ini" - packages = ["."] - pruneopts = "" - revision = "32e4c1e6bc4e7d0d8451aa6b75200d19e37a536a" - version = "v1.32.0" - -[[projects]] - digest = "1:8e67153fc0a9fb0d6c9707e36cf80e217a012364307b222eb4ba6828f7e881e6" - name = "github.com/go-resty/resty" - packages = ["."] - pruneopts = "" - revision = "97a15579492cd5f35632499f315d7a8df94160a1" - version = "v1.8.0" - -[[projects]] - digest = "1:54d5c6a784a9de9c836fc070d51d0689c3e99ee6d24dba8a36f0762039dae830" - name = "github.com/gogo/googleapis" - packages = [ - "google/api", - "google/rpc", - ] - pruneopts = "" - revision = "8558fb44d2f1fc223118afc694129d2c2d2924d1" - version = "v1.1.0" - -[[projects]] - digest = "1:6e73003ecd35f4487a5e88270d3ca0a81bc80dc88053ac7e4dcfec5fba30d918" - name = "github.com/gogo/protobuf" - packages = [ - "gogoproto", - "jsonpb", - "proto", - "protoc-gen-gogo/descriptor", - "sortkeys", - "types", - ] - pruneopts = "" - revision = "636bf0302bc95575d69441b25a2603156ffdddf1" - version = "v1.1.1" - -[[projects]] - branch = "master" - digest = "1:b12aff239810a9fa71e901a712a52f9da4c6e536852e943be693dec1d4519dfd" - name = "github.com/golang/glog" - packages = ["."] - pruneopts = "" - revision = "3fa5b9870d1d29f6d7907b29f1ae8c6eeb403829" - source = "github.com/kubermatic/glog-logrus" - -[[projects]] - digest = "1:3dd078fda7500c341bc26cfbc6c6a34614f295a2457149fc1045cab767cbcf18" - name = "github.com/golang/protobuf" - packages = [ - "proto", - "protoc-gen-go/descriptor", - "ptypes", - "ptypes/any", - "ptypes/duration", - "ptypes/timestamp", - ] - pruneopts = "" - revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" - version = "v1.2.0" - -[[projects]] - branch = "master" - digest = "1:be28c0531a755f2178acf1e327e6f5a8a3968feb5f2567cdc968064253141751" - name = "github.com/google/btree" - packages = ["."] - pruneopts = "" - revision = "e89373fe6b4a7413d7acd6da1725b83ef713e6e4" - -[[projects]] - branch = "master" - digest = "1:9abc49f39e3e23e262594bb4fb70abf74c0c99e94f99153f43b143805e850719" - name = "github.com/google/go-querystring" - packages = ["query"] - pruneopts = "" - revision = "53e6ce116135b80d037921a7fdd5138cf32d7a8a" - -[[projects]] - digest = "1:a2823c34933d4a2b36284f617f483d51fe156a443923284b3660f183dcfa3338" - name = "github.com/google/gofuzz" - packages = ["."] - pruneopts = "" - revision = "44d81051d367757e1c7c6a5a86423ece9afcf63c" - -[[projects]] - digest = "1:16b2837c8b3cf045fa2cdc82af0cf78b19582701394484ae76b2c3bc3c99ad73" - name = "github.com/googleapis/gnostic" - packages = [ - "OpenAPIv2", - "compiler", - "extensions", - ] - pruneopts = "" - revision = "7c663266750e7d82587642f65e60bc4083f1f84e" - version = "v0.2.0" - -[[projects]] - branch = "master" - digest = "1:54a44d48a24a104e078ef5f94d82f025a6be757e7c42b4370c621a3928d6ab7c" - name = "github.com/gophercloud/gophercloud" - packages = [ - ".", - "openstack", - "openstack/dns/v2/recordsets", - "openstack/dns/v2/zones", - "openstack/identity/v2/tenants", - "openstack/identity/v2/tokens", - "openstack/identity/v3/tokens", - "openstack/utils", - "pagination", - ] - pruneopts = "" - revision = "bfc4756e1a693a850d7d459f4b28b21f35a24b5a" - -[[projects]] - digest = "1:dbbeb8ddb0be949954c8157ee8439c2adfd8dc1c9510eb44a6e58cb68c3dce28" - name = "github.com/gorilla/context" - packages = ["."] - pruneopts = "" - revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42" - version = "v1.1.1" - -[[projects]] - digest = "1:c2c8666b4836c81a1d247bdf21c6a6fc1ab586538ab56f74437c2e0df5c375e1" - name = "github.com/gorilla/mux" - packages = ["."] - pruneopts = "" - revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf" - version = "v1.6.2" - -[[projects]] - branch = "master" - digest = "1:009a1928b8c096338b68b5822d838a72b4d8520715c1463614476359f3282ec8" - name = "github.com/gregjones/httpcache" - packages = [ - ".", - "diskcache", - ] - pruneopts = "" - revision = "9cad4c3443a7200dd6400aef47183728de563a38" - -[[projects]] - digest = "1:8e3bd93036b4a925fe2250d3e4f38f21cadb8ef623561cd80c3c50c114b13201" - name = "github.com/hashicorp/errwrap" - packages = ["."] - pruneopts = "" - revision = "8a6fb523712970c966eefc6b39ed2c5e74880354" - version = "v1.0.0" - -[[projects]] - branch = "master" - digest = "1:72308fdd6d5ef61106a95be7ca72349a5565809042b6426a3cfb61d99483b824" - name = "github.com/hashicorp/go-multierror" - packages = ["."] - pruneopts = "" - revision = "886a7fbe3eb1c874d46f623bfa70af45f425b3d1" - -[[projects]] - digest = "1:3313a63031ae281e5f6fd7b0bbca733dfa04d2429df86519e3b4d4c016ccb836" - name = "github.com/hashicorp/golang-lru" - packages = [ - ".", - "simplelru", - ] - pruneopts = "" - revision = "20f1fb78b0740ba8c3cb143a61e86ba5c8669768" - version = "v0.5.0" - -[[projects]] - digest = "1:af7e132906cb360f4d7c34a9e1434825467f21c4ff5c521ad4cc5b55352876a8" - name = "github.com/imdario/mergo" - packages = ["."] - pruneopts = "" - revision = "6633656539c1639d9d78127b7d47c622b5d7b6dc" - -[[projects]] - digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" - name = "github.com/inconshreveable/mousetrap" - packages = ["."] - pruneopts = "" - revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" - version = "v1.0" - -[[projects]] - branch = "master" - digest = "1:e0a13d0a368c028716e78448db972657b5292c7238d61405e8289f47c05c8706" - name = "github.com/infobloxopen/infoblox-go-client" - packages = ["."] - pruneopts = "" - revision = "61dc5f9b0a655ebf43026f0d8a837ad1e28e4b96" - -[[projects]] - digest = "1:6f49eae0c1e5dab1dafafee34b207aeb7a42303105960944828c2079b92fc88e" - name = "github.com/jmespath/go-jmespath" - packages = ["."] - pruneopts = "" - revision = "0b12d6b5" - -[[projects]] - digest = "1:53ac4e911e12dde0ab68655e2006449d207a5a681f084974da2b06e5dbeaca72" - name = "github.com/json-iterator/go" - packages = ["."] - pruneopts = "" - revision = "ab8a2e0c74be9d3be70b3184d9acc634935ded82" - version = "1.1.4" - -[[projects]] - branch = "master" - digest = "1:fd50e2c52f29bb81f9a172f0d5aee1438b201ca0502ff3a20ebbe9629e274875" - name = "github.com/kubernetes/repo-infra" - packages = ["verify/boilerplate/test"] - pruneopts = "" - revision = "1bcb110c8726cee477939f507f4760a95e155347" - -[[projects]] - digest = "1:7c23a751ce2f84663fa411acb87eae0da2d09c39a8e99b08bd8f65fae75d8928" - name = "github.com/linki/instrumented_http" - packages = ["."] - pruneopts = "" - revision = "508103cfb3b315fa9752b5bcd4fb2d97bbc26d89" - version = "v0.2.0" - -[[projects]] - digest = "1:1c41354ef11c9dbae2fe1ceee8369fcb2634977ba07e701e19ea53e8742c5420" - name = "github.com/linode/linodego" - packages = ["."] - pruneopts = "" - revision = "7edbc87f0140b7561dbc20458877a56bdded5eb8" - version = "v0.3.0" - -[[projects]] - digest = "1:a404c6713d3e7fb61746c8bc355c6009ddb8fbb2e376ce33c03881645217f1a1" - name = "github.com/lyft/protoc-gen-validate" - packages = ["validate"] - pruneopts = "" - revision = "ff6f7a9bc2e5fe006509b9f8c7594c41a953d50f" - version = "v0.0.14" - -[[projects]] - branch = "master" - digest = "1:63722a4b1e1717be7b98fc686e0b30d5e7f734b9e93d7dee86293b6deab7ea28" - name = "github.com/matttproud/golang_protobuf_extensions" - packages = ["pbutil"] - pruneopts = "" - revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" - -[[projects]] - digest = "1:4c8d8358c45ba11ab7bb15df749d4df8664ff1582daead28bae58cf8cbe49890" - name = "github.com/miekg/dns" - packages = ["."] - pruneopts = "" - revision = "5a2b9fab83ff0f8bfc99684bd5f43a37abe560f1" - version = "v1.0.8" - -[[projects]] - digest = "1:bcc46a0fbd9e933087bef394871256b5c60269575bb661935874729c65bbbf60" - name = "github.com/mitchellh/mapstructure" - packages = ["."] - pruneopts = "" - revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe" - version = "v1.1.2" - -[[projects]] - digest = "1:0c0ff2a89c1bb0d01887e1dac043ad7efbf3ec77482ef058ac423d13497e16fd" - name = "github.com/modern-go/concurrent" - packages = ["."] - pruneopts = "" - revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94" - version = "1.0.3" - -[[projects]] - digest = "1:e32bdbdb7c377a07a9a46378290059822efdce5c8d96fe71940d87cb4f918855" - name = "github.com/modern-go/reflect2" - packages = ["."] - pruneopts = "" - revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" - version = "1.0.1" - -[[projects]] - digest = "1:11c58e19ff7ce22740423bb933f1ddca3bf575def40d5ac3437ec12871b1648b" - name = "github.com/natefinch/lumberjack" - packages = ["."] - pruneopts = "" - revision = "a96e63847dc3c67d17befa69c303767e2f84e54f" - version = "v2.1" - -[[projects]] - digest = "1:d8b5d0ecca348c835914a1ed8589f17a6a7f309befab7327b0470324531f7ac4" - name = "github.com/nesv/go-dynect" - packages = ["dynect"] - pruneopts = "" - revision = "cdd946344b54bdf7dbeac406c2f1fe93150f08ea" - version = "v0.6.0" - -[[projects]] - digest = "1:d0b493d855fe23836a334940271745a43587f20b2fab4bb7c4ff0add1adcbcd4" - name = "github.com/nic-at/rc0go" - packages = ["."] - pruneopts = "" - revision = "d5560d32f90d7722d154a4e0d3b797d5e0ae0b68" - version = "v1.1.0" - -[[projects]] - digest = "1:70df8e71a953626770223d4982801fa73e7e99cbfcca068b95127f72af9b9edd" - name = "github.com/oracle/oci-go-sdk" - packages = [ - "common", - "dns", - ] - pruneopts = "" - revision = "a2ded717dc4bb4916c0416ec79f81718b576dbc4" - version = "v1.8.0" - -[[projects]] - branch = "master" - digest = "1:c24598ffeadd2762552269271b3b1510df2d83ee6696c1e543a0ff653af494bc" - name = "github.com/petar/GoLLRB" - packages = ["llrb"] - pruneopts = "" - revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4" - -[[projects]] - digest = "1:b46305723171710475f2dd37547edd57b67b9de9f2a6267cafdd98331fd6897f" - name = "github.com/peterbourgon/diskv" - packages = ["."] - pruneopts = "" - revision = "5f041e8faa004a95c88a202771f4cc3e991971e6" - version = "v2.0.1" - -[[projects]] - digest = "1:cf172c58bb2a13ed39ea1c9e79525567c63bcc2c4afbb6cf023e87b31780f249" - name = "github.com/pkg/errors" - packages = ["."] - pruneopts = "" - revision = "f15c970de5b76fac0b59abb32d62c17cc7bed265" - -[[projects]] - digest = "1:256484dbbcd271f9ecebc6795b2df8cad4c458dd0f5fd82a8c2fa0c29f233411" - name = "github.com/pmezard/go-difflib" - packages = ["difflib"] - pruneopts = "" - revision = "792786c7400a136282c1664665ae0a8db921c6c2" - version = "v1.0.0" - -[[projects]] - digest = "1:6f218995d6a74636cfcab45ce03005371e682b4b9bee0e5eb0ccfd83ef85364f" - name = "github.com/prometheus/client_golang" - packages = [ - "prometheus", - "prometheus/internal", - "prometheus/promhttp", - ] - pruneopts = "" - revision = "505eaef017263e299324067d40ca2c48f6a2cf50" - version = "v0.9.2" - -[[projects]] - branch = "master" - digest = "1:60aca47f4eeeb972f1b9da7e7db51dee15ff6c59f7b401c1588b8e6771ba15ef" - name = "github.com/prometheus/client_model" - packages = ["go"] - pruneopts = "" - revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c" - -[[projects]] - branch = "master" - digest = "1:e3aa5178be4fc4ae8cdb37d11c02f7490c00450a9f419e6aa84d02d3b47e90d2" - name = "github.com/prometheus/common" - packages = [ - "expfmt", - "internal/bitbucket.org/ww/goautoneg", - "model", - ] - pruneopts = "" - revision = "2e54d0b93cba2fd133edc32211dcc32c06ef72ca" - -[[projects]] - digest = "1:a6a85fc81f2a06ccac3d45005523afbeee45138d781d4f3cb7ad9889d5c65aab" - name = "github.com/prometheus/procfs" - packages = [ - ".", - "xfs", - ] - pruneopts = "" - revision = "a6e9df898b1336106c743392c48ee0b71f5c4efa" - -[[projects]] - branch = "master" - digest = "1:bb2ccb2d56cbafdec58af0f473f45304e19876f09fa671960ca87802b656a9c0" - name = "github.com/sanyu/dynectsoap" - packages = ["dynectsoap"] - pruneopts = "" - revision = "b83de5edc4e022f22903eeb3b428d2f39fb740e5" - -[[projects]] - digest = "1:7f569d906bdd20d906b606415b7d794f798f91a62fcfb6a4daa6d50690fb7a3f" - name = "github.com/satori/go.uuid" - packages = ["."] - pruneopts = "" - revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3" - version = "v1.2.0" - -[[projects]] - digest = "1:3ac248add5bb40a3c631c5334adcd09aa72d15af2768a5bc0274084ea7b2e5ba" - name = "github.com/sirupsen/logrus" - packages = ["."] - pruneopts = "" - revision = "f006c2ac4710855cf0f916dd6b77acf6b048dc6e" - version = "v1.0.3" - -[[projects]] - digest = "1:a1403cc8a94b8d7956ee5e9694badef0e7b051af289caad1cf668331e3ffa4f6" - name = "github.com/spf13/cobra" - packages = ["."] - pruneopts = "" - revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385" - version = "v0.0.3" - -[[projects]] - digest = "1:0a52bcb568386d98f4894575d53ce3e456f56471de6897bb8b9de13c33d9340e" - name = "github.com/spf13/pflag" - packages = ["."] - pruneopts = "" - revision = "9a97c102cda95a86cec2345a6f09f55a939babf5" - version = "v1.0.2" - -[[projects]] - digest = "1:306417ea2f31ea733df356a2b895de63776b6a5107085b33458e5cd6eb1d584d" - name = "github.com/stretchr/objx" - packages = ["."] - pruneopts = "" - revision = "facf9a85c22f48d2f52f2380e4efce1768749a89" - version = "v0.1" - -[[projects]] - digest = "1:a30066593578732a356dc7e5d7f78d69184ca65aeeff5939241a3ab10559bb06" - name = "github.com/stretchr/testify" - packages = [ - "assert", - "mock", - "require", - "suite", - ] - pruneopts = "" - revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" - version = "v1.2.1" - -[[projects]] - branch = "master" - digest = "1:81f435c83e3523a7ee3f277769727f73ca66218ca8188d96a0935a4841b47a76" - name = "github.com/tent/http-link-go" - packages = ["."] - pruneopts = "" - revision = "ac974c61c2f990f4115b119354b5e0b47550e888" - -[[projects]] - branch = "master" - digest = "1:cd1c3692d12b8c8e4a2d8af87de070b21a1c8e67fdd1d8dc31aa14791394fa17" - name = "github.com/yl2chen/cidranger" - packages = [ - ".", - "net", - ] - pruneopts = "" - revision = "928b519e5268fe386d0f7decce320205cc09ca95" - -[[projects]] - digest = "1:74f86c458e82e1c4efbab95233e0cf51b7cc02dc03193be9f62cd81224e10401" - name = "go.uber.org/atomic" - packages = ["."] - pruneopts = "" - revision = "1ea20fb1cbb1cc08cbd0d913a96dead89aa18289" - version = "v1.3.2" - -[[projects]] - digest = "1:22c7effcb4da0eacb2bb1940ee173fac010e9ef3c691f5de4b524d538bd980f5" - name = "go.uber.org/multierr" - packages = ["."] - pruneopts = "" - revision = "3c4937480c32f4c13a875a1829af76c98ca3d40a" - version = "v1.1.0" - -[[projects]] - digest = "1:246f378f80fba6fcf0f191c486b6613265abd2bc0f2fa55a36b928c67352021e" - name = "go.uber.org/zap" - packages = [ - ".", - "buffer", - "internal/bufferpool", - "internal/color", - "internal/exit", - "zapcore", - "zapgrpc", - ] - pruneopts = "" - revision = "ff33455a0e382e8a81d14dd7c922020b6b5e7982" - version = "v1.9.1" - -[[projects]] - branch = "master" - digest = "1:b2d8b39397ca07929a3de3a3fd2b6ca4c8d48e9cadaa7cf2b083e27fd9e78107" - name = "golang.org/x/crypto" - packages = [ - "ed25519", - "ed25519/internal/edwards25519", - "ssh/terminal", - ] - pruneopts = "" - revision = "0709b304e793a5edb4a2c0145f281ecdc20838a4" - -[[projects]] - digest = "1:782723d6fc27d202f1943219d68d58b3f6bcab6212c85294b1ddd8b586b1d356" - name = "golang.org/x/net" - packages = [ - "bpf", - "context", - "context/ctxhttp", - "http/httpguts", - "http2", - "http2/hpack", - "idna", - "internal/iana", - "internal/socket", - "internal/timeseries", - "ipv4", - "ipv6", - "publicsuffix", - "trace", - ] - pruneopts = "" - revision = "161cd47e91fd58ac17490ef4d742dc98bb4cf60e" - -[[projects]] - digest = "1:dad5a319c4710358be1f2bf68f9fb7f90a71d7c641221b79801d5667b28f19e3" - name = "golang.org/x/oauth2" - packages = [ - ".", - "google", - "internal", - "jws", - "jwt", - ] - pruneopts = "" - revision = "3c3a985cb79f52a3190fbc056984415ca6763d01" - -[[projects]] - digest = "1:39d88a855976e160babdd254802e1c2ae75ed380328c39742b57928852da6207" - name = "golang.org/x/sys" - packages = [ - "unix", - "windows", - ] - pruneopts = "" - revision = "13d03a9a82fba647c21a0ef8fba44a795d0f0835" - -[[projects]] - digest = "1:5acd3512b047305d49e8763eef7ba423901e85d5dd2fd1e71778a0ea8de10bd4" - name = "golang.org/x/text" - packages = [ - "collate", - "collate/build", - "internal/colltab", - "internal/gen", - "internal/tag", - "internal/triegen", - "internal/ucd", - "language", - "secure/bidirule", - "transform", - "unicode/bidi", - "unicode/cldr", - "unicode/norm", - "unicode/rangetable", - ] - pruneopts = "" - revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" - version = "v0.3.0" - -[[projects]] - branch = "master" - digest = "1:55a681cb66f28755765fa5fa5104cbd8dc85c55c02d206f9f89566451e3fe1aa" - name = "golang.org/x/time" - packages = ["rate"] - pruneopts = "" - revision = "fbb02b2291d28baffd63558aa44b4b56f178d650" - -[[projects]] - digest = "1:2ad38d79865e33dde6157b7048debd6e7d47e0709df7b5e11bb888168e316908" - name = "google.golang.org/api" - packages = [ - "dns/v1", - "gensupport", - "googleapi", - "googleapi/internal/uritemplates", - ] - pruneopts = "" - revision = "a0ff90edab763c86aa88f2b1eb4f3301b82f6336" - -[[projects]] - digest = "1:41e2b7e287117f6136f75286d48072ecf781ba54823ffeb2098e152e7dc45ef6" - name = "google.golang.org/appengine" - packages = [ - ".", - "internal", - "internal/app_identity", - "internal/base", - "internal/datastore", - "internal/log", - "internal/modules", - "internal/remote_api", - "internal/urlfetch", - "urlfetch", - ] - pruneopts = "" - revision = "4f7eeb5305a4ba1966344836ba4af9996b7b4e05" - -[[projects]] - branch = "master" - digest = "1:e43f1cb3f488a0c2be85939c2a594636f60b442a12a196c778bd2d6c9aca3df7" - name = "google.golang.org/genproto" - packages = [ - "googleapis/api/annotations", - "googleapis/rpc/status", - ] - pruneopts = "" - revision = "11092d34479b07829b72e10713b159248caf5dad" - -[[projects]] - digest = "1:ca75b3775a5d4e5d1fb48f57ef0865b4aaa8b3f00e6b52be68db991c4594e0a7" - name = "google.golang.org/grpc" - packages = [ - ".", - "balancer", - "balancer/base", - "balancer/roundrobin", - "codes", - "connectivity", - "credentials", - "encoding", - "encoding/proto", - "grpclog", - "health/grpc_health_v1", - "internal", - "internal/backoff", - "internal/channelz", - "internal/envconfig", - "internal/grpcrand", - "internal/transport", - "keepalive", - "metadata", - "naming", - "peer", - "resolver", - "resolver/dns", - "resolver/passthrough", - "stats", - "status", - "tap", - ] - pruneopts = "" - revision = "32fb0ac620c32ba40a4626ddf94d90d12cce3455" - version = "v1.14.0" - -[[projects]] - digest = "1:e5d1fb981765b6f7513f793a3fcaac7158408cca77f75f7311ac82cc88e9c445" - name = "gopkg.in/inf.v0" - packages = ["."] - pruneopts = "" - revision = "3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4" - version = "v0.9.0" - -[[projects]] - digest = "1:ecbf853ae69b7b38a5039077c10892c9798bd5345c73902026622a65a94ea501" - name = "gopkg.in/resty.v1" - packages = ["."] - pruneopts = "" - revision = "03c09fa32a21b7b27b8dbb3877826c1ab3d2daa2" - version = "v1.11.0" - -[[projects]] - digest = "1:f0620375dd1f6251d9973b5f2596228cc8042e887cd7f827e4220bc1ce8c30e2" - name = "gopkg.in/yaml.v2" - packages = ["."] - pruneopts = "" - revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" - version = "v2.2.1" - -[[projects]] - branch = "release-1.1" - digest = "1:17f75d4a08eab0d4bb4a862a6a0e6789c9065063b450c7850c835ccde85e0981" - name = "istio.io/api" - packages = [ - "authentication/v1alpha1", - "mesh/v1alpha1", - "mixer/v1", - "mixer/v1/config/client", - "networking/v1alpha3", - "policy/v1beta1", - "rbac/v1alpha1", - ] - pruneopts = "" - revision = "db16d82d3672586b4beec10c1af0643d518cb454" - -[[projects]] - digest = "1:901e05af6afa9e7b49028d97c5f39c86470bec561153e0cc931dfd6c9c5ec2ed" - name = "istio.io/istio" - packages = [ - "galley/pkg/metadata", - "galley/pkg/runtime/resource", - "pilot/pkg/config/kube/crd", - "pilot/pkg/model", - "pilot/pkg/model/test", - "pilot/pkg/serviceregistry/kube", - "pkg/cache", - "pkg/features/pilot", - "pkg/kube", - "pkg/log", - "pkg/spiffe", - ] - pruneopts = "" - revision = "2b1331886076df103179e3da5dc9077fed59c989" - version = "1.1.1" - -[[projects]] - digest = "1:f420c8548c93242d8e5dcfa5b34e0243883b4e660f65076e869daafac877144d" - name = "k8s.io/api" - packages = [ - "admissionregistration/v1alpha1", - "admissionregistration/v1beta1", - "apps/v1", - "apps/v1beta1", - "apps/v1beta2", - "authentication/v1", - "authentication/v1beta1", - "authorization/v1", - "authorization/v1beta1", - "autoscaling/v1", - "autoscaling/v2beta1", - "batch/v1", - "batch/v1beta1", - "batch/v2alpha1", - "certificates/v1beta1", - "core/v1", - "events/v1beta1", - "extensions/v1beta1", - "networking/v1", - "policy/v1beta1", - "rbac/v1", - "rbac/v1alpha1", - "rbac/v1beta1", - "scheduling/v1alpha1", - "scheduling/v1beta1", - "settings/v1alpha1", - "storage/v1", - "storage/v1alpha1", - "storage/v1beta1", - ] - pruneopts = "" - revision = "072894a440bdee3a891dea811fe42902311cd2a3" - version = "kubernetes-1.11.0" - -[[projects]] - branch = "release-1.11" - digest = "1:545b132408a3b9c330cd062bb336b91d9578576623b440c753d81b45c968d7d3" - name = "k8s.io/apiextensions-apiserver" - packages = [ - "pkg/apis/apiextensions", - "pkg/apis/apiextensions/v1beta1", - "pkg/client/clientset/clientset", - "pkg/client/clientset/clientset/scheme", - "pkg/client/clientset/clientset/typed/apiextensions/v1beta1", - ] - pruneopts = "" - revision = "57c81b676ab11a0924f53d62f3fdccb8e784df03" - -[[projects]] - digest = "1:b6b2fb7b4da1ac973b64534ace2299a02504f16bc7820cb48edb8ca4077183e1" - name = "k8s.io/apimachinery" - packages = [ - "pkg/api/errors", - "pkg/api/meta", - "pkg/api/resource", - "pkg/apis/meta/internalversion", - "pkg/apis/meta/v1", - "pkg/apis/meta/v1/unstructured", - "pkg/apis/meta/v1beta1", - "pkg/conversion", - "pkg/conversion/queryparams", - "pkg/fields", - "pkg/labels", - "pkg/runtime", - "pkg/runtime/schema", - "pkg/runtime/serializer", - "pkg/runtime/serializer/json", - "pkg/runtime/serializer/protobuf", - "pkg/runtime/serializer/recognizer", - "pkg/runtime/serializer/streaming", - "pkg/runtime/serializer/versioning", - "pkg/selection", - "pkg/types", - "pkg/util/cache", - "pkg/util/clock", - "pkg/util/diff", - "pkg/util/errors", - "pkg/util/framer", - "pkg/util/intstr", - "pkg/util/json", - "pkg/util/mergepatch", - "pkg/util/net", - "pkg/util/runtime", - "pkg/util/sets", - "pkg/util/strategicpatch", - "pkg/util/validation", - "pkg/util/validation/field", - "pkg/util/wait", - "pkg/util/yaml", - "pkg/version", - "pkg/watch", - "third_party/forked/golang/json", - "third_party/forked/golang/reflect", - ] - pruneopts = "" - revision = "103fd098999dc9c0c88536f5c9ad2e5da39373ae" - version = "kubernetes-1.11.0" - -[[projects]] - digest = "1:d04779a8de7d5465e0463bd986506348de5e89677c74777f695d3145a7a8d15e" - name = "k8s.io/client-go" - packages = [ - "discovery", - "discovery/fake", - "informers", - "informers/admissionregistration", - "informers/admissionregistration/v1alpha1", - "informers/admissionregistration/v1beta1", - "informers/apps", - "informers/apps/v1", - "informers/apps/v1beta1", - "informers/apps/v1beta2", - "informers/autoscaling", - "informers/autoscaling/v1", - "informers/autoscaling/v2beta1", - "informers/batch", - "informers/batch/v1", - "informers/batch/v1beta1", - "informers/batch/v2alpha1", - "informers/certificates", - "informers/certificates/v1beta1", - "informers/core", - "informers/core/v1", - "informers/events", - "informers/events/v1beta1", - "informers/extensions", - "informers/extensions/v1beta1", - "informers/internalinterfaces", - "informers/networking", - "informers/networking/v1", - "informers/policy", - "informers/policy/v1beta1", - "informers/rbac", - "informers/rbac/v1", - "informers/rbac/v1alpha1", - "informers/rbac/v1beta1", - "informers/scheduling", - "informers/scheduling/v1alpha1", - "informers/scheduling/v1beta1", - "informers/settings", - "informers/settings/v1alpha1", - "informers/storage", - "informers/storage/v1", - "informers/storage/v1alpha1", - "informers/storage/v1beta1", - "kubernetes", - "kubernetes/fake", - "kubernetes/scheme", - "kubernetes/typed/admissionregistration/v1alpha1", - "kubernetes/typed/admissionregistration/v1alpha1/fake", - "kubernetes/typed/admissionregistration/v1beta1", - "kubernetes/typed/admissionregistration/v1beta1/fake", - "kubernetes/typed/apps/v1", - "kubernetes/typed/apps/v1/fake", - "kubernetes/typed/apps/v1beta1", - "kubernetes/typed/apps/v1beta1/fake", - "kubernetes/typed/apps/v1beta2", - "kubernetes/typed/apps/v1beta2/fake", - "kubernetes/typed/authentication/v1", - "kubernetes/typed/authentication/v1/fake", - "kubernetes/typed/authentication/v1beta1", - "kubernetes/typed/authentication/v1beta1/fake", - "kubernetes/typed/authorization/v1", - "kubernetes/typed/authorization/v1/fake", - "kubernetes/typed/authorization/v1beta1", - "kubernetes/typed/authorization/v1beta1/fake", - "kubernetes/typed/autoscaling/v1", - "kubernetes/typed/autoscaling/v1/fake", - "kubernetes/typed/autoscaling/v2beta1", - "kubernetes/typed/autoscaling/v2beta1/fake", - "kubernetes/typed/batch/v1", - "kubernetes/typed/batch/v1/fake", - "kubernetes/typed/batch/v1beta1", - "kubernetes/typed/batch/v1beta1/fake", - "kubernetes/typed/batch/v2alpha1", - "kubernetes/typed/batch/v2alpha1/fake", - "kubernetes/typed/certificates/v1beta1", - "kubernetes/typed/certificates/v1beta1/fake", - "kubernetes/typed/core/v1", - "kubernetes/typed/core/v1/fake", - "kubernetes/typed/events/v1beta1", - "kubernetes/typed/events/v1beta1/fake", - "kubernetes/typed/extensions/v1beta1", - "kubernetes/typed/extensions/v1beta1/fake", - "kubernetes/typed/networking/v1", - "kubernetes/typed/networking/v1/fake", - "kubernetes/typed/policy/v1beta1", - "kubernetes/typed/policy/v1beta1/fake", - "kubernetes/typed/rbac/v1", - "kubernetes/typed/rbac/v1/fake", - "kubernetes/typed/rbac/v1alpha1", - "kubernetes/typed/rbac/v1alpha1/fake", - "kubernetes/typed/rbac/v1beta1", - "kubernetes/typed/rbac/v1beta1/fake", - "kubernetes/typed/scheduling/v1alpha1", - "kubernetes/typed/scheduling/v1alpha1/fake", - "kubernetes/typed/scheduling/v1beta1", - "kubernetes/typed/scheduling/v1beta1/fake", - "kubernetes/typed/settings/v1alpha1", - "kubernetes/typed/settings/v1alpha1/fake", - "kubernetes/typed/storage/v1", - "kubernetes/typed/storage/v1/fake", - "kubernetes/typed/storage/v1alpha1", - "kubernetes/typed/storage/v1alpha1/fake", - "kubernetes/typed/storage/v1beta1", - "kubernetes/typed/storage/v1beta1/fake", - "listers/admissionregistration/v1alpha1", - "listers/admissionregistration/v1beta1", - "listers/apps/v1", - "listers/apps/v1beta1", - "listers/apps/v1beta2", - "listers/autoscaling/v1", - "listers/autoscaling/v2beta1", - "listers/batch/v1", - "listers/batch/v1beta1", - "listers/batch/v2alpha1", - "listers/certificates/v1beta1", - "listers/core/v1", - "listers/events/v1beta1", - "listers/extensions/v1beta1", - "listers/networking/v1", - "listers/policy/v1beta1", - "listers/rbac/v1", - "listers/rbac/v1alpha1", - "listers/rbac/v1beta1", - "listers/scheduling/v1alpha1", - "listers/scheduling/v1beta1", - "listers/settings/v1alpha1", - "listers/storage/v1", - "listers/storage/v1alpha1", - "listers/storage/v1beta1", - "pkg/apis/clientauthentication", - "pkg/apis/clientauthentication/v1alpha1", - "pkg/apis/clientauthentication/v1beta1", - "pkg/version", - "plugin/pkg/client/auth", - "plugin/pkg/client/auth/azure", - "plugin/pkg/client/auth/exec", - "plugin/pkg/client/auth/gcp", - "plugin/pkg/client/auth/oidc", - "plugin/pkg/client/auth/openstack", - "rest", - "rest/fake", - "rest/watch", - "testing", - "third_party/forked/golang/template", - "tools/auth", - "tools/cache", - "tools/clientcmd", - "tools/clientcmd/api", - "tools/clientcmd/api/latest", - "tools/clientcmd/api/v1", - "tools/metrics", - "tools/pager", - "tools/reference", - "transport", - "util/buffer", - "util/cert", - "util/connrotation", - "util/flowcontrol", - "util/homedir", - "util/integer", - "util/jsonpath", - "util/retry", - ] - pruneopts = "" - revision = "7d04d0e2a0a1a4d4a1cd6baa432a2301492e4e65" - version = "v8.0.0" - -[[projects]] - branch = "master" - digest = "1:526095379da1098c3f191a0008cc59c9bf9927492e63da7689e5de424219c162" - name = "k8s.io/kube-openapi" - packages = ["pkg/util/proto"] - pruneopts = "" - revision = "d8ea2fe547a448256204cfc68dfee7b26c720acb" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "cloud.google.com/go/compute/metadata", - "github.com/Azure/azure-sdk-for-go/arm/dns", - "github.com/Azure/go-autorest/autorest", - "github.com/Azure/go-autorest/autorest/adal", - "github.com/Azure/go-autorest/autorest/azure", - "github.com/Azure/go-autorest/autorest/to", - "github.com/alecthomas/kingpin", - "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests", - "github.com/aliyun/alibaba-cloud-sdk-go/services/alidns", - "github.com/aliyun/alibaba-cloud-sdk-go/services/pvtz", - "github.com/aws/aws-sdk-go/aws", - "github.com/aws/aws-sdk-go/aws/credentials/stscreds", - "github.com/aws/aws-sdk-go/aws/request", - "github.com/aws/aws-sdk-go/aws/session", - "github.com/aws/aws-sdk-go/service/route53", - "github.com/aws/aws-sdk-go/service/servicediscovery", - "github.com/cloudflare/cloudflare-go", - "github.com/coreos/etcd/clientv3", - "github.com/denverdino/aliyungo/metadata", - "github.com/digitalocean/godo", - "github.com/digitalocean/godo/context", - "github.com/dnsimple/dnsimple-go/dnsimple", - "github.com/exoscale/egoscale", - "github.com/ffledgling/pdns-go", - "github.com/gophercloud/gophercloud", - "github.com/gophercloud/gophercloud/openstack", - "github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets", - "github.com/gophercloud/gophercloud/openstack/dns/v2/zones", - "github.com/gophercloud/gophercloud/pagination", - "github.com/infobloxopen/infoblox-go-client", - "github.com/kubernetes/repo-infra/verify/boilerplate/test", - "github.com/linki/instrumented_http", - "github.com/linode/linodego", - "github.com/miekg/dns", - "github.com/nesv/go-dynect/dynect", - "github.com/nic-at/rc0go", - "github.com/oracle/oci-go-sdk/common", - "github.com/oracle/oci-go-sdk/dns", - "github.com/pkg/errors", - "github.com/prometheus/client_golang/prometheus", - "github.com/prometheus/client_golang/prometheus/promhttp", - "github.com/sanyu/dynectsoap/dynectsoap", - "github.com/sirupsen/logrus", - "github.com/stretchr/testify/assert", - "github.com/stretchr/testify/mock", - "github.com/stretchr/testify/require", - "github.com/stretchr/testify/suite", - "golang.org/x/net/context", - "golang.org/x/oauth2", - "golang.org/x/oauth2/google", - "google.golang.org/api/dns/v1", - "google.golang.org/api/googleapi", - "gopkg.in/yaml.v2", - "istio.io/api/networking/v1alpha3", - "istio.io/istio/pilot/pkg/config/kube/crd", - "istio.io/istio/pilot/pkg/model", - "k8s.io/api/core/v1", - "k8s.io/api/extensions/v1beta1", - "k8s.io/apimachinery/pkg/api/errors", - "k8s.io/apimachinery/pkg/apis/meta/v1", - "k8s.io/apimachinery/pkg/labels", - "k8s.io/apimachinery/pkg/runtime", - "k8s.io/apimachinery/pkg/runtime/schema", - "k8s.io/apimachinery/pkg/runtime/serializer", - "k8s.io/apimachinery/pkg/util/wait", - "k8s.io/client-go/informers", - "k8s.io/client-go/informers/core/v1", - "k8s.io/client-go/informers/extensions/v1beta1", - "k8s.io/client-go/kubernetes", - "k8s.io/client-go/kubernetes/fake", - "k8s.io/client-go/plugin/pkg/client/auth", - "k8s.io/client-go/rest", - "k8s.io/client-go/rest/fake", - "k8s.io/client-go/tools/cache", - "k8s.io/client-go/tools/clientcmd", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index 0489ac11d..000000000 --- a/Gopkg.toml +++ /dev/null @@ -1,125 +0,0 @@ -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. - -required = ["github.com/kubernetes/repo-infra/verify/boilerplate/test"] -ignored = ["github.com/kubernetes/repo-infra/kazel"] - -[[constraint]] - name = "github.com/Azure/azure-sdk-for-go" - version = "~10.0.4-beta" - -[[constraint]] - name = "github.com/Azure/go-autorest" - version = "~10.9.0" - -[[constraint]] - name = "github.com/alecthomas/kingpin" - version = "~2.2.4" - -[[constraint]] - name = "github.com/aws/aws-sdk-go" - version = "~1.13.7" - -[[constraint]] - branch = "master" - name = "github.com/cloudflare/cloudflare-go" - -[[constraint]] - name = "github.com/digitalocean/godo" - version = "~1.1.0" - -[[constraint]] - name = "github.com/dnsimple/dnsimple-go" - version = "0.14.0" - -[[constraint]] - branch = "master" - name = "github.com/infobloxopen/infoblox-go-client" - -[[constraint]] - name = "github.com/linki/instrumented_http" - version = "0.2.0" - -[[constraint]] - name = "github.com/prometheus/client_golang" - version = "0.9.0-pre1" - -[[constraint]] - name = "github.com/sirupsen/logrus" - version = "~1.0.3" - -[[constraint]] - name = "github.com/stretchr/testify" - version = "~1.2.1" - -[[override]] - name = "github.com/kubernetes/repo-infra" - branch = "master" - -[[constraint]] - name = "github.com/nesv/go-dynect" - version = "0.6.0" - -[[constraint]] - name = "github.com/exoscale/egoscale" - version = "~0.11.0" - -[[constraint]] - name = "github.com/oracle/oci-go-sdk" - version = "1.8.0" - -[[constraint]] - name = "github.com/linode/linodego" - version = "0.3.0" - -[[constraint]] - name = "github.com/aliyun/alibaba-cloud-sdk-go" - version = "1.27.7" - -[[constraint]] - name = "istio.io/istio" - version = "1.1.0" - -[[override]] - name = "github.com/golang/glog" - source = "github.com/kubermatic/glog-logrus" - -[[override]] - name = "github.com/golang/protobuf" - version = "1.1.0" - -[[constraint]] - name = "k8s.io/client-go" - version = "8.0.0" - -[[override]] - name = "k8s.io/apimachinery" - version = "kubernetes-1.11.0" - -[[override]] - name = "k8s.io/api" - version = "kubernetes-1.11.0" - -[[override]] - name = "golang.org/x/sys" - revision = "13d03a9a82fba647c21a0ef8fba44a795d0f0835" - -[[override]] - name = "github.com/spf13/pflag" - version = "1.0.2" - -[[override]] - name = "golang.org/x/net" - revision = "161cd47e91fd58ac17490ef4d742dc98bb4cf60e" - -[[constraint]] - name = "github.com/miekg/dns" - version = "1.0.8" - -[[constraint]] - name = "github.com/sanyu/dynectsoap" - branch = "master" - -[[constraint]] - name = "github.com/nic-at/rc0go" - version = "1.1.0" diff --git a/Makefile b/Makefile index 9771ab8f2..8ba62f20f 100644 --- a/Makefile +++ b/Makefile @@ -27,9 +27,12 @@ cover: cover-html: cover go tool cover -html cover.out -dep: - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh - dep ensure -vendor-only +.PHONY: lint + +# Run all the linters +lint: + golangci-lint run ./... + # The verify target runs tasks similar to the CI tasks, but without code coverage .PHONY: verify test @@ -37,10 +40,6 @@ dep: test: go test -v -race $(shell go list ./... | grep -v /vendor/) -verify: test - vendor/github.com/kubernetes/repo-infra/verify/verify-boilerplate.sh --rootdir=${CURDIR} - vendor/github.com/kubernetes/repo-infra/verify/verify-go-src.sh -v --rootdir ${CURDIR} - # The build targets allow to build the binary and docker image .PHONY: build build.docker diff --git a/go.mod b/go.mod new file mode 100644 index 000000000..3ad9578aa --- /dev/null +++ b/go.mod @@ -0,0 +1,103 @@ +module github.com/kubernetes-incubator/external-dns + +go 1.12 + +require ( + cloud.google.com/go v0.34.0 + github.com/Azure/azure-sdk-for-go v10.0.4-beta+incompatible + github.com/Azure/go-autorest v10.9.0+incompatible + github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 // indirect + github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 // indirect + github.com/alecthomas/kingpin v2.2.5+incompatible + github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1 // indirect + github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20180828111155-cad214d7d71f + github.com/aws/aws-sdk-go v1.13.32 + github.com/cenkalti/backoff v2.1.1+incompatible // indirect + github.com/cloudflare/cloudflare-go v0.0.0-20190102215809-0c85496d8730 + github.com/coreos/bbolt v1.3.2 // indirect + github.com/coreos/etcd v3.3.10+incompatible + github.com/coreos/go-semver v0.2.0 // indirect + github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e // indirect + github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect + github.com/denverdino/aliyungo v0.0.0-20180815121905-69560d9530f5 + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect + github.com/digitalocean/godo v1.1.1 + github.com/dnaeon/go-vcr v1.0.1 // indirect + github.com/dnsimple/dnsimple-go v0.14.0 + github.com/envoyproxy/go-control-plane v0.6.9 // indirect + github.com/exoscale/egoscale v0.11.0 + github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99 + github.com/go-ini/ini v1.32.0 // indirect + github.com/go-resty/resty v1.8.0 // indirect + github.com/gogo/googleapis v1.1.0 // indirect + github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect + github.com/golang/mock v1.2.0 // indirect + github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a // indirect + github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect + github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect + github.com/googleapis/gnostic v0.2.0 // indirect + github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8 + github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c // indirect + github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc // indirect + github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79 // indirect + github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.8.5 // indirect + github.com/hashicorp/go-multierror v1.0.0 // indirect + github.com/imdario/mergo v0.3.5 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/infobloxopen/infoblox-go-client v0.0.0-20180606155407-61dc5f9b0a65 + github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 // indirect + github.com/jonboulle/clockwork v0.1.0 // indirect + github.com/json-iterator/go v1.1.6 // indirect + github.com/linki/instrumented_http v0.2.0 + github.com/linode/linodego v0.3.0 + github.com/lyft/protoc-gen-validate v0.0.14 // indirect + github.com/mattn/go-isatty v0.0.7 // indirect + github.com/miekg/dns v1.0.8 + github.com/mitchellh/mapstructure v1.1.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/natefinch/lumberjack v2.0.0+incompatible // indirect + github.com/nesv/go-dynect v0.6.0 + github.com/nic-at/rc0go v1.1.0 + github.com/onsi/ginkgo v1.8.0 // indirect + github.com/onsi/gomega v1.5.0 // indirect + github.com/oracle/oci-go-sdk v1.8.0 + github.com/peterbourgon/diskv v2.0.1+incompatible // indirect + github.com/pkg/errors v0.8.0 + github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 + github.com/sanyu/dynectsoap v0.0.0-20181203081243-b83de5edc4e0 + github.com/satori/go.uuid v1.2.0 // indirect + github.com/sergi/go-diff v1.0.0 // indirect + github.com/sirupsen/logrus v1.2.0 + github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect + github.com/soheilhy/cmux v0.1.3 // indirect + github.com/spf13/cobra v0.0.3 // indirect + github.com/spf13/pflag v1.0.2 // indirect + github.com/stretchr/testify v1.2.2 + github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9 // indirect + github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8 // indirect + github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780 // indirect + github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect + github.com/yl2chen/cidranger v0.0.0-20180214081945-928b519e5268 // indirect + go.etcd.io/bbolt v1.3.2 // indirect + go.uber.org/atomic v1.3.2 // indirect + go.uber.org/multierr v1.1.0 // indirect + go.uber.org/zap v1.9.1 // indirect + golang.org/x/net v0.0.0-20190311183353-d8887717615a + golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 + google.golang.org/api v0.3.0 + google.golang.org/appengine v1.5.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ini.v1 v1.42.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect + gopkg.in/yaml.v2 v2.2.2 + istio.io/api v0.0.0-20190321180614-db16d82d3672 + istio.io/istio v0.0.0-20190322063008-2b1331886076 + k8s.io/api v0.0.0-20180628040859-072894a440bd + k8s.io/apiextensions-apiserver v0.0.0-20180628053655-3de98c57bc05 // indirect + k8s.io/apimachinery v0.0.0-20180621070125-103fd098999d + k8s.io/client-go v8.0.0+incompatible + k8s.io/kube-openapi v0.0.0-20190401085232-94e1e7b7574c // indirect + launchpad.net/gocheck v0.0.0-20140225173054-000000000087 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 000000000..56c515220 --- /dev/null +++ b/go.sum @@ -0,0 +1,406 @@ +cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/Azure/azure-sdk-for-go v10.0.4-beta+incompatible h1:FhnlL7/4O3gAB7EBgN43vA3Bb0fAlCBIMm9avXbcHlE= +github.com/Azure/azure-sdk-for-go v10.0.4-beta+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-autorest v10.9.0+incompatible h1:3ccqKLQg+scl0J6krcDgih2Rl+GC1eNuHZeRQYQxKkk= +github.com/Azure/go-autorest v10.9.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= +github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= +github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo= +github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= +github.com/alecthomas/kingpin v2.2.5+incompatible h1:umWl1NNd72+ZvRti3T9C0SYean2hPZ7ZhxU8bsgc9BQ= +github.com/alecthomas/kingpin v2.2.5+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE= +github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1 h1:GDQdwm/gAcJcLAKQQZGOJ4knlw+7rfEQQcmwTbt4p5E= +github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20180828111155-cad214d7d71f h1:hinXH9rcBjRoIih5tl4f1BCbNjOmPJ2UnZwcYDhEHR0= +github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20180828111155-cad214d7d71f/go.mod h1:T9M45xf79ahXVelWoOBmH0y4aC1t5kXO5BxwyakgIGA= +github.com/apache/thrift v0.12.0 h1:pODnxUFNcjP9UTLZGTdeh+j16A8lJbRvD3rOtrk/7bs= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/aws/aws-sdk-go v1.13.32 h1:AoV2boU+diwKoMaschMtUJim3nmBpM/4y45UqY708F4= +github.com/aws/aws-sdk-go v1.13.32/go.mod h1:ZRmQr0FajVIyZ4ZzBYKG5P3ZqPz9IHG41ZoMu1ADI3k= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY= +github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/cloudflare-go v0.0.0-20190102215809-0c85496d8730 h1:+TK6ytATp7coqI4UlTBboFYD0kSkWZt6L6/T+1yBK6k= +github.com/cloudflare/cloudflare-go v0.0.0-20190102215809-0c85496d8730/go.mod h1:qKQ9S///VKEax9N8kFel9/AvmnkYgvb8uiKTnoVFvpg= +github.com/codegangsta/cli v1.20.0 h1:iX1FXEgwzd5+XN6wk5cVHOGQj6Q3Dcp20lUeS4lHNTw= +github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= +github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denverdino/aliyungo v0.0.0-20180815121905-69560d9530f5 h1:YjnQWGUNtqeKqndapy9V1BzlfMwc/dBJf2MU9dmuXSQ= +github.com/denverdino/aliyungo v0.0.0-20180815121905-69560d9530f5/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/digitalocean/godo v1.1.1 h1:v0A7yF3xmKLjjdJGIeBbINfMufcrrRhqZsxuVQMoT+U= +github.com/digitalocean/godo v1.1.1/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= +github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/dnsimple/dnsimple-go v0.14.0 h1:JGYtVVA/uHc91q0LjDWqR1oVj6EGu9Kn0lMRxjH/w30= +github.com/dnsimple/dnsimple-go v0.14.0/go.mod h1:0FYu4qVNv/UcfZPNwa9zi68IkggJu3TIwM54D7rhmI4= +github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/envoyproxy/go-control-plane v0.6.9 h1:deEH9W8ZAUGNbCdX+9iNzBOGrAOrnpJGoy0PcTqk/tE= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/exoscale/egoscale v0.11.0 h1:g+UBsxLDouKWW2BK/UTgQFAVnM2aHygheF0Dxj0ycC8= +github.com/exoscale/egoscale v0.11.0/go.mod h1:Ee3U4ZjSDpbbEc9VkQ/jttUU8USE8Nv7L3YzVi03Y1U= +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/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-ini/ini v1.32.0 h1:/MArBHSS0TFR28yPPDK1vPIjt4wUnPBfb81i6iiyKvA= +github.com/go-ini/ini v1.32.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-resty/resty v1.8.0 h1:vbNCxbHOWCototzwxf3L63PQCKx6xgT6v8SHfoqkp6U= +github.com/go-resty/resty v1.8.0/go.mod h1:n37daLLGIHq2FFYHxg+FYQiwA95FpfNI+A9uxoIYGRk= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/googleapis v1.1.0 h1:kFkMAZBNAn4j7K0GiZr8cRYzejq68VbheufiV3YuyFI= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a h1:ZJu5NB1Bk5ms4vw0Xu4i+jD32SE9jQXyfnOvwhHqlT0= +github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0= +github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= +github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8 h1:L9JPKrtsHMQ4VCRQfHvbbHBfB2Urn8xf6QZeXZ+OrN4= +github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c h1:Lh2aW+HnU2Nbe1gqD9SOJLJxW1jBMmQOktN2acDyJk8= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc h1:f8eY6cV/x1x+HLjOp4r72s/31/V2aTUtg5oKRRPf8/Q= +github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79 h1:lR9ssWAqp9qL0bALxqEEkuudiP1eweOdv9jsRK3e7lE= +github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.6.2/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE= +github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/infobloxopen/infoblox-go-client v0.0.0-20180606155407-61dc5f9b0a65 h1:FP5rOFP4ifbtFIjFHJmwhFrsbDyONILK/FNntl/Pou8= +github.com/infobloxopen/infoblox-go-client v0.0.0-20180606155407-61dc5f9b0a65/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/linki/instrumented_http v0.2.0 h1:zLhcB3Q/McQQqml3qd5kzdZ0cGnL3vquPFIW2338f5Y= +github.com/linki/instrumented_http v0.2.0/go.mod h1:pjYbItoegfuVi2GUOMhEqzvm/SJKuEL3H0tc8QRLRFk= +github.com/linode/linodego v0.3.0 h1:I83pEPg4owSy5pCPaKix7xkGbWIjPxmAoc/Yu5OYDDY= +github.com/linode/linodego v0.3.0/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY= +github.com/lyft/protoc-gen-validate v0.0.14 h1:xbdDVIHd0Xq5Bfzu+8JR9s7mFmJPMvNLmfGhgcHJdFU= +github.com/lyft/protoc-gen-validate v0.0.14/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.8 h1:Zi8HNpze3NeRWH1PQV6O71YcvJRQ6j0lORO6DAEmAAI= +github.com/miekg/dns v1.0.8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +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= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= +github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= +github.com/nesv/go-dynect v0.6.0 h1:Ow/DiSm4LAISwnFku/FITSQHnU6pBvhQMsUE5Gu6Oq4= +github.com/nesv/go-dynect v0.6.0/go.mod h1:GHRBRKzTwjAMhosHJQq/KrZaFkXIFyJ5zRE7thGXXrs= +github.com/nic-at/rc0go v1.1.0 h1:k6/Bru/npTjmCSFw65ulYRw/b3ycIS30t6/YM4r42V4= +github.com/nic-at/rc0go v1.1.0/go.mod h1:KEa3H5fmDNXCaXSqOeAZxkKnG/8ggr1OHIG25Ve7fjU= +github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/openzipkin/zipkin-go v0.1.6 h1:yXiysv1CSK7Q5yjGy1710zZGnsbMUIjluWBxtLXHPBo= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/oracle/oci-go-sdk v1.8.0 h1:4SO45bKV0I3/Mn1os3ANDZmV0eSE5z5CLdSUIkxtyzs= +github.com/oracle/oci-go-sdk v1.8.0/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= +github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f h1:BVwpUVJDADN2ufcGik7W992pyps0wZ888b/y9GXcLTU= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1 h1:/K3IL0Z1quvmJ7X0A1AwNEK7CRkVK3YwfOU/QAL4WGg= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af h1:gu+uRPtBe88sKxUCEXRoeCvVG90TJmwhiqRpvdhQFng= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/sanyu/dynectsoap v0.0.0-20181203081243-b83de5edc4e0 h1:vOcHdR1nu7DO4BAx1rwzdHV7jQTzW3gqcBT5qxHSc6A= +github.com/sanyu/dynectsoap v0.0.0-20181203081243-b83de5edc4e0/go.mod h1:FeplEtXXejBYC4NPAFTrs5L7KuK+5RL9bf5nB2vZe9o= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.3 h1:09wy7WZk4AqO03yH85Ex1X+Uo3vDsil3Fa9AgF8Emss= +github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc= +github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9 h1:/Bsw4C+DEdqPjt8vAqaC9LAqpAQnaCQQqmolqq3S1T4= +github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9/go.mod h1:RHkNRtSLfOK7qBTHaeSX1D6BNpI3qw7NTxsmNr4RvN8= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8 h1:ndzgwNDnKIqyCvHTXaCqh9KlOWKvBry6nuXMJmonVsE= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.2 h1:JON3E2/GPW2iDNGoSAusl1KDf5TRQ8k8q7Tp097pZGs= +github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= +github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780 h1:vG/gY/PxA3v3l04qxe3tDjXyu3bozii8ulSlIPOYKhI= +github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yl2chen/cidranger v0.0.0-20180214081945-928b519e5268 h1:lkoOjizoHqOcEFsvYGE5c8Ykdijjnd0R3r1yDYHzLno= +github.com/yl2chen/cidranger v0.0.0-20180214081945-928b519e5268/go.mod h1:mq0zhomp/G6rRTb0dvHWXRHr/2+Qgeq5hMXfJ670+i4= +go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A= +go.opencensus.io v0.19.2 h1:ZZpq6xI6kv/LuE/5s5UQvBU5vMjvRnPb8PvJrIntAnc= +go.opencensus.io v0.19.2/go.mod h1:NO/8qkisMZLZ1FCsKNqtJPwc8/TaclWyY0B6wcYNg9M= +go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4 h1:c2HOrn5iMezYjSlGPncknSEr/8x5LELb/ilJbXi9DEA= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 h1:x/bBzNauLQAlE3fLku/xy92Y8QwKX5HZymrMz2IiKFc= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f h1:hX65Cu3JDlGH3uEdK7I99Ii+9kjD6mvnnpfLdEAH0x4= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384 h1:TFlARGu6Czu1z7q93HTxcP1P+/ZFC/IKythI5RzrnRg= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.2.0/go.mod h1:IfRCZScioGtypHNTlz3gFk67J8uePVW7uDTBzXuIkhU= +google.golang.org/api v0.3.0 h1:UIJY20OEo3+tK5MBlcdx37kmdH6EnRjGkW78mc6+EeA= +google.golang.org/api v0.3.0/go.mod h1:IuvZyQh8jgscv8qWfQ4ABd8m7hEudgBFM/EdhA3BnXw= +google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181219182458-5a97ab628bfb/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +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.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk= +gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099 h1:XJP7lxbSxWLOMNdBE4B/STaqVy6L73o0knwj2vIlxnw= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +istio.io/api v0.0.0-20190321180614-db16d82d3672 h1:luY97pBVarSo1v++zf2kgb84Q55G5hv/ult2A4KPQuk= +istio.io/api v0.0.0-20190321180614-db16d82d3672/go.mod h1:hhLFQmpHia8zgaM37vb2ml9iS5NfNfqZGRt1pS9aVEo= +istio.io/istio v0.0.0-20190322063008-2b1331886076 h1:gZhCrmVzfQJoDl4oav8i5+NF7p7v0M1Pou+2O+hZBtc= +istio.io/istio v0.0.0-20190322063008-2b1331886076/go.mod h1:OWBySrQjjk549IhxWCt7DTl9ZSsXdvbgm+SmgGVRsGA= +k8s.io/api v0.0.0-20180628040859-072894a440bd h1:HzgYeLDS1jLxw8DGr68KJh9cdQ5iZJizG0HZWstIhfQ= +k8s.io/api v0.0.0-20180628040859-072894a440bd/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= +k8s.io/apiextensions-apiserver v0.0.0-20180628053655-3de98c57bc05 h1:uKDX+1GgQuV/J6TTgrtHYGRFZUPWxC13mJwBhjIhm/w= +k8s.io/apiextensions-apiserver v0.0.0-20180628053655-3de98c57bc05/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE= +k8s.io/apimachinery v0.0.0-20180621070125-103fd098999d h1:MZjlsu9igBoVPZkXpIGoxI6EonqNsXXZU7hhvfQLkd4= +k8s.io/apimachinery v0.0.0-20180621070125-103fd098999d/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= +k8s.io/client-go v8.0.0+incompatible h1:tTI4hRmb1DRMl4fG6Vclfdi6nTM82oIrTT7HfitmxC4= +k8s.io/client-go v8.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= +k8s.io/kube-openapi v0.0.0-20190401085232-94e1e7b7574c h1:kJCzg2vGCzah5icgkKR7O1Dzn0NA2iGlym27sb0ZfGE= +k8s.io/kube-openapi v0.0.0-20190401085232-94e1e7b7574c/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= diff --git a/provider/alibaba_cloud.go b/provider/alibaba_cloud.go index 1bfcd3566..a0b66734b 100644 --- a/provider/alibaba_cloud.go +++ b/provider/alibaba_cloud.go @@ -19,22 +19,18 @@ package provider import ( "fmt" "io/ioutil" - - log "github.com/sirupsen/logrus" - - "gopkg.in/yaml.v2" - - "github.com/aliyun/alibaba-cloud-sdk-go/services/alidns" - - "github.com/kubernetes-incubator/external-dns/endpoint" - "github.com/kubernetes-incubator/external-dns/plan" - - "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" - "github.com/aliyun/alibaba-cloud-sdk-go/services/pvtz" - "github.com/denverdino/aliyungo/metadata" "strings" "sync" "time" + + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/services/alidns" + "github.com/aliyun/alibaba-cloud-sdk-go/services/pvtz" + "github.com/denverdino/aliyungo/metadata" + "github.com/kubernetes-incubator/external-dns/endpoint" + "github.com/kubernetes-incubator/external-dns/plan" + log "github.com/sirupsen/logrus" + yaml "gopkg.in/yaml.v2" ) const ( @@ -155,6 +151,10 @@ func NewAlibabaCloudProvider(configFile string, domainFilter DomainFilter, zoneI ) } + if err != nil { + return nil, err + } + provider := &AlibabaCloudProvider{ domainFilter: domainFilter, zoneIDFilter: zoneIDFileter, diff --git a/provider/alibaba_cloud_test.go b/provider/alibaba_cloud_test.go index d9d70a1cc..defbfad1b 100644 --- a/provider/alibaba_cloud_test.go +++ b/provider/alibaba_cloud_test.go @@ -17,10 +17,11 @@ limitations under the License. package provider import ( + "testing" + "github.com/aliyun/alibaba-cloud-sdk-go/services/alidns" "github.com/aliyun/alibaba-cloud-sdk-go/services/pvtz" "github.com/kubernetes-incubator/external-dns/endpoint" - "testing" "github.com/kubernetes-incubator/external-dns/plan" ) diff --git a/provider/aws_sd.go b/provider/aws_sd.go index 480dfbd5a..2bd74a132 100644 --- a/provider/aws_sd.go +++ b/provider/aws_sd.go @@ -22,6 +22,7 @@ import ( "crypto/sha256" "encoding/hex" "fmt" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials/stscreds" "github.com/aws/aws-sdk-go/aws/request" diff --git a/provider/aws_test.go b/provider/aws_test.go index f4ca41f6e..5010f0894 100644 --- a/provider/aws_test.go +++ b/provider/aws_test.go @@ -990,7 +990,7 @@ func setupAWSRecords(t *testing.T, provider *AWSProvider, endpoints []*endpoint. 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.") - records, err = provider.Records() + _, err = provider.Records() require.NoError(t, err) } diff --git a/provider/designate_test.go b/provider/designate_test.go index baba6ee35..db060b92f 100644 --- a/provider/designate_test.go +++ b/provider/designate_test.go @@ -110,7 +110,9 @@ func (c fakeDesignateClient) UpdateRecordSet(zoneID, recordSetID string, opts re if rs == nil { return fmt.Errorf("unknown record-set %s", recordSetID) } - rs.Description = opts.Description + if opts.Description != nil { + rs.Description = *opts.Description + } rs.TTL = opts.TTL rs.Records = opts.Records return nil diff --git a/provider/dnsimple_test.go b/provider/dnsimple_test.go index 78cf3658c..b0139c4eb 100644 --- a/provider/dnsimple_test.go +++ b/provider/dnsimple_test.go @@ -136,7 +136,7 @@ func testDnsimpleProviderZones(t *testing.T) { validateDnsimpleZones(t, result, dnsimpleListZonesResponse.Data) mockProvider.accountID = "2" - result, err = mockProvider.Zones() + _, err = mockProvider.Zones() assert.NotNil(t, err) } @@ -147,7 +147,7 @@ func testDnsimpleProviderRecords(t *testing.T) { assert.Equal(t, len(dnsimpleListRecordsResponse.Data), len(result)) mockProvider.accountID = "2" - result, err = mockProvider.Records() + _, err = mockProvider.Records() assert.NotNil(t, err) } func testDnsimpleProviderApplyChanges(t *testing.T) { diff --git a/provider/google.go b/provider/google.go index 0474a7446..94d933349 100644 --- a/provider/google.go +++ b/provider/google.go @@ -143,9 +143,9 @@ func NewGoogleProvider(project string, domainFilter DomainFilter, zoneIDFilter Z provider := &GoogleProvider{ project: project, + dryRun: dryRun, domainFilter: domainFilter, zoneIDFilter: zoneIDFilter, - dryRun: dryRun, resourceRecordSetsClient: resourceRecordSetsService{dnsClient.ResourceRecordSets}, managedZonesClient: managedZonesService{dnsClient.ManagedZones}, changesClient: changesService{dnsClient.Changes}, diff --git a/provider/google_test.go b/provider/google_test.go index a58d2520b..8f7ea9162 100644 --- a/provider/google_test.go +++ b/provider/google_test.go @@ -565,9 +565,9 @@ func validateChangeRecord(t *testing.T, record *dns.ResourceRecordSet, expected func newGoogleProvider(t *testing.T, domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, dryRun bool, records []*endpoint.Endpoint) *GoogleProvider { provider := &GoogleProvider{ project: "zalando-external-dns-test", + dryRun: false, domainFilter: domainFilter, zoneIDFilter: zoneIDFilter, - dryRun: false, resourceRecordSetsClient: &mockResourceRecordSetsClient{}, managedZonesClient: &mockManagedZonesClient{}, changesClient: &mockChangesClient{}, diff --git a/provider/linode_test.go b/provider/linode_test.go index 974f8f770..5b7169f05 100644 --- a/provider/linode_test.go +++ b/provider/linode_test.go @@ -131,7 +131,7 @@ func TestLinodeConvertRecordType(t *testing.T) { require.NoError(t, err) assert.Equal(t, linodego.RecordTypeSRV, record) - record, err = convertRecordType("INVALID") + _, err = convertRecordType("INVALID") require.Error(t, err) } diff --git a/provider/pdns_test.go b/provider/pdns_test.go index 76fdb7041..7caf48a04 100644 --- a/provider/pdns_test.go +++ b/provider/pdns_test.go @@ -19,6 +19,7 @@ package provider import ( "context" "errors" + //"fmt" "net/http" "strings" @@ -805,13 +806,13 @@ func (suite *NewPDNSProviderTestSuite) TestPDNSRecords() { p = &PDNSProvider{ client: &PDNSAPIClientStubListZoneFailure{}, } - eps, err = p.Records() + _, err = p.Records() assert.NotNil(suite.T(), err) p = &PDNSProvider{ client: &PDNSAPIClientStubListZonesFailure{}, } - eps, err = p.Records() + _, err = p.Records() assert.NotNil(suite.T(), err) } diff --git a/provider/rcode0.go b/provider/rcode0.go index 07533ebd9..866dd735c 100644 --- a/provider/rcode0.go +++ b/provider/rcode0.go @@ -18,13 +18,14 @@ package provider import ( "fmt" + "net/url" + "os" + "strings" + "github.com/kubernetes-incubator/external-dns/endpoint" "github.com/kubernetes-incubator/external-dns/plan" rc0 "github.com/nic-at/rc0go" log "github.com/sirupsen/logrus" - "net/url" - "os" - "strings" ) // RcodeZeroProvider implements the DNS provider for RcodeZero Anycast DNS. @@ -50,7 +51,7 @@ func NewRcodeZeroProvider(domainFilter DomainFilter, dryRun bool, txtEnc bool) ( value := os.Getenv("RC0_BASE_URL") if len(value) != 0 { - client.BaseURL, _ = url.Parse(os.Getenv("RC0_BASE_URL")) + client.BaseURL, err = url.Parse(os.Getenv("RC0_BASE_URL")) } if err != nil { diff --git a/provider/rcode0_test.go b/provider/rcode0_test.go index 73c312a7a..9355a3733 100644 --- a/provider/rcode0_test.go +++ b/provider/rcode0_test.go @@ -18,12 +18,13 @@ package provider import ( "fmt" + "os" + "testing" + "github.com/kubernetes-incubator/external-dns/endpoint" "github.com/kubernetes-incubator/external-dns/plan" rc0 "github.com/nic-at/rc0go" "github.com/stretchr/testify/require" - "os" - "testing" ) const ( @@ -229,7 +230,7 @@ func TestRcodeZeroProvider_Zones(t *testing.T) { mockZoneManagementService.TestErrorReturned = true - zones, err = provider.Zones() + _, err = provider.Zones() if err == nil { t.Errorf("expected to fail, %s", err) } diff --git a/source/compatibility.go b/source/compatibility.go index b4398b0d1..e25184d04 100644 --- a/source/compatibility.go +++ b/source/compatibility.go @@ -19,9 +19,8 @@ package source import ( "strings" - "k8s.io/api/core/v1" - "github.com/kubernetes-incubator/external-dns/endpoint" + v1 "k8s.io/api/core/v1" ) const ( diff --git a/source/gateway_test.go b/source/gateway_test.go index 763aedfb3..d82d30ed4 100644 --- a/source/gateway_test.go +++ b/source/gateway_test.go @@ -31,7 +31,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" ) @@ -1008,9 +1008,8 @@ func testGatewayEndpoints(t *testing.T) { fqdnTemplate: "{{.Name}}.ext-dns.test.com", }, { - title: "ignore hostname annotations", - ignoreHostnameAnnotation: true, - targetNamespace: "", + title: "ignore hostname annotations", + targetNamespace: "", lbServices: []fakeIngressGatewayService{ { ips: []string{"8.8.8.8"}, @@ -1053,6 +1052,7 @@ func testGatewayEndpoints(t *testing.T) { Targets: endpoint.Targets{"lb.com"}, }, }, + ignoreHostnameAnnotation: true, }, } { t.Run(ti.title, func(t *testing.T) { diff --git a/source/ingress.go b/source/ingress.go index 163ea2886..ff3f00bef 100644 --- a/source/ingress.go +++ b/source/ingress.go @@ -23,6 +23,8 @@ import ( "strings" "text/template" + "time" + "github.com/kubernetes-incubator/external-dns/endpoint" log "github.com/sirupsen/logrus" "k8s.io/api/extensions/v1beta1" @@ -33,7 +35,6 @@ import ( extinformers "k8s.io/client-go/informers/extensions/v1beta1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" - "time" ) // ingressSource is an implementation of Source for Kubernetes ingress objects. diff --git a/source/ingress_test.go b/source/ingress_test.go index c9b9164de..ee79177a5 100644 --- a/source/ingress_test.go +++ b/source/ingress_test.go @@ -20,7 +20,7 @@ import ( "testing" "time" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/api/extensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" diff --git a/source/service.go b/source/service.go index 4c26bb5bf..7ae70997b 100644 --- a/source/service.go +++ b/source/service.go @@ -19,13 +19,14 @@ package source import ( "bytes" "fmt" - kubeinformers "k8s.io/client-go/informers" - coreinformers "k8s.io/client-go/informers/core/v1" - "k8s.io/client-go/tools/cache" "sort" "strings" "text/template" + kubeinformers "k8s.io/client-go/informers" + coreinformers "k8s.io/client-go/informers/core/v1" + "k8s.io/client-go/tools/cache" + log "github.com/sirupsen/logrus" v1 "k8s.io/api/core/v1" @@ -35,8 +36,9 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" - "github.com/kubernetes-incubator/external-dns/endpoint" "time" + + "github.com/kubernetes-incubator/external-dns/endpoint" ) const ( diff --git a/source/service_test.go b/source/service_test.go index 6c8b1fc8f..650dda759 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -17,11 +17,12 @@ limitations under the License. package source import ( - "k8s.io/apimachinery/pkg/util/wait" "net" "testing" "time" + "k8s.io/apimachinery/pkg/util/wait" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" diff --git a/source/shared_test.go b/source/shared_test.go index fe27e626e..09645fcb4 100644 --- a/source/shared_test.go +++ b/source/shared_test.go @@ -17,10 +17,11 @@ limitations under the License. package source import ( - "github.com/kubernetes-incubator/external-dns/endpoint" "sort" "strings" "testing" + + "github.com/kubernetes-incubator/external-dns/endpoint" ) // test helper functions From 44c32e1ed8d1c4d48177e3bc53573d54e31587f9 Mon Sep 17 00:00:00 2001 From: Jason Fillo Date: Tue, 9 Apr 2019 11:51:44 -0500 Subject: [PATCH 084/136] aws-r53: adding china ELB endpoints and hosted zone id's. fixed typo when reformatting --- provider/aws.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/aws.go b/provider/aws.go index c42d460d2..be2d516d8 100644 --- a/provider/aws.go +++ b/provider/aws.go @@ -62,7 +62,7 @@ var ( "sa-east-1.elb.amazonaws.com": "Z2P70J7HTTTPLU", "cn-north-1.elb.amazonaws.com.cn": "Z3BX2TMKNYI13Y", "cn-northwest-1.elb.amazonaws.com.cn": "Z3BX2TMKNYI13Y", - // Network Load Balance + // Network Load Balancers "elb.us-east-2.amazonaws.com": "ZLMOA37VPKANP", "elb.us-east-1.amazonaws.com": "Z26RNL4JYFTOTI", "elb.us-west-1.amazonaws.com": "Z24FKFUX50B4VW", From f6eeb3ca599b97087e427769c7bf51ae01935d4e Mon Sep 17 00:00:00 2001 From: njuettner Date: Wed, 10 Apr 2019 11:54:17 +0200 Subject: [PATCH 085/136] Fix delivery.yaml to work with go modules --- delivery.yaml | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/delivery.yaml b/delivery.yaml index 222ff01a5..6df6938cd 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -1,15 +1,23 @@ version: "2017-09-20" pipeline: - id: build + overlay: ci/golang + cache: + paths: + - /go/pkg/mod # pkg cache for Go modules + - ~/.cache/go-build # Go build cache type: script commands: - - desc: Build and push Docker image + - desc: build + cmd: | + make build.docker + - desc: push cmd: | if [[ $CDP_TARGET_BRANCH == master && ! $CDP_PULL_REQUEST_NUMBER ]]; then - RELEASE_VERSION=$(git describe --tags --always --dirty) - IMAGE=registry-write.opensource.zalan.do/teapot/external-dns:${RELEASE_VERSION} + IMAGE=registry-write.opensource.zalan.do/teapot/external-dns + VERSION=$(git describe --tags --always --dirty) else - IMAGE=registry-write.opensource.zalan.do/teapot/external-dns-test:${CDP_BUILD_VERSION} + IMAGE=registry-write.opensource.zalan.do/teapot/external-dns-test + VERSION=$CDP_BUILD_VERSION fi - docker build --squash --tag "$IMAGE" . - docker push "$IMAGE" + IMAGE=$IMAGE VERSION=$VERSION make build.push From cb6970eee33ed3d98b72133b6c1ff48c3037b4b8 Mon Sep 17 00:00:00 2001 From: Bas Timmer Date: Wed, 10 Apr 2019 22:10:02 +0200 Subject: [PATCH 086/136] Add support for multiple target addresses in CloudFlare provider --- provider/cloudflare.go | 75 ++++++++++++++++++++++--------------- provider/cloudflare_test.go | 28 ++++++++------ 2 files changed, 60 insertions(+), 43 deletions(-) diff --git a/provider/cloudflare.go b/provider/cloudflare.go index 6fa7c3db5..a7705d0bd 100644 --- a/provider/cloudflare.go +++ b/provider/cloudflare.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "os" + "sort" "strconv" "strings" @@ -111,7 +112,7 @@ type CloudFlareProvider struct { // cloudFlareChange differentiates between ChangActions type cloudFlareChange struct { Action string - ResourceRecordSet cloudflare.DNSRecord + ResourceRecordSet []cloudflare.DNSRecord } // NewCloudFlareProvider initializes a new CloudFlare DNS based Provider. @@ -224,11 +225,12 @@ func (p *CloudFlareProvider) submitChanges(changes []*cloudFlareChange) error { } for _, change := range changes { logFields := log.Fields{ - "record": change.ResourceRecordSet.Name, - "type": change.ResourceRecordSet.Type, - "ttl": change.ResourceRecordSet.TTL, - "action": change.Action, - "zone": zoneID, + "record": change.ResourceRecordSet[0].Name, + "type": change.ResourceRecordSet[0].Type, + "ttl": change.ResourceRecordSet[0].TTL, + "targets": len(change.ResourceRecordSet), + "action": change.Action, + "zone": zoneID, } log.WithFields(logFields).Info("Changing record.") @@ -236,22 +238,25 @@ func (p *CloudFlareProvider) submitChanges(changes []*cloudFlareChange) error { if p.DryRun { continue } - recordID := p.getRecordID(records, change.ResourceRecordSet) - switch change.Action { - case cloudFlareCreate: - _, err := p.Client.CreateDNSRecord(zoneID, change.ResourceRecordSet) - if err != nil { - log.WithFields(logFields).Errorf("failed to create record: %v", err) + + recordIDs := p.getRecordIDs(records, change.ResourceRecordSet[0]) + + // to simplify bookkeeping for multiple records, an update is executed as delete+create + if change.Action == cloudFlareDelete || change.Action == cloudFlareUpdate { + for _, recordID := range recordIDs { + err := p.Client.DeleteDNSRecord(zoneID, recordID) + if err != nil { + log.WithFields(logFields).Errorf("failed to delete record: %v", err) + } } - case cloudFlareDelete: - err := p.Client.DeleteDNSRecord(zoneID, recordID) - if err != nil { - log.WithFields(logFields).Errorf("failed to delete record: %v", err) - } - case cloudFlareUpdate: - err := p.Client.UpdateDNSRecord(zoneID, recordID, change.ResourceRecordSet) - if err != nil { - log.WithFields(logFields).Errorf("failed to update record: %v", err) + } + + if change.Action == cloudFlareCreate || change.Action == cloudFlareUpdate { + for _, record := range change.ResourceRecordSet { + _, err := p.Client.CreateDNSRecord(zoneID, record) + if err != nil { + log.WithFields(logFields).Errorf("failed to create record: %v", err) + } } } } @@ -270,9 +275,9 @@ func (p *CloudFlareProvider) changesByZone(zones []cloudflare.Zone, changeSet [] } for _, c := range changeSet { - zoneID, _ := zoneNameIDMapper.FindZone(c.ResourceRecordSet.Name) + zoneID, _ := zoneNameIDMapper.FindZone(c.ResourceRecordSet[0].Name) if zoneID == "" { - log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected ", c.ResourceRecordSet.Name) + log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected ", c.ResourceRecordSet[0].Name) continue } changes[zoneID] = append(changes[zoneID], c) @@ -281,13 +286,15 @@ func (p *CloudFlareProvider) changesByZone(zones []cloudflare.Zone, changeSet [] return changes } -func (p *CloudFlareProvider) getRecordID(records []cloudflare.DNSRecord, record cloudflare.DNSRecord) string { +func (p *CloudFlareProvider) getRecordIDs(records []cloudflare.DNSRecord, record cloudflare.DNSRecord) []string { + recordIDs := make([]string, 0) for _, zoneRecord := range records { if zoneRecord.Name == record.Name && zoneRecord.Type == record.Type { - return zoneRecord.ID + recordIDs = append(recordIDs, zoneRecord.ID) } } - return "" + sort.Strings(recordIDs) + return recordIDs } // newCloudFlareChanges returns a collection of Changes based on the given records and action. @@ -309,15 +316,21 @@ func newCloudFlareChange(action string, endpoint *endpoint.Endpoint, proxiedByDe ttl = int(endpoint.RecordTTL) } - return &cloudFlareChange{ - Action: action, - ResourceRecordSet: cloudflare.DNSRecord{ + resourceRecordSet := make([]cloudflare.DNSRecord, len(endpoint.Targets)) + + for i := range endpoint.Targets { + resourceRecordSet[i] = cloudflare.DNSRecord{ Name: endpoint.DNSName, TTL: ttl, Proxied: proxied, Type: endpoint.RecordType, - Content: endpoint.Targets[0], - }, + Content: endpoint.Targets[i], + } + } + + return &cloudFlareChange{ + Action: action, + ResourceRecordSet: resourceRecordSet, } } diff --git a/provider/cloudflare_test.go b/provider/cloudflare_test.go index dbad258de..e67d4a7bd 100644 --- a/provider/cloudflare_test.go +++ b/provider/cloudflare_test.go @@ -399,7 +399,7 @@ func TestNewCloudFlareChanges(t *testing.T) { for i, change := range changes { assert.Equal( t, - change.ResourceRecordSet.TTL, + change.ResourceRecordSet[0].TTL, expect[i].TTL, expect[i].Name) } @@ -407,7 +407,7 @@ func TestNewCloudFlareChanges(t *testing.T) { func TestNewCloudFlareChangeNoProxied(t *testing.T) { change := newCloudFlareChange(cloudFlareCreate, &endpoint.Endpoint{DNSName: "new", RecordType: "A", Targets: endpoint.Targets{"target"}}, false) - assert.False(t, change.ResourceRecordSet.Proxied) + assert.False(t, change.ResourceRecordSet[0].Proxied) } func TestNewCloudFlareProxiedAnnotationTrue(t *testing.T) { @@ -417,7 +417,7 @@ func TestNewCloudFlareProxiedAnnotationTrue(t *testing.T) { Value: "true", }, }}, false) - assert.True(t, change.ResourceRecordSet.Proxied) + assert.True(t, change.ResourceRecordSet[0].Proxied) } func TestNewCloudFlareProxiedAnnotationFalse(t *testing.T) { @@ -427,7 +427,7 @@ func TestNewCloudFlareProxiedAnnotationFalse(t *testing.T) { Value: "false", }, }}, true) - assert.False(t, change.ResourceRecordSet.Proxied) + assert.False(t, change.ResourceRecordSet[0].Proxied) } func TestNewCloudFlareProxiedAnnotationIllegalValue(t *testing.T) { @@ -437,7 +437,7 @@ func TestNewCloudFlareProxiedAnnotationIllegalValue(t *testing.T) { Value: "asdaslkjndaslkdjals", }, }}, false) - assert.False(t, change.ResourceRecordSet.Proxied) + assert.False(t, change.ResourceRecordSet[0].Proxied) } func TestNewCloudFlareChangeProxiable(t *testing.T) { @@ -459,14 +459,14 @@ func TestNewCloudFlareChangeProxiable(t *testing.T) { change := newCloudFlareChange(cloudFlareCreate, &endpoint.Endpoint{DNSName: "new", RecordType: cloudFlareType.recordType, Targets: endpoint.Targets{"target"}}, true) if cloudFlareType.proxiable { - assert.True(t, change.ResourceRecordSet.Proxied) + assert.True(t, change.ResourceRecordSet[0].Proxied) } else { - assert.False(t, change.ResourceRecordSet.Proxied) + assert.False(t, change.ResourceRecordSet[0].Proxied) } } change := newCloudFlareChange(cloudFlareCreate, &endpoint.Endpoint{DNSName: "*.foo", RecordType: "A", Targets: endpoint.Targets{"target"}}, true) - assert.False(t, change.ResourceRecordSet.Proxied) + assert.False(t, change.ResourceRecordSet[0].Proxied) } func TestCloudFlareZones(t *testing.T) { @@ -574,14 +574,18 @@ func TestCloudFlareGetRecordID(t *testing.T) { }, } - assert.Equal(t, "", p.getRecordID(records, cloudflare.DNSRecord{ + assert.Len(t, p.getRecordIDs(records, cloudflare.DNSRecord{ Name: "foo.com", Type: endpoint.RecordTypeA, - })) - assert.Equal(t, "2", p.getRecordID(records, cloudflare.DNSRecord{ + }), 0) + assert.Len(t, p.getRecordIDs(records, cloudflare.DNSRecord{ Name: "bar.de", Type: endpoint.RecordTypeA, - })) + }), 1) + assert.Equal(t, "2", p.getRecordIDs(records, cloudflare.DNSRecord{ + Name: "bar.de", + Type: endpoint.RecordTypeA, + })[0]) } func validateCloudFlareZones(t *testing.T, zones []cloudflare.Zone, expected []cloudflare.Zone) { From f4f3c9939fc1efedcd2d54a1ad15a3df649340cf Mon Sep 17 00:00:00 2001 From: Michael Fraenkel Date: Fri, 5 Apr 2019 21:02:26 -0400 Subject: [PATCH 087/136] Streamline AWS ApplyChanges - collect the zones and records once --- provider/aws.go | 64 +++++++++++++++++++++++++++----------------- provider/aws_test.go | 59 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 93 insertions(+), 30 deletions(-) diff --git a/provider/aws.go b/provider/aws.go index 7bfd17ac0..b01bba83d 100644 --- a/provider/aws.go +++ b/provider/aws.go @@ -230,6 +230,11 @@ func (p *AWSProvider) Records() (endpoints []*endpoint.Endpoint, _ error) { return nil, err } + return p.records(zones) +} + +func (p *AWSProvider) records(zones map[string]*route53.HostedZone) ([]*endpoint.Endpoint, error) { + endpoints := make([]*endpoint.Endpoint, 0) f := func(resp *route53.ListResourceRecordSetsOutput, lastPage bool) (shouldContinue bool) { for _, r := range resp.ResourceRecordSets { // TODO(linki, ownership): Remove once ownership system is in place. @@ -279,43 +284,61 @@ func (p *AWSProvider) Records() (endpoints []*endpoint.Endpoint, _ error) { // CreateRecords creates a given set of DNS records in the given hosted zone. func (p *AWSProvider) CreateRecords(endpoints []*endpoint.Endpoint) error { - return p.submitChanges(p.newChanges(route53.ChangeActionCreate, endpoints)) + return p.doRecords(route53.ChangeActionCreate, endpoints) } // UpdateRecords updates a given set of old records to a new set of records in a given hosted zone. func (p *AWSProvider) UpdateRecords(endpoints, _ []*endpoint.Endpoint) error { - return p.submitChanges(p.newChanges(route53.ChangeActionUpsert, endpoints)) + return p.doRecords(route53.ChangeActionUpsert, endpoints) } // DeleteRecords deletes a given set of DNS records in a given zone. func (p *AWSProvider) DeleteRecords(endpoints []*endpoint.Endpoint) error { - return p.submitChanges(p.newChanges(route53.ChangeActionDelete, endpoints)) + return p.doRecords(route53.ChangeActionDelete, endpoints) +} + +func (p *AWSProvider) doRecords(action string, endpoints []*endpoint.Endpoint) error { + zones, err := p.Zones() + if err != nil { + return err + } + + records, err := p.records(zones) + if err != nil { + log.Errorf("getting records failed: %v", err) + } + return p.submitChanges(p.newChanges(action, endpoints, records, zones), zones) } // ApplyChanges applies a given set of changes in a given zone. func (p *AWSProvider) ApplyChanges(changes *plan.Changes) error { + zones, err := p.Zones() + if err != nil { + return err + } + + records, err := p.records(zones) + if err != nil { + log.Errorf("getting records failed: %v", err) + } + combinedChanges := make([]*route53.Change, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete)) - combinedChanges = append(combinedChanges, p.newChanges(route53.ChangeActionCreate, changes.Create)...) - combinedChanges = append(combinedChanges, p.newChanges(route53.ChangeActionUpsert, changes.UpdateNew)...) - combinedChanges = append(combinedChanges, p.newChanges(route53.ChangeActionDelete, changes.Delete)...) + combinedChanges = append(combinedChanges, p.newChanges(route53.ChangeActionCreate, changes.Create, records, zones)...) + combinedChanges = append(combinedChanges, p.newChanges(route53.ChangeActionUpsert, changes.UpdateNew, records, zones)...) + combinedChanges = append(combinedChanges, p.newChanges(route53.ChangeActionDelete, changes.Delete, records, zones)...) - return p.submitChanges(combinedChanges) + return p.submitChanges(combinedChanges, zones) } // submitChanges takes a zone and a collection of Changes and sends them as a single transaction. -func (p *AWSProvider) submitChanges(changes []*route53.Change) error { +func (p *AWSProvider) submitChanges(changes []*route53.Change, zones map[string]*route53.HostedZone) error { // return early if there is nothing to change if len(changes) == 0 { log.Info("All records are already up to date") return nil } - zones, err := p.Zones() - if err != nil { - return err - } - // separate into per-zone change sets to be passed to the API. changesByZone := changesByZone(zones, changes) if len(changesByZone) == 0 { @@ -367,16 +390,11 @@ func (p *AWSProvider) submitChanges(changes []*route53.Change) error { } // newChanges returns a collection of Changes based on the given records and action. -func (p *AWSProvider) newChanges(action string, endpoints []*endpoint.Endpoint) []*route53.Change { - records, err := p.Records() - if err != nil { - log.Errorf("getting records failed: %v", err) - } - +func (p *AWSProvider) newChanges(action string, endpoints []*endpoint.Endpoint, recordsCache []*endpoint.Endpoint, zones map[string]*route53.HostedZone) []*route53.Change { changes := make([]*route53.Change, 0, len(endpoints)) for _, endpoint := range endpoints { - changes = append(changes, p.newChange(action, endpoint, records)) + changes = append(changes, p.newChange(action, endpoint, recordsCache, zones)) } return changes @@ -385,7 +403,7 @@ func (p *AWSProvider) newChanges(action string, endpoints []*endpoint.Endpoint) // newChange returns a Change of the given record by the given action, e.g. // action=ChangeActionCreate returns a change for creation of the record and // action=ChangeActionDelete returns a change for deletion of the record. -func (p *AWSProvider) newChange(action string, endpoint *endpoint.Endpoint, recordsCache []*endpoint.Endpoint) *route53.Change { +func (p *AWSProvider) newChange(action string, endpoint *endpoint.Endpoint, recordsCache []*endpoint.Endpoint, zones map[string]*route53.HostedZone) *route53.Change { change := &route53.Change{ Action: aws.String(action), ResourceRecordSet: &route53.ResourceRecordSet{ @@ -406,10 +424,6 @@ func (p *AWSProvider) newChange(action string, endpoint *endpoint.Endpoint, reco EvaluateTargetHealth: aws.Bool(evalTargetHealth), } } else if hostedZone := isAWSAlias(endpoint, recordsCache); hostedZone != "" { - zones, err := p.Zones() - if err != nil { - log.Errorf("getting zones failed: %v", err) - } for _, zone := range zones { change.ResourceRecordSet.Type = aws.String(route53.RRTypeA) change.ResourceRecordSet.AliasTarget = &route53.AliasTarget{ diff --git a/provider/aws_test.go b/provider/aws_test.go index f4ca41f6e..0ba9a9e9f 100644 --- a/provider/aws_test.go +++ b/provider/aws_test.go @@ -89,6 +89,43 @@ func (r *Route53APIStub) ListResourceRecordSetsPages(input *route53.ListResource return nil } +type Route53APICounter struct { + wrapped Route53API + calls map[string]int +} + +func NewRoute53APICounter(w Route53API) *Route53APICounter { + return &Route53APICounter{ + wrapped: w, + calls: map[string]int{}, + } +} + +func (c *Route53APICounter) ListResourceRecordSetsPages(input *route53.ListResourceRecordSetsInput, fn func(resp *route53.ListResourceRecordSetsOutput, lastPage bool) (shouldContinue bool)) error { + c.calls["ListResourceRecordSetsPages"]++ + return c.wrapped.ListResourceRecordSetsPages(input, fn) +} + +func (c *Route53APICounter) ChangeResourceRecordSets(input *route53.ChangeResourceRecordSetsInput) (*route53.ChangeResourceRecordSetsOutput, error) { + c.calls["ChangeResourceRecordSets"]++ + return c.wrapped.ChangeResourceRecordSets(input) +} + +func (c *Route53APICounter) CreateHostedZone(input *route53.CreateHostedZoneInput) (*route53.CreateHostedZoneOutput, error) { + c.calls["CreateHostedZone"]++ + return c.wrapped.CreateHostedZone(input) +} + +func (c *Route53APICounter) ListHostedZonesPages(input *route53.ListHostedZonesInput, fn func(resp *route53.ListHostedZonesOutput, lastPage bool) (shouldContinue bool)) error { + c.calls["ListHostedZonesPages"]++ + return c.wrapped.ListHostedZonesPages(input, fn) +} + +func (c *Route53APICounter) ListTagsForResource(input *route53.ListTagsForResourceInput) (*route53.ListTagsForResourceOutput, error) { + c.calls["ListTagsForResource"]++ + return c.wrapped.ListTagsForResource(input) +} + // Route53 stores wildcards escaped: http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DomainNameFormat.html?shortFooter=true#domain-name-format-asterisk func wildcardEscape(s string) string { if strings.Contains(s, "*") { @@ -426,8 +463,13 @@ func TestAWSApplyChanges(t *testing.T) { Delete: deleteRecords, } + counter := NewRoute53APICounter(provider.client) + provider.client = counter require.NoError(t, provider.ApplyChanges(changes)) + assert.Equal(t, 1, counter.calls["ListHostedZonesPages"]) + assert.Equal(t, 3, counter.calls["ListResourceRecordSetsPages"]) + records, err := provider.Records() require.NoError(t, err) @@ -619,10 +661,12 @@ func TestAWSsubmitChanges(t *testing.T) { } } + zones, _ := provider.Zones() + records, _ := provider.Records() cs := make([]*route53.Change, 0, len(endpoints)) - cs = append(cs, provider.newChanges(route53.ChangeActionCreate, endpoints)...) + cs = append(cs, provider.newChanges(route53.ChangeActionCreate, endpoints, records, zones)...) - require.NoError(t, provider.submitChanges(cs)) + require.NoError(t, provider.submitChanges(cs, zones)) records, err := provider.Records() require.NoError(t, err) @@ -634,10 +678,15 @@ func TestAWSsubmitChangesError(t *testing.T) { provider, clientStub := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneIDFilter([]string{}), NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, []*endpoint.Endpoint{}) clientStub.MockMethod("ChangeResourceRecordSets", mock.Anything).Return(nil, fmt.Errorf("Mock route53 failure")) - ep := endpoint.NewEndpointWithTTL("fail.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.0.0.1") - cs := provider.newChanges(route53.ChangeActionCreate, []*endpoint.Endpoint{ep}) + zones, err := provider.Zones() + require.NoError(t, err) + records, err := provider.Records() + require.NoError(t, err) - require.Error(t, provider.submitChanges(cs)) + ep := endpoint.NewEndpointWithTTL("fail.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.0.0.1") + cs := provider.newChanges(route53.ChangeActionCreate, []*endpoint.Endpoint{ep}, records, zones) + + require.Error(t, provider.submitChanges(cs, zones)) } func TestAWSBatchChangeSet(t *testing.T) { From 0b46e8fcef8ecf5c2c17d07cf2ae2c720b68a6ab Mon Sep 17 00:00:00 2001 From: njuettner Date: Fri, 12 Apr 2019 14:23:59 +0200 Subject: [PATCH 088/136] Updating README.md --- README.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 85f5d985c..85471e96c 100644 --- a/README.md +++ b/README.md @@ -122,11 +122,19 @@ Make sure you have the following prerequisites: First, get ExternalDNS: -**To install all dependencies, make sure to install [dep](https://github.com/golang/dep) first.** - ```console $ git clone https://github.com/kubernetes-incubator/external-dns.git && cd external-dns -$ dep ensure -vendor-only +``` + +**This project uses [Go modules](https://github.com/golang/go/wiki/Modules) as +introduced in Go 1.11 therefore you need Go >=1.11 installed in order to build.** +If using Go 1.11 you also need to [activate Module +support](https://github.com/golang/go/wiki/Modules#installing-and-activating-module-support). + +Assuming Go has been setup with module support it can be built simply by running: + +```console +$ export GO111MODULE=on # needed if the project is checked out in your $GOPATH. $ make ``` From f4f97087910a9ae4280eee6f1b6c36a9ce88fd46 Mon Sep 17 00:00:00 2001 From: Michael Fraenkel Date: Fri, 12 Apr 2019 22:44:58 +0000 Subject: [PATCH 089/136] Set a default TTL for AWS Alias records --- provider/aws.go | 4 ++++ provider/aws_test.go | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/provider/aws.go b/provider/aws.go index ab9119bc4..37d758768 100644 --- a/provider/aws.go +++ b/provider/aws.go @@ -263,6 +263,10 @@ func (p *AWSProvider) records(zones map[string]*route53.HostedZone) ([]*endpoint } if r.AliasTarget != nil { + // Alias records don't have TTLs so provide the default to match the TXT generation + if ttl == 0 { + ttl = recordTTL + } ep := endpoint. NewEndpointWithTTL(wildcardUnescape(aws.StringValue(r.Name)), endpoint.RecordTypeCNAME, ttl, aws.StringValue(r.AliasTarget.DNSName)). WithProviderSpecific(providerSpecificEvaluateTargetHealth, fmt.Sprintf("%t", aws.BoolValue(r.AliasTarget.EvaluateTargetHealth))) diff --git a/provider/aws_test.go b/provider/aws_test.go index 4e11ab9f1..14cfcfba3 100644 --- a/provider/aws_test.go +++ b/provider/aws_test.go @@ -322,9 +322,9 @@ func TestAWSRecords(t *testing.T) { 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-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false"), + endpoint.NewEndpointWithTTL("*.wildcard-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false"), + endpoint.NewEndpointWithTTL("list-test-alias-evaluate.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "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"), }) From 8f0b1c9c52981124790be0bb98915ef46bc11c13 Mon Sep 17 00:00:00 2001 From: Joseph Date: Sun, 14 Apr 2019 11:35:38 +0800 Subject: [PATCH 090/136] fix wrong arg 'alibaba-cloud-zone' -> 'alibaba-cloud-zone-type' --- docs/tutorials/alibabacloud.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/tutorials/alibabacloud.md b/docs/tutorials/alibabacloud.md index 2fc509579..a3d9f9427 100644 --- a/docs/tutorials/alibabacloud.md +++ b/docs/tutorials/alibabacloud.md @@ -117,7 +117,7 @@ spec: - --domain-filter=external-dns-test.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones - --provider=alibabacloud - --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization - - --alibaba-cloud-zone=public # only look at public hosted zones (valid values are public, private or no value for both) + - --alibaba-cloud-zone-type=public # only look at public hosted zones (valid values are public, private or no value for both) - --registry=txt - --txt-owner-id=my-identifier volumeMounts: @@ -210,9 +210,9 @@ spec: This list is not the full list, but a few arguments that where chosen. -### alibabacloud-zone-type +### alibaba-cloud-zone-type -`alibabacloud-zone-type` allows filtering for private and public zones +`alibaba-cloud-zone-type` allows filtering for private and public zones * If value is `public`, it will sync with records in Alibaba Cloud DNS Service * If value is `private`, it will sync with records in Alibaba Cloud Private Zone Service From 24c0d0eef3b69a332fa774e6bf16dafba30264b8 Mon Sep 17 00:00:00 2001 From: xianlubird Date: Sun, 14 Apr 2019 19:24:57 +0800 Subject: [PATCH 091/136] Enable sts token --- docs/tutorials/alibabacloud.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/tutorials/alibabacloud.md b/docs/tutorials/alibabacloud.md index a3d9f9427..3e5a9786e 100644 --- a/docs/tutorials/alibabacloud.md +++ b/docs/tutorials/alibabacloud.md @@ -194,6 +194,7 @@ spec: - --alibaba-cloud-zone-type=public # only look at public hosted zones (valid values are public, private or no value for both) - --registry=txt - --txt-owner-id=my-identifier + - --alibaba-cloud-config-file= # enable sts token volumeMounts: - mountPath: /usr/share/zoneinfo name: hostpath From 261765fb71aab24870feb10ae690cdc242d0540c Mon Sep 17 00:00:00 2001 From: xianlubird Date: Sun, 14 Apr 2019 19:33:02 +0800 Subject: [PATCH 092/136] Add Chinese docs link --- docs/tutorials/alibabacloud.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/tutorials/alibabacloud.md b/docs/tutorials/alibabacloud.md index 3e5a9786e..a58b8d425 100644 --- a/docs/tutorials/alibabacloud.md +++ b/docs/tutorials/alibabacloud.md @@ -380,3 +380,5 @@ Give ExternalDNS some time to clean up the DNS records for you. Then delete the ```console $ aliyun alidns DeleteDomain --DomainName external-dns-test.com ``` + +For more info about Alibaba Cloud external dns, please refer this docs [link](https://yq.aliyun.com/articles/633412) From 7485ef6f682c208290ed1e078c07d37e4d36f23c Mon Sep 17 00:00:00 2001 From: Gordan Grasarevic Date: Sun, 14 Apr 2019 16:36:46 +0100 Subject: [PATCH 093/136] Remove disable-addon argument from gke + nginx tutorial --- docs/tutorials/nginx-ingress.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/tutorials/nginx-ingress.md b/docs/tutorials/nginx-ingress.md index 3c495be64..d180e2a0d 100644 --- a/docs/tutorials/nginx-ingress.md +++ b/docs/tutorials/nginx-ingress.md @@ -15,8 +15,7 @@ Create a GKE cluster without using the default ingress controller. ```console $ gcloud container clusters create "external-dns" \ --num-nodes 1 \ - --scopes "https://www.googleapis.com/auth/ndev.clouddns.readwrite" \ - --disable-addons=HttpLoadBalancing + --scopes "https://www.googleapis.com/auth/ndev.clouddns.readwrite" ``` Create a DNS zone which will contain the managed DNS records. From 4ea79d1144014834f409ed2e1c9ce29595a3ca7a Mon Sep 17 00:00:00 2001 From: xianlubird Date: Tue, 16 Apr 2019 09:58:20 +0800 Subject: [PATCH 094/136] Update the docs link --- docs/tutorials/alibabacloud.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/alibabacloud.md b/docs/tutorials/alibabacloud.md index a58b8d425..5ecd087ab 100644 --- a/docs/tutorials/alibabacloud.md +++ b/docs/tutorials/alibabacloud.md @@ -381,4 +381,4 @@ Give ExternalDNS some time to clean up the DNS records for you. Then delete the $ aliyun alidns DeleteDomain --DomainName external-dns-test.com ``` -For more info about Alibaba Cloud external dns, please refer this docs [link](https://yq.aliyun.com/articles/633412) +For more info about Alibaba Cloud external dns, please refer this [docs](https://yq.aliyun.com/articles/633412) From 0d608fd0188b740b3985ba75c3029b080859daa0 Mon Sep 17 00:00:00 2001 From: Yaroslav Verbin Date: Tue, 16 Apr 2019 19:13:26 +0300 Subject: [PATCH 095/136] create missing DNS entry. fix https://github.com/kubernetes-incubator/external-dns/issues/964 --- source/service.go | 38 ++++++++++++++++++++------------------ source/service_test.go | 6 ++++++ 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/source/service.go b/source/service.go index 7ae70997b..0b2df1a64 100644 --- a/source/service.go +++ b/source/service.go @@ -238,26 +238,28 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri targetsByHeadlessDomain := make(map[string][]string) for _, v := range pods { - headlessDomain := hostname - if v.Spec.Hostname != "" { - headlessDomain = v.Spec.Hostname + "." + headlessDomain - } + headlessDomains := []string{hostname} - if sc.publishHostIP == true { - log.Debugf("Generating matching endpoint %s with HostIP %s", headlessDomain, v.Status.HostIP) - // To reduce traffice on the DNS API only add record for running Pods. Good Idea? - if v.Status.Phase == v1.PodRunning { - targetsByHeadlessDomain[headlessDomain] = append(targetsByHeadlessDomain[headlessDomain], v.Status.HostIP) + if v.Spec.Hostname != "" { + headlessDomains = append(headlessDomains, v.Spec.Hostname+"."+hostname) + } + for _, headlessDomain := range headlessDomains { + if sc.publishHostIP == true { + log.Debugf("Generating matching endpoint %s with HostIP %s", headlessDomain, v.Status.HostIP) + // To reduce traffice on the DNS API only add record for running Pods. Good Idea? + if v.Status.Phase == v1.PodRunning { + targetsByHeadlessDomain[headlessDomain] = append(targetsByHeadlessDomain[headlessDomain], v.Status.HostIP) + } else { + log.Debugf("Pod %s is not in running phase", v.Spec.Hostname) + } } else { - log.Debugf("Pod %s is not in running phase", v.Spec.Hostname) - } - } else { - log.Debugf("Generating matching endpoint %s with PodIP %s", headlessDomain, v.Status.PodIP) - // To reduce traffice on the DNS API only add record for running Pods. Good Idea? - if v.Status.Phase == v1.PodRunning { - targetsByHeadlessDomain[headlessDomain] = append(targetsByHeadlessDomain[headlessDomain], v.Status.PodIP) - } else { - log.Debugf("Pod %s is not in running phase", v.Spec.Hostname) + log.Debugf("Generating matching endpoint %s with PodIP %s", headlessDomain, v.Status.PodIP) + // To reduce traffice on the DNS API only add record for running Pods. Good Idea? + if v.Status.Phase == v1.PodRunning { + targetsByHeadlessDomain[headlessDomain] = append(targetsByHeadlessDomain[headlessDomain], v.Status.PodIP) + } else { + log.Debugf("Pod %s is not in running phase", v.Spec.Hostname) + } } } diff --git a/source/service_test.go b/source/service_test.go index 650dda759..909d5456a 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -1562,6 +1562,7 @@ func TestHeadlessServices(t *testing.T) { []*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"}}, }, false, }, @@ -1616,6 +1617,7 @@ func TestHeadlessServices(t *testing.T) { []*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)}, }, false, }, @@ -1643,6 +1645,7 @@ func TestHeadlessServices(t *testing.T) { []v1.PodPhase{v1.PodRunning, v1.PodFailed}, []*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"}}, }, false, }, @@ -1793,6 +1796,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { []*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"}}, }, false, }, @@ -1847,6 +1851,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { []*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)}, }, false, }, @@ -1874,6 +1879,7 @@ func TestHeadlessServicesHostIP(t *testing.T) { []v1.PodPhase{v1.PodRunning, v1.PodFailed}, []*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"}}, }, false, }, From 31ecea14fb1ae9e8afa5d1da3f14eebe6ce2a005 Mon Sep 17 00:00:00 2001 From: Richard Lee Date: Tue, 16 Apr 2019 19:56:28 -0700 Subject: [PATCH 096/136] Fixing what seems an obvious omission of /github.com/ dir. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f7166647d..d11f684cd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,7 +25,7 @@ RUN make build FROM registry.opensource.zalan.do/stups/alpine:latest LABEL maintainer="Team Teapot @ Zalando SE " -COPY --from=builder /kubernetes-incubator/external-dns/build/external-dns /bin/external-dns +COPY --from=builder /github.com/kubernetes-incubator/external-dns/build/external-dns /bin/external-dns USER nobody From 90407b3927ba0f455846951893672fa66a2c2801 Mon Sep 17 00:00:00 2001 From: Michael FIG Date: Wed, 17 Apr 2019 10:47:43 -0600 Subject: [PATCH 097/136] Azure: Support multiple A targets. --- Dockerfile | 2 +- provider/azure.go | 38 +++++++++++-------- provider/azure_test.go | 83 ++++++++++++++++++++++++++++++++---------- 3 files changed, 87 insertions(+), 36 deletions(-) diff --git a/Dockerfile b/Dockerfile index f7166647d..d11f684cd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,7 +25,7 @@ RUN make build FROM registry.opensource.zalan.do/stups/alpine:latest LABEL maintainer="Team Teapot @ Zalando SE " -COPY --from=builder /kubernetes-incubator/external-dns/build/external-dns /bin/external-dns +COPY --from=builder /github.com/kubernetes-incubator/external-dns/build/external-dns /bin/external-dns USER nobody diff --git a/provider/azure.go b/provider/azure.go index 82077139e..900262048 100644 --- a/provider/azure.go +++ b/provider/azure.go @@ -179,9 +179,9 @@ func (p *AzureProvider) Records() (endpoints []*endpoint.Endpoint, _ error) { return true } name := formatAzureDNSName(*recordSet.Name, *zone.Name) - target := extractAzureTarget(&recordSet) - if target == "" { - log.Errorf("Failed to extract target for '%s' with type '%s'.", name, recordType) + targets := extractAzureTargets(&recordSet) + if len(targets) == 0 { + log.Errorf("Failed to extract targets for '%s' with type '%s'.", name, recordType) return true } var ttl endpoint.TTL @@ -189,7 +189,7 @@ func (p *AzureProvider) Records() (endpoints []*endpoint.Endpoint, _ error) { ttl = endpoint.TTL(*recordSet.TTL) } - ep := endpoint.NewEndpointWithTTL(name, recordType, endpoint.TTL(ttl), target) + ep := endpoint.NewEndpointWithTTL(name, recordType, endpoint.TTL(ttl), targets...) log.Debugf( "Found %s record for '%s' with target '%s'.", ep.RecordType, @@ -414,14 +414,16 @@ func (p *AzureProvider) newRecordSet(endpoint *endpoint.Endpoint) (dns.RecordSet } switch dns.RecordType(endpoint.RecordType) { case dns.A: + aRecords := make([]dns.ARecord, len(endpoint.Targets)) + for i, target := range endpoint.Targets { + aRecords[i] = dns.ARecord{ + Ipv4Address: to.StringPtr(target), + } + } return dns.RecordSet{ RecordSetProperties: &dns.RecordSetProperties{ - TTL: to.Int64Ptr(ttl), - ARecords: &[]dns.ARecord{ - { - Ipv4Address: to.StringPtr(endpoint.Targets[0]), - }, - }, + TTL: to.Int64Ptr(ttl), + ARecords: &aRecords, }, }, nil case dns.CNAME: @@ -459,22 +461,26 @@ func formatAzureDNSName(recordName, zoneName string) string { } // Helper function (shared with text code) -func extractAzureTarget(recordSet *dns.RecordSet) string { +func extractAzureTargets(recordSet *dns.RecordSet) []string { properties := recordSet.RecordSetProperties if properties == nil { - return "" + return []string{} } // Check for A records aRecords := properties.ARecords if aRecords != nil && len(*aRecords) > 0 && (*aRecords)[0].Ipv4Address != nil { - return *(*aRecords)[0].Ipv4Address + targets := make([]string, len(*aRecords)) + for i, aRecord := range *aRecords { + targets[i] = *aRecord.Ipv4Address + } + return targets } // Check for CNAME records cnameRecord := properties.CnameRecord if cnameRecord != nil && cnameRecord.Cname != nil { - return *cnameRecord.Cname + return []string{*cnameRecord.Cname} } // Check for TXT records @@ -482,8 +488,8 @@ func extractAzureTarget(recordSet *dns.RecordSet) string { if txtRecords != nil && len(*txtRecords) > 0 && (*txtRecords)[0].Value != nil { values := (*txtRecords)[0].Value if values != nil && len(*values) > 0 { - return (*values)[0] + return []string{(*values)[0]} } } - return "" + return []string{} } diff --git a/provider/azure_test.go b/provider/azure_test.go index e9da5c286..37e5fb138 100644 --- a/provider/azure_test.go +++ b/provider/azure_test.go @@ -57,47 +57,52 @@ func (client *mockZonesClient) ListByResourceGroupNextResults(lastResults dns.Zo return dns.ZoneListResult{}, nil } -func aRecordSetPropertiesGetter(value string, ttl int64) *dns.RecordSetProperties { +func aRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetProperties { + aRecords := make([]dns.ARecord, len(values)) + for i, value := range values { + aRecords[i] = dns.ARecord{ + Ipv4Address: to.StringPtr(value), + } + } return &dns.RecordSetProperties{ - TTL: to.Int64Ptr(ttl), - ARecords: &[]dns.ARecord{ - { - Ipv4Address: to.StringPtr(value), - }, - }, + TTL: to.Int64Ptr(ttl), + ARecords: &aRecords, } } -func cNameRecordSetPropertiesGetter(value string, ttl int64) *dns.RecordSetProperties { +func cNameRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetProperties { return &dns.RecordSetProperties{ TTL: to.Int64Ptr(ttl), CnameRecord: &dns.CnameRecord{ - Cname: to.StringPtr(value), + Cname: to.StringPtr(values[0]), }, } } -func txtRecordSetPropertiesGetter(value string, ttl int64) *dns.RecordSetProperties { +func txtRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetProperties { return &dns.RecordSetProperties{ TTL: to.Int64Ptr(ttl), TxtRecords: &[]dns.TxtRecord{ { - Value: &[]string{value}, + Value: &[]string{values[0]}, }, }, } } -func othersRecordSetPropertiesGetter(value string, ttl int64) *dns.RecordSetProperties { +func othersRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetProperties { return &dns.RecordSetProperties{ TTL: to.Int64Ptr(ttl), } } -func createMockRecordSet(name, recordType, value string) dns.RecordSet { - return createMockRecordSetWithTTL(name, recordType, value, 0) +func createMockRecordSet(name, recordType string, values ...string) dns.RecordSet { + return createMockRecordSetMultiWithTTL(name, recordType, 0, values...) } func createMockRecordSetWithTTL(name, recordType, value string, ttl int64) dns.RecordSet { - var getterFunc func(value string, ttl int64) *dns.RecordSetProperties + return createMockRecordSetMultiWithTTL(name, recordType, ttl, value) +} +func createMockRecordSetMultiWithTTL(name, recordType string, ttl int64, values ...string) dns.RecordSet { + var getterFunc func(values []string, ttl int64) *dns.RecordSetProperties switch recordType { case endpoint.RecordTypeA: @@ -112,7 +117,7 @@ func createMockRecordSetWithTTL(name, recordType, value string, ttl int64) dns.R return dns.RecordSet{ Name: to.StringPtr(name), Type: to.StringPtr("Microsoft.Network/dnszones/" + recordType), - RecordSetProperties: getterFunc(value, ttl), + RecordSetProperties: getterFunc(values, ttl), } } @@ -148,7 +153,7 @@ func (client *mockRecordsClient) CreateOrUpdate(resourceGroupName string, zoneNa formatAzureDNSName(relativeRecordSetName, zoneName), string(recordType), ttl, - extractAzureTarget(¶meters), + extractAzureTargets(¶meters)..., ), ) return parameters, nil @@ -209,6 +214,46 @@ func TestAzureRecord(t *testing.T) { } +func TestAzureMultiRecord(t *testing.T) { + zonesClient := mockZonesClient{ + mockZoneListResult: &dns.ZoneListResult{ + Value: &[]dns.Zone{ + createMockZone("example.com", "/dnszones/example.com"), + }, + }, + } + + recordsClient := mockRecordsClient{ + mockRecordSet: &[]dns.RecordSet{ + createMockRecordSet("@", "NS", "ns1-03.azure-dns.com."), + createMockRecordSet("@", "SOA", "Email: azuredns-hostmaster.microsoft.com"), + createMockRecordSet("@", endpoint.RecordTypeA, "123.123.123.122", "234.234.234.233"), + createMockRecordSet("@", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"), + 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), + }, + } + + provider := newAzureProvider(NewDomainFilter([]string{"example.com"}), NewZoneIDFilter([]string{""}), true, "k8s", &zonesClient, &recordsClient) + + actual, err := provider.Records() + + if err != nil { + t.Fatal(err) + } + expected := []*endpoint.Endpoint{ + endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "123.123.123.122", "234.234.234.233"), + endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"), + 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"), + } + + validateAzureEndpoints(t, actual, expected) + +} + func TestAzureApplyChanges(t *testing.T) { recordsClient := mockRecordsClient{} @@ -224,7 +269,7 @@ func TestAzureApplyChanges(t *testing.T) { validateAzureEndpoints(t, recordsClient.updatedEndpoints, []*endpoint.Endpoint{ endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4"), endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"), - endpoint.NewEndpointWithTTL("foo.example.com", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4"), + endpoint.NewEndpointWithTTL("foo.example.com", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4", "1.2.3.5"), endpoint.NewEndpointWithTTL("foo.example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"), endpoint.NewEndpointWithTTL("bar.example.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "other.com"), endpoint.NewEndpointWithTTL("bar.example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"), @@ -265,7 +310,7 @@ func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordsClie createRecords := []*endpoint.Endpoint{ endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "1.2.3.4"), endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "tag"), - endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "1.2.3.4"), + endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "1.2.3.5", "1.2.3.4"), endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeTXT, "tag"), endpoint.NewEndpoint("bar.example.com", endpoint.RecordTypeCNAME, "other.com"), endpoint.NewEndpoint("bar.example.com", endpoint.RecordTypeTXT, "tag"), From 1798dbdfb5e7649ad45c21f1959467cb037e0456 Mon Sep 17 00:00:00 2001 From: Yaroslav Verbin Date: Thu, 18 Apr 2019 11:15:01 +0300 Subject: [PATCH 098/136] proper field formatting --- source/service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/service.go b/source/service.go index 0b2df1a64..606716e65 100644 --- a/source/service.go +++ b/source/service.go @@ -241,7 +241,7 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri headlessDomains := []string{hostname} if v.Spec.Hostname != "" { - headlessDomains = append(headlessDomains, v.Spec.Hostname+"."+hostname) + headlessDomains = append(headlessDomains, fmt.Sprintf("%s.%s", v.Spec.Hostname, hostname)) } for _, headlessDomain := range headlessDomains { if sc.publishHostIP == true { From 189e8f9f82ee5a9220744af564257d8036f13313 Mon Sep 17 00:00:00 2001 From: njuettner Date: Thu, 18 Apr 2019 10:27:51 +0200 Subject: [PATCH 099/136] release v0.5.13 Signed-off-by: njuettner --- CHANGELOG.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63c0371ce..d125dc359 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ +## v0.5.13 - 2019-04-18 + + - Azure: Support multiple A targets (#987) @michaelfig + - Core: Fixing what seems an obvious omission of /github.com/ dir in Dockerfile (#985) @llamahunter + - Docs: GKE tutorial remove disable-addon argument (#978) @ggordan + - Docs: Alibaba Cloud config file missing by enable sts token (#977) @xianlubird + - Docs: Alibaba Cloud fix wrong arg in manifest (#976) @iamzhout + - AWS: Set a default TTL for Alias records (#975) @fraenkel + - Cloudflare: Add support for multiple target addresses (#970) @nta + - AWS: Adding China ELB endpoints and hosted zone id's (#968) @jfillo + - AWS: Streamline ApplyChanges (#966) @fraenkel + - Core: Switch to go modules (#960) @njuettner + - Docs: AWS how to check if your cluster has a RBAC (#959) @confiq + - Docs: AWS remove superfluous trailing period from hostname (#952) @hobti01 + - Core: Add generic logic to remove secrets from logs (#951) @dsbrng25b + - RFC2136: Remove unnecessary parameter (#948) @ChristianMoesl + - Infoblox: Reduce verbosity of logs (#945) @dsbrng25b + ## v0.5.12 - 2019-03-26 - + - Bumping istio to 1.1.0 (#942) @venezia - Docs: Added stability matrix and minor improvements to README (#938) @Raffo - Docs: Added a reference to a blogpost which uses ExternalDNS in a CI/CD setup (#928) @vanhumbeecka From 0c68309f806448c94ecfbdd866275e06159da67a Mon Sep 17 00:00:00 2001 From: Josh Harshman Date: Fri, 19 Apr 2019 09:49:56 -0600 Subject: [PATCH 100/136] scratch - add Dockerfile.scratch --- Dockerfile.scratch | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Dockerfile.scratch diff --git a/Dockerfile.scratch b/Dockerfile.scratch new file mode 100644 index 000000000..dc75b95d3 --- /dev/null +++ b/Dockerfile.scratch @@ -0,0 +1,3 @@ +FROM scratch +COPY ./build/external-dns /bin/external-dns +ENTRYPOINT ["/bin/external-dns"] From f9248ee585ce1aa8af4e97cd0c2faec5140be6fc Mon Sep 17 00:00:00 2001 From: Josh Harshman Date: Fri, 19 Apr 2019 10:06:13 -0600 Subject: [PATCH 101/136] scratch - pin builder to specific image --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d11f684cd..f987cf7d4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ # limitations under the License. # builder image -FROM golang as builder +FROM golang:1.12.4 as builder WORKDIR /github.com/kubernetes-incubator/external-dns COPY . . From 525517c8cbf0c0d3d5f4d9064d42614146a31abf Mon Sep 17 00:00:00 2001 From: Josh Harshman Date: Fri, 19 Apr 2019 10:36:14 -0600 Subject: [PATCH 102/136] scratch - add certs to scratch fs --- Dockerfile.scratch | 15 +++++++++++++-- Makefile | 5 ++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/Dockerfile.scratch b/Dockerfile.scratch index dc75b95d3..257525b4d 100644 --- a/Dockerfile.scratch +++ b/Dockerfile.scratch @@ -1,3 +1,14 @@ +FROM golang:1.12.4 as builder +WORKDIR /external-dns +COPY . . +RUN make build + +FROM alpine:latest as pkgs +RUN apk --update add ca-certificates + FROM scratch -COPY ./build/external-dns /bin/external-dns -ENTRYPOINT ["/bin/external-dns"] +COPY --from=pkgs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +COPY --from=pkgs /etc/passwd /etc/passwd +COPY --from=builder /external-dns/build/external-dns /external-dns +USER nobody +ENTRYPOINT ["./external-dns"] diff --git a/Makefile b/Makefile index 8ba62f20f..8670e38c4 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ test: go test -v -race $(shell go list ./... | grep -v /vendor/) # The build targets allow to build the binary and docker image -.PHONY: build build.docker +.PHONY: build build.docker build.scratch BINARY ?= external-dns SOURCES = $(shell find . -name '*.go') @@ -61,5 +61,8 @@ build.push: build.docker build.docker: docker build --rm --tag "$(IMAGE):$(VERSION)" . +build.scratch: + docker build --rm --tag "$(IMAGE):$(VERSION)" -f Dockerfile.scratch . + clean: @rm -rf build From bff09c20c9512dd70b1b4048c717d87eb19b124b Mon Sep 17 00:00:00 2001 From: mburtless Date: Wed, 17 Oct 2018 21:55:52 -0400 Subject: [PATCH 103/136] Initial Skeleton From NS1 Provider This needs unit tests and a full integration test. Pushing this as an initial checkpoint. --- main.go | 8 ++ provider/ns1.go | 241 +++++++++++++++++++++++++++++++++++++++++++ provider/ns1_test.go | 1 + 3 files changed, 250 insertions(+) create mode 100644 provider/ns1.go create mode 100644 provider/ns1_test.go diff --git a/main.go b/main.go index 5e2354cd4..bebb7444c 100644 --- a/main.go +++ b/main.go @@ -201,6 +201,14 @@ func main() { } case "rfc2136": p, err = provider.NewRfc2136Provider(cfg.RFC2136Host, cfg.RFC2136Port, cfg.RFC2136Zone, cfg.RFC2136Insecure, cfg.RFC2136TSIGKeyName, cfg.RFC2136TSIGSecret, cfg.RFC2136TSIGSecretAlg, cfg.RFC2136TAXFR, domainFilter, cfg.DryRun, nil) + case "ns1": + p, err = provider.NewNS1Provider( + provider.NS1Config{ + DomainFilter: domainFilter, + ZoneIDFilter: zoneIDFilter, + DryRun: cfg.DryRun, + }, + ) default: log.Fatalf("unknown dns provider: %s", cfg.Provider) } diff --git a/provider/ns1.go b/provider/ns1.go new file mode 100644 index 000000000..3f20e1247 --- /dev/null +++ b/provider/ns1.go @@ -0,0 +1,241 @@ +package provider + +import ( + "fmt" + "net/http" + "os" + "strings" + + log "github.com/sirupsen/logrus" + + api "gopkg.in/ns1/ns1-go.v2/rest" + "gopkg.in/ns1/ns1-go.v2/rest/model/dns" + + "github.com/kubernetes-incubator/external-dns/endpoint" + "github.com/kubernetes-incubator/external-dns/plan" +) + +const ( + // ns1Create is a ChangeAction enum value + ns1Create = "CREATE" + // ns1Delete is a ChangeAction enum value + ns1Delete = "DELETE" + // ns1Update is a ChangeAction enum value + ns1Update = "UPDATE" + // ns1DefaultTTL is the default ttl for ttls that are not set + ns1DefaultTTL = 10 +) + +// NS1Config passes cli args to the NS1Provider +type NS1Config struct { + DomainFilter DomainFilter + ZoneIDFilter ZoneIDFilter + DryRun bool +} + +// NS1Provider is the NS1 provider +type NS1Provider struct { + client *api.Client + domainFilter DomainFilter + zoneIDFilter ZoneIDFilter + dryRun bool +} + +// NewNS1Provider creates a new NS1 Provider +func NewNS1Provider(config NS1Config) (*NS1Provider, error) { + return newNS1ProviderWithHTTPClient(config, http.DefaultClient) +} + +func newNS1ProviderWithHTTPClient(config NS1Config, client *http.Client) (*NS1Provider, error) { + token, ok := os.LookupEnv("NS1_APIKEY") + if !ok { + return nil, fmt.Errorf("NS1_APIKEY environment variable is not set") + } + + apiClient := api.NewClient(client, api.SetAPIKey(token)) + + provider := &NS1Provider{ + client: apiClient, + domainFilter: config.DomainFilter, + zoneIDFilter: config.ZoneIDFilter, + } + return provider, nil +} + +func (p *NS1Provider) matchEither(id string) bool { + return p.domainFilter.Match(id) || p.zoneIDFilter.Match(id) +} + +// Records returns the endpoints this provider knows about +func (p *NS1Provider) Records() ([]*endpoint.Endpoint, error) { + zones, err := p.zonesFiltered() + if err != nil { + return nil, err + } + + var endpoints []*endpoint.Endpoint + + for _, zone := range zones { + + // TODO handle Header Codes + zoneData, _, err := p.client.Zones.Get(zone.String()) + if err != nil { + return nil, err + } + + for _, record := range zoneData.Records { + if supportedRecordType(record.Type) { + name := fmt.Sprintf("%s.%s", record.Domain, zoneData.Zone) + endpoints = append(endpoints, endpoint.NewEndpointWithTTL( + name, + record.Type, + endpoint.TTL(record.TTL), + record.ShortAns..., + ), + ) + } + } + } + + return endpoints, nil +} + +func ns1BuildRecord(zoneName string, change *ns1Change) *dns.Record { + record := dns.NewRecord(zoneName, change.Endpoint.DNSName, change.Endpoint.RecordType) + for _, v := range change.Endpoint.Targets { + record.AddAnswer(dns.NewAnswer(strings.Split(v, " "))) + } + // set detault ttl + var ttl = ns1DefaultTTL + if change.Endpoint.RecordTTL.IsConfigured() { + ttl = int(change.Endpoint.RecordTTL) + } + record.TTL = ttl + + return record +} + +func (p *NS1Provider) ns1SubmitChanges(changes []*ns1Change) error { + // return early if there is nothing to change + if len(changes) == 0 { + return nil + } + + zones, err := p.zonesFiltered() + if err != nil { + return err + } + + // separate into per-zone change sets to be passed to the API. + changesByZone := ns1ChangesByZone(zones, changes) + for zoneName, changes := range changesByZone { + for _, change := range changes { + record := ns1BuildRecord(zoneName, change) + logFields := log.Fields{ + "record": record.Domain, + "type": record.Type, + "ttl": record.TTL, + "action": change.Action, + "zone": zoneName, + } + + log.WithFields(logFields).Info("Changing record.") + + if p.dryRun { + continue + } + + switch change.Action { + case ns1Create: + _, err := p.client.Records.Create(record) + if err != nil { + return err + } + case ns1Delete: + _, err := p.client.Records.Delete(zoneName, record.Domain, record.Type) + if err != nil { + return err + } + case ns1Update: + _, err := p.client.Records.Update(record) + if err != nil { + return err + } + } + } + } + return nil +} + +// Zones returns the list of hosted zones. +func (p *NS1Provider) zonesFiltered() ([]*dns.Zone, error) { + // TODO handle Header Codes + zones, _, err := p.client.Zones.List() + if err != nil { + return nil, err + } + + toReturn := []*dns.Zone{} + + for _, z := range zones { + if !p.matchEither(z.Zone) && !p.matchEither(z.ID) { + continue + } + toReturn = append(toReturn, z) + } + + return toReturn, nil +} + +// ns1Change differentiates between ChangActions +type ns1Change struct { + Action string + Endpoint *endpoint.Endpoint +} + +// ApplyChanges applies a given set of changes in a given zone. +func (p *NS1Provider) ApplyChanges(changes *plan.Changes) error { + combinedChanges := make([]*ns1Change, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete)) + + combinedChanges = append(combinedChanges, newNS1Changes(ns1Create, changes.Create)...) + combinedChanges = append(combinedChanges, newNS1Changes(ns1Update, changes.UpdateNew)...) + combinedChanges = append(combinedChanges, newNS1Changes(ns1Delete, changes.Delete)...) + + return p.ns1SubmitChanges(combinedChanges) +} + +// newNS1Changes returns a collection of Changes based on the given records and action. +func newNS1Changes(action string, endpoints []*endpoint.Endpoint) []*ns1Change { + changes := make([]*ns1Change, 0, len(endpoints)) + + for _, endpoint := range endpoints { + changes = append(changes, &ns1Change{ + Action: action, + Endpoint: endpoint, + }, + ) + } + + return changes +} + +// ns1ChangesByZone separates a multi-zone change into a single change per zone. +func ns1ChangesByZone(zones []*dns.Zone, changeSets []*ns1Change) map[string][]*ns1Change { + changes := make(map[string][]*ns1Change) + zoneNameIDMapper := zoneIDName{} + for _, z := range zones { + zoneNameIDMapper.Add(z.Zone, z.Zone) + changes[z.Zone] = []*ns1Change{} + } + + for _, c := range changeSets { + zone, _ := zoneNameIDMapper.FindZone(c.Endpoint.DNSName) + if zone == "" { + log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected ", c.Endpoint.DNSName) + continue + } + changes[zone] = append(changes[zone], c) + } + + return changes +} diff --git a/provider/ns1_test.go b/provider/ns1_test.go new file mode 100644 index 000000000..4f504f668 --- /dev/null +++ b/provider/ns1_test.go @@ -0,0 +1 @@ +package provider From bd791ebdf44da4aefa58a351312983d1e0a43739 Mon Sep 17 00:00:00 2001 From: mburtless Date: Wed, 27 Mar 2019 18:18:25 -0400 Subject: [PATCH 104/136] Add ns1 option for provider flag --- 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 ad497fb87..18ba6e00e 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -253,7 +253,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("service-type-filter", "The service types to take care about (default: all, expected: ClusterIP, NodePort, LoadBalancer or ExternalName)").StringsVar(&cfg.ServiceTypeFilter) // Flags related to providers - app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, google, azure, cloudflare, rcodezero, digitalocean, dnsimple, infoblox, dyn, designate, coredns, skydns, inmemory, pdns, oci, exoscale, linode, rfc2136)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "pdns", "oci", "exoscale", "linode", "rfc2136") + app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, google, azure, cloudflare, rcodezero, digitalocean, dnsimple, infoblox, dyn, designate, coredns, skydns, inmemory, pdns, oci, exoscale, linode, rfc2136, ns1)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1") app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter) app.Flag("zone-id-filter", "Filter target zones by hosted zone id; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.ZoneIDFilter) app.Flag("google-project", "When using the Google provider, current project is auto-detected, when running on GCP. Specify other project with this. Must be specified when running outside GCP.").Default(defaultConfig.GoogleProject).StringVar(&cfg.GoogleProject) From b9392049a6f6a5b6ecbc109be5e63886875b54bf Mon Sep 17 00:00:00 2001 From: mburtless Date: Wed, 27 Mar 2019 20:11:03 -0400 Subject: [PATCH 105/136] Fix construction of record name in endpoint --- provider/ns1.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/provider/ns1.go b/provider/ns1.go index 3f20e1247..b608633c2 100644 --- a/provider/ns1.go +++ b/provider/ns1.go @@ -85,9 +85,8 @@ func (p *NS1Provider) Records() ([]*endpoint.Endpoint, error) { for _, record := range zoneData.Records { if supportedRecordType(record.Type) { - name := fmt.Sprintf("%s.%s", record.Domain, zoneData.Zone) endpoints = append(endpoints, endpoint.NewEndpointWithTTL( - name, + record.Domain, record.Type, endpoint.TTL(record.TTL), record.ShortAns..., From 9acbecefad6bf9a6c3c44105b7c0d1d3e9263543 Mon Sep 17 00:00:00 2001 From: mburtless Date: Thu, 28 Mar 2019 16:36:25 -0400 Subject: [PATCH 106/136] Adds comments and fixes filtering --- provider/ns1.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/provider/ns1.go b/provider/ns1.go index b608633c2..6cfcffbb2 100644 --- a/provider/ns1.go +++ b/provider/ns1.go @@ -62,10 +62,6 @@ func newNS1ProviderWithHTTPClient(config NS1Config, client *http.Client) (*NS1Pr return provider, nil } -func (p *NS1Provider) matchEither(id string) bool { - return p.domainFilter.Match(id) || p.zoneIDFilter.Match(id) -} - // Records returns the endpoints this provider knows about func (p *NS1Provider) Records() ([]*endpoint.Endpoint, error) { zones, err := p.zonesFiltered() @@ -99,6 +95,7 @@ func (p *NS1Provider) Records() ([]*endpoint.Endpoint, error) { return endpoints, nil } +// ns1BuildRecord returns a dns.Record for a change set func ns1BuildRecord(zoneName string, change *ns1Change) *dns.Record { record := dns.NewRecord(zoneName, change.Endpoint.DNSName, change.Endpoint.RecordType) for _, v := range change.Endpoint.Targets { @@ -114,6 +111,7 @@ func ns1BuildRecord(zoneName string, change *ns1Change) *dns.Record { return record } +// ns1SubmitChanges takes an array of changes and sends them to NS1 func (p *NS1Provider) ns1SubmitChanges(changes []*ns1Change) error { // return early if there is nothing to change if len(changes) == 0 { @@ -177,16 +175,18 @@ func (p *NS1Provider) zonesFiltered() ([]*dns.Zone, error) { toReturn := []*dns.Zone{} for _, z := range zones { - if !p.matchEither(z.Zone) && !p.matchEither(z.ID) { - continue + if p.domainFilter.Match(z.Zone) && p.zoneIDFilter.Match(z.ID) { + toReturn = append(toReturn, z) + log.Debugf("Matched %s", z.Zone) + } else { + log.Debugf("Filtered %s", z.Zone) } - toReturn = append(toReturn, z) } return toReturn, nil } -// ns1Change differentiates between ChangActions +// ns1Change differentiates between ChangeActions type ns1Change struct { Action string Endpoint *endpoint.Endpoint From 23cca93afe9f06eaa446ddcc517713bc842947c8 Mon Sep 17 00:00:00 2001 From: mburtless Date: Thu, 28 Mar 2019 18:07:59 -0400 Subject: [PATCH 107/136] Wrap ns1 client with interface for easier testing --- provider/ns1.go | 48 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/provider/ns1.go b/provider/ns1.go index 6cfcffbb2..490ab0326 100644 --- a/provider/ns1.go +++ b/provider/ns1.go @@ -26,6 +26,39 @@ const ( ns1DefaultTTL = 10 ) +// NS1DomainClient interface to ease testing +type NS1DomainClient interface { + CreateRecord(r *dns.Record) (*http.Response, error) + DeleteRecord(zone string, domain string, t string) (*http.Response, error) + UpdateRecord(r *dns.Record) (*http.Response, error) + GetZone(zone string) (*dns.Zone, *http.Response, error) + ListZones() ([]*dns.Zone, *http.Response, error) +} + +type NS1DomainService struct { + service *api.Client +} + +func (n NS1DomainService) CreateRecord(r *dns.Record) (*http.Response, error) { + return n.service.Records.Create(r) +} + +func (n NS1DomainService) DeleteRecord(zone string, domain string, t string) (*http.Response, error) { + return n.service.Records.Delete(zone, domain, t) +} + +func (n NS1DomainService) UpdateRecord(r *dns.Record) (*http.Response, error) { + return n.service.Records.Update(r) +} + +func (n NS1DomainService) GetZone(zone string) (*dns.Zone, *http.Response, error) { + return n.service.Zones.Get(zone) +} + +func (n NS1DomainService) ListZones() ([]*dns.Zone, *http.Response, error) { + return n.service.Zones.List() +} + // NS1Config passes cli args to the NS1Provider type NS1Config struct { DomainFilter DomainFilter @@ -35,7 +68,7 @@ type NS1Config struct { // NS1Provider is the NS1 provider type NS1Provider struct { - client *api.Client + client NS1DomainClient domainFilter DomainFilter zoneIDFilter ZoneIDFilter dryRun bool @@ -55,7 +88,8 @@ func newNS1ProviderWithHTTPClient(config NS1Config, client *http.Client) (*NS1Pr apiClient := api.NewClient(client, api.SetAPIKey(token)) provider := &NS1Provider{ - client: apiClient, + //client: apiClient, + client: NS1DomainService{apiClient}, domainFilter: config.DomainFilter, zoneIDFilter: config.ZoneIDFilter, } @@ -74,7 +108,7 @@ func (p *NS1Provider) Records() ([]*endpoint.Endpoint, error) { for _, zone := range zones { // TODO handle Header Codes - zoneData, _, err := p.client.Zones.Get(zone.String()) + zoneData, _, err := p.client.GetZone(zone.String()) if err != nil { return nil, err } @@ -144,17 +178,17 @@ func (p *NS1Provider) ns1SubmitChanges(changes []*ns1Change) error { switch change.Action { case ns1Create: - _, err := p.client.Records.Create(record) + _, err := p.client.CreateRecord(record) if err != nil { return err } case ns1Delete: - _, err := p.client.Records.Delete(zoneName, record.Domain, record.Type) + _, err := p.client.DeleteRecord(zoneName, record.Domain, record.Type) if err != nil { return err } case ns1Update: - _, err := p.client.Records.Update(record) + _, err := p.client.UpdateRecord(record) if err != nil { return err } @@ -167,7 +201,7 @@ func (p *NS1Provider) ns1SubmitChanges(changes []*ns1Change) error { // Zones returns the list of hosted zones. func (p *NS1Provider) zonesFiltered() ([]*dns.Zone, error) { // TODO handle Header Codes - zones, _, err := p.client.Zones.List() + zones, _, err := p.client.ListZones() if err != nil { return nil, err } From 7b6155c9a662a6c41e864767e4b22a5d28f14358 Mon Sep 17 00:00:00 2001 From: mburtless Date: Mon, 1 Apr 2019 17:14:00 -0400 Subject: [PATCH 108/136] add comments --- provider/ns1.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/provider/ns1.go b/provider/ns1.go index 490ab0326..6f2c37a79 100644 --- a/provider/ns1.go +++ b/provider/ns1.go @@ -26,7 +26,7 @@ const ( ns1DefaultTTL = 10 ) -// NS1DomainClient interface to ease testing +// NS1DomainClient is a subset of the NS1 API the the provider uses, to ease testing type NS1DomainClient interface { CreateRecord(r *dns.Record) (*http.Response, error) DeleteRecord(zone string, domain string, t string) (*http.Response, error) @@ -35,26 +35,32 @@ type NS1DomainClient interface { ListZones() ([]*dns.Zone, *http.Response, error) } +// NS1DomainService wraps the API and fulfills the NS1DomainClient interface type NS1DomainService struct { service *api.Client } +// CreateRecord wraps the Create method of the API's Record service func (n NS1DomainService) CreateRecord(r *dns.Record) (*http.Response, error) { return n.service.Records.Create(r) } +// DeleteRecord wraps the Delete method of the API's Record service func (n NS1DomainService) DeleteRecord(zone string, domain string, t string) (*http.Response, error) { return n.service.Records.Delete(zone, domain, t) } +// UpdateRecord wraps the Update method of the API's Record service func (n NS1DomainService) UpdateRecord(r *dns.Record) (*http.Response, error) { return n.service.Records.Update(r) } +// GetZone wraps the Get method of the API's Zones service func (n NS1DomainService) GetZone(zone string) (*dns.Zone, *http.Response, error) { return n.service.Zones.Get(zone) } +// ListZones wraps the List method of the API's Zones service func (n NS1DomainService) ListZones() ([]*dns.Zone, *http.Response, error) { return n.service.Zones.List() } From 31201344e5b6566df132905cffcd0fe2f370aca8 Mon Sep 17 00:00:00 2001 From: mburtless Date: Mon, 1 Apr 2019 17:14:22 -0400 Subject: [PATCH 109/136] add acceptance tests for ns1 provider --- provider/ns1_test.go | 289 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) diff --git a/provider/ns1_test.go b/provider/ns1_test.go index 4f504f668..979de8e45 100644 --- a/provider/ns1_test.go +++ b/provider/ns1_test.go @@ -1 +1,290 @@ package provider + +import ( + "fmt" + "net/http" + "os" + "testing" + + "github.com/kubernetes-incubator/external-dns/endpoint" + "github.com/kubernetes-incubator/external-dns/plan" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + api "gopkg.in/ns1/ns1-go.v2/rest" + "gopkg.in/ns1/ns1-go.v2/rest/model/dns" +) + +type MockNS1DomainClient struct { + mock.Mock +} + +func (m *MockNS1DomainClient) CreateRecord(r *dns.Record) (*http.Response, error) { + return nil, nil +} + +func (m *MockNS1DomainClient) DeleteRecord(zone string, domain string, t string) (*http.Response, error) { + return nil, nil +} + +func (m *MockNS1DomainClient) UpdateRecord(r *dns.Record) (*http.Response, error) { + return nil, nil +} + +func (m *MockNS1DomainClient) GetZone(zone string) (*dns.Zone, *http.Response, error) { + r := &dns.ZoneRecord{ + Domain: "test.foo.com", + ShortAns: []string{"2.2.2.2"}, + TTL: 3600, + Type: "A", + ID: "123456789abcdefghijklmno", + } + z := &dns.Zone{ + Zone: "foo.com", + Records: []*dns.ZoneRecord{r}, + TTL: 3600, + ID: "12345678910111213141516a", + } + + if zone == "foo.com" { + return z, nil, nil + } + return nil, nil, nil +} + +func (m *MockNS1DomainClient) ListZones() ([]*dns.Zone, *http.Response, error) { + zones := []*dns.Zone{ + {Zone: "foo.com", ID: "12345678910111213141516a"}, + {Zone: "bar.com", ID: "12345678910111213141516b"}, + } + return zones, nil, nil +} + +type MockNS1GetZoneFail struct{} + +func (m *MockNS1GetZoneFail) CreateRecord(r *dns.Record) (*http.Response, error) { + return nil, nil +} + +func (m *MockNS1GetZoneFail) DeleteRecord(zone string, domain string, t string) (*http.Response, error) { + return nil, nil +} + +func (m *MockNS1GetZoneFail) UpdateRecord(r *dns.Record) (*http.Response, error) { + return nil, nil +} + +func (m *MockNS1GetZoneFail) GetZone(zone string) (*dns.Zone, *http.Response, error) { + return nil, nil, api.ErrZoneMissing +} + +func (m *MockNS1GetZoneFail) ListZones() ([]*dns.Zone, *http.Response, error) { + zones := []*dns.Zone{ + {Zone: "foo.com", ID: "12345678910111213141516a"}, + {Zone: "bar.com", ID: "12345678910111213141516b"}, + } + return zones, nil, nil +} + +type MockNS1ListZonesFail struct{} + +func (m *MockNS1ListZonesFail) CreateRecord(r *dns.Record) (*http.Response, error) { + return nil, nil +} + +func (m *MockNS1ListZonesFail) DeleteRecord(zone string, domain string, t string) (*http.Response, error) { + return nil, nil +} + +func (m *MockNS1ListZonesFail) UpdateRecord(r *dns.Record) (*http.Response, error) { + return nil, nil +} + +func (m *MockNS1ListZonesFail) GetZone(zone string) (*dns.Zone, *http.Response, error) { + return &dns.Zone{}, nil, nil +} + +func (m *MockNS1ListZonesFail) ListZones() ([]*dns.Zone, *http.Response, error) { + return nil, nil, fmt.Errorf("no zones available") +} + +func TestNS1Records(t *testing.T) { + provider := &NS1Provider{ + client: &MockNS1DomainClient{}, + domainFilter: NewDomainFilter([]string{"foo.com."}), + zoneIDFilter: NewZoneIDFilter([]string{""}), + } + records, err := provider.Records() + require.NoError(t, err) + assert.Equal(t, 1, len(records)) + + provider.client = &MockNS1GetZoneFail{} + _, err = provider.Records() + require.Error(t, err) + + provider.client = &MockNS1ListZonesFail{} + _, err = provider.Records() + require.Error(t, err) +} + +func TestNewNS1Provider(t *testing.T) { + _ = os.Setenv("NS1_APIKEY", "xxxxxxxxxxxxxxxxx") + testNS1Config := NS1Config{ + DomainFilter: NewDomainFilter([]string{"foo.com."}), + ZoneIDFilter: NewZoneIDFilter([]string{""}), + DryRun: false, + } + _, err := NewNS1Provider(testNS1Config) + require.NoError(t, err) + + _ = os.Unsetenv("NS1_APIKEY") + _, err = NewNS1Provider(testNS1Config) + require.Error(t, err) +} + +func TestNS1Zones(t *testing.T) { + provider := &NS1Provider{ + client: &MockNS1DomainClient{}, + domainFilter: NewDomainFilter([]string{"foo.com."}), + zoneIDFilter: NewZoneIDFilter([]string{""}), + } + + zones, err := provider.zonesFiltered() + require.NoError(t, err) + + validateNS1Zones(t, zones, []*dns.Zone{ + {Zone: "foo.com"}, + }) +} + +func validateNS1Zones(t *testing.T, zones []*dns.Zone, expected []*dns.Zone) { + require.Len(t, zones, len(expected)) + + for i, zone := range zones { + assert.Equal(t, expected[i].Zone, zone.Zone) + } +} + +func TestNS1BuildRecord(t *testing.T) { + change := &ns1Change{ + Action: ns1Create, + Endpoint: &endpoint.Endpoint{ + DNSName: "new", + Targets: endpoint.Targets{"target"}, + RecordType: "A", + }, + } + record := ns1BuildRecord("foo.com", change) + assert.Equal(t, "foo.com", record.Zone) + assert.Equal(t, "new.foo.com", record.Domain) + assert.Equal(t, ns1DefaultTTL, record.TTL) + + changeWithTTL := &ns1Change{ + Action: ns1Create, + Endpoint: &endpoint.Endpoint{ + DNSName: "new-b", + Targets: endpoint.Targets{"target"}, + RecordType: "A", + RecordTTL: 100, + }, + } + record = ns1BuildRecord("foo.com", changeWithTTL) + assert.Equal(t, "foo.com", record.Zone) + assert.Equal(t, "new-b.foo.com", record.Domain) + assert.Equal(t, 100, record.TTL) +} + +func TestNS1ApplyChanges(t *testing.T) { + changes := &plan.Changes{} + provider := &NS1Provider{ + client: &MockNS1DomainClient{}, + } + changes.Create = []*endpoint.Endpoint{ + {DNSName: "new.foo.com", Targets: endpoint.Targets{"target"}}, + {DNSName: "new.subdomain.bar.com", Targets: endpoint.Targets{"target"}}, + } + changes.Delete = []*endpoint.Endpoint{{DNSName: "test.foo.com", Targets: endpoint.Targets{"target"}}} + changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "test.foo.com", Targets: endpoint.Targets{"target-new"}}} + err := provider.ApplyChanges(changes) + require.NoError(t, err) + + // empty changes + changes.Create = []*endpoint.Endpoint{} + changes.Delete = []*endpoint.Endpoint{} + changes.UpdateNew = []*endpoint.Endpoint{} + err = provider.ApplyChanges(changes) + require.NoError(t, err) +} + +func TestNewNS1Changes(t *testing.T) { + endpoints := []*endpoint.Endpoint{ + { + DNSName: "testa.foo.com", + Targets: endpoint.Targets{"target-old"}, + RecordType: "A", + }, + { + DNSName: "testba.bar.com", + Targets: endpoint.Targets{"target-new"}, + RecordType: "A", + }, + } + expected := []*ns1Change{ + { + Action: "ns1Create", + Endpoint: endpoints[0], + }, + { + Action: "ns1Create", + Endpoint: endpoints[1], + }, + } + changes := newNS1Changes("ns1Create", endpoints) + require.Len(t, changes, len(expected)) + assert.Equal(t, expected, changes) +} + +func TestNewNS1ChangesByZone(t *testing.T) { + provider := &NS1Provider{ + client: &MockNS1DomainClient{}, + } + zones, _ := provider.zonesFiltered() + changeSets := []*ns1Change{ + { + Action: "ns1Create", + Endpoint: &endpoint.Endpoint{ + DNSName: "new.foo.com", + Targets: endpoint.Targets{"target"}, + RecordType: "A", + }, + }, + { + Action: "ns1Create", + Endpoint: &endpoint.Endpoint{ + DNSName: "unrelated.bar.com", + Targets: endpoint.Targets{"target"}, + RecordType: "A", + }, + }, + { + Action: "ns1Delete", + Endpoint: &endpoint.Endpoint{ + DNSName: "test.foo.com", + Targets: endpoint.Targets{"target"}, + RecordType: "A", + }, + }, + { + Action: "ns1Update", + Endpoint: &endpoint.Endpoint{ + DNSName: "test.foo.com", + Targets: endpoint.Targets{"target-new"}, + RecordType: "A", + }, + }, + } + + changes := ns1ChangesByZone(zones, changeSets) + assert.Len(t, changes["bar.com"], 1) + assert.Len(t, changes["foo.com"], 3) +} From 60666d8757bbe5d983a3a34082f7428ac6e7f1a3 Mon Sep 17 00:00:00 2001 From: mburtless Date: Wed, 3 Apr 2019 15:48:45 -0400 Subject: [PATCH 110/136] Remove comment --- provider/ns1.go | 1 - 1 file changed, 1 deletion(-) diff --git a/provider/ns1.go b/provider/ns1.go index 6f2c37a79..e4a0d0395 100644 --- a/provider/ns1.go +++ b/provider/ns1.go @@ -94,7 +94,6 @@ func newNS1ProviderWithHTTPClient(config NS1Config, client *http.Client) (*NS1Pr apiClient := api.NewClient(client, api.SetAPIKey(token)) provider := &NS1Provider{ - //client: apiClient, client: NS1DomainService{apiClient}, domainFilter: config.DomainFilter, zoneIDFilter: config.ZoneIDFilter, From 3d46e95f65afc5e89589487c1b2d7d70be04b804 Mon Sep 17 00:00:00 2001 From: mburtless Date: Wed, 3 Apr 2019 16:30:40 -0400 Subject: [PATCH 111/136] Add boilerplate to ns1 provider and tests and simplfy code for inititializing provider --- main.go | 6 +++--- provider/ns1.go | 16 ++++++++++++++++ provider/ns1_test.go | 16 ++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index bebb7444c..174e03bba 100644 --- a/main.go +++ b/main.go @@ -204,9 +204,9 @@ func main() { case "ns1": p, err = provider.NewNS1Provider( provider.NS1Config{ - DomainFilter: domainFilter, - ZoneIDFilter: zoneIDFilter, - DryRun: cfg.DryRun, + DomainFilter: domainFilter, + ZoneIDFilter: zoneIDFilter, + DryRun: cfg.DryRun, }, ) default: diff --git a/provider/ns1.go b/provider/ns1.go index e4a0d0395..31341f0d9 100644 --- a/provider/ns1.go +++ b/provider/ns1.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 provider import ( diff --git a/provider/ns1_test.go b/provider/ns1_test.go index 979de8e45..7ff0aa9b5 100644 --- a/provider/ns1_test.go +++ b/provider/ns1_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 provider import ( From e4f40b4db896e0664dc816ee9388057ac748f3d9 Mon Sep 17 00:00:00 2001 From: mburtless Date: Mon, 22 Apr 2019 14:14:27 -0400 Subject: [PATCH 112/136] Add ns1-go to go.mod and go.sum --- go.mod | 1 + go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/go.mod b/go.mod index 3ad9578aa..254a49d8f 100644 --- a/go.mod +++ b/go.mod @@ -91,6 +91,7 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.42.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect + gopkg.in/ns1/ns1-go.v2 v2.0.0-20190322154155-0dafb5275fd1 gopkg.in/yaml.v2 v2.2.2 istio.io/api v0.0.0-20190321180614-db16d82d3672 istio.io/istio v0.0.0-20190322063008-2b1331886076 diff --git a/go.sum b/go.sum index 56c515220..1629195f3 100644 --- a/go.sum +++ b/go.sum @@ -375,6 +375,8 @@ gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/ns1/ns1-go.v2 v2.0.0-20190322154155-0dafb5275fd1 h1:+fgY/3ngqdBW9oLQCMwL5g+QRkKFPJH05fx2/pipqRQ= +gopkg.in/ns1/ns1-go.v2 v2.0.0-20190322154155-0dafb5275fd1/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw= gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= From 46e475b059015ed3d951f77f313686167a2a3a02 Mon Sep 17 00:00:00 2001 From: mburtless Date: Mon, 22 Apr 2019 14:24:45 -0400 Subject: [PATCH 113/136] Update README with references to NS1 provider --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 85471e96c..a369e159a 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ ExternalDNS' current release is `v0.5`. This version allows you to keep selected * [Oracle Cloud Infrastructure DNS](https://docs.cloud.oracle.com/iaas/Content/DNS/Concepts/dnszonemanagement.htm) * [Linode DNS](https://www.linode.com/docs/networking/dns/) * [RFC2136](https://tools.ietf.org/html/rfc2136) +* [NS1](https://ns1.com/) From this release, ExternalDNS can become aware of the records it is managing (enabled via `--registry=txt`), therefore ExternalDNS can safely manage non-empty hosted zones. We strongly encourage you to use `v0.5` (or greater) with `--registry=txt` enabled and `--txt-owner-id` set to a unique value that doesn't change for the lifetime of your cluster. You might also want to run ExternalDNS in a dry run mode (`--dry-run` flag) to see the changes to be submitted to your DNS Provider API. @@ -78,6 +79,7 @@ The following table clarifies the current status of the providers according to t | Oracle Cloud Infrastructure DNS | Alpha | | Linode DNS | Alpha | | RFC2136 | Alpha | +| NS1 | Alpha | ## Running ExternalDNS: @@ -230,6 +232,7 @@ Here's a rough outline on what is to come (subject to change): - [x] Support for PowerDNS - [x] Support for Linode - [x] Support for RcodeZero +- [x] Support for NS1 ### v0.6 From 4fdaf42e727294f6b3d7aecd960369a7e06f4747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nick=20J=C3=BCttner?= Date: Sat, 20 Apr 2019 16:23:17 +0200 Subject: [PATCH 114/136] Supress noisy logs from Kubernetes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nick Jüttner --- CHANGELOG.md | 4 +++ go.mod | 2 ++ go.sum | 91 ++-------------------------------------------------- 3 files changed, 8 insertions(+), 89 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d125dc359..32d36d8c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## v0.5.14 - 2019-04-23 + + - Core: Supress Kubernetes logs (#991) @njuettner + ## v0.5.13 - 2019-04-18 - Azure: Support multiple A targets (#987) @michaelfig diff --git a/go.mod b/go.mod index 3ad9578aa..d50486f7f 100644 --- a/go.mod +++ b/go.mod @@ -101,3 +101,5 @@ require ( k8s.io/kube-openapi v0.0.0-20190401085232-94e1e7b7574c // indirect launchpad.net/gocheck v0.0.0-20140225173054-000000000087 // indirect ) + +replace github.com/golang/glog => github.com/kubermatic/glog-logrus v0.0.0-20180829085450-3fa5b9870d1d diff --git a/go.sum b/go.sum index 56c515220..519a63b26 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,3 @@ -cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= @@ -7,19 +6,13 @@ github.com/Azure/azure-sdk-for-go v10.0.4-beta+incompatible h1:FhnlL7/4O3gAB7EBg github.com/Azure/azure-sdk-for-go v10.0.4-beta+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-autorest v10.9.0+incompatible h1:3ccqKLQg+scl0J6krcDgih2Rl+GC1eNuHZeRQYQxKkk= github.com/Azure/go-autorest v10.9.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= -github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo= github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= github.com/alecthomas/kingpin v2.2.5+incompatible h1:umWl1NNd72+ZvRti3T9C0SYean2hPZ7ZhxU8bsgc9BQ= github.com/alecthomas/kingpin v2.2.5+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE= -github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1 h1:GDQdwm/gAcJcLAKQQZGOJ4knlw+7rfEQQcmwTbt4p5E= github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -27,29 +20,21 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZq github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20180828111155-cad214d7d71f h1:hinXH9rcBjRoIih5tl4f1BCbNjOmPJ2UnZwcYDhEHR0= github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20180828111155-cad214d7d71f/go.mod h1:T9M45xf79ahXVelWoOBmH0y4aC1t5kXO5BxwyakgIGA= -github.com/apache/thrift v0.12.0 h1:pODnxUFNcjP9UTLZGTdeh+j16A8lJbRvD3rOtrk/7bs= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/aws/aws-sdk-go v1.13.32 h1:AoV2boU+diwKoMaschMtUJim3nmBpM/4y45UqY708F4= github.com/aws/aws-sdk-go v1.13.32/go.mod h1:ZRmQr0FajVIyZ4ZzBYKG5P3ZqPz9IHG41ZoMu1ADI3k= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cloudflare-go v0.0.0-20190102215809-0c85496d8730 h1:+TK6ytATp7coqI4UlTBboFYD0kSkWZt6L6/T+1yBK6k= github.com/cloudflare/cloudflare-go v0.0.0-20190102215809-0c85496d8730/go.mod h1:qKQ9S///VKEax9N8kFel9/AvmnkYgvb8uiKTnoVFvpg= -github.com/codegangsta/cli v1.20.0 h1:iX1FXEgwzd5+XN6wk5cVHOGQj6Q3Dcp20lUeS4lHNTw= github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= -github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -59,15 +44,11 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/digitalocean/godo v1.1.1 h1:v0A7yF3xmKLjjdJGIeBbINfMufcrrRhqZsxuVQMoT+U= github.com/digitalocean/godo v1.1.1/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= -github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/dnsimple/dnsimple-go v0.14.0 h1:JGYtVVA/uHc91q0LjDWqR1oVj6EGu9Kn0lMRxjH/w30= github.com/dnsimple/dnsimple-go v0.14.0/go.mod h1:0FYu4qVNv/UcfZPNwa9zi68IkggJu3TIwM54D7rhmI4= -github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/envoyproxy/go-control-plane v0.6.9 h1:deEH9W8ZAUGNbCdX+9iNzBOGrAOrnpJGoy0PcTqk/tE= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= @@ -75,42 +56,32 @@ github.com/exoscale/egoscale v0.11.0 h1:g+UBsxLDouKWW2BK/UTgQFAVnM2aHygheF0Dxj0y github.com/exoscale/egoscale v0.11.0/go.mod h1:Ee3U4ZjSDpbbEc9VkQ/jttUU8USE8Nv7L3YzVi03Y1U= 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/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-ini/ini v1.32.0 h1:/MArBHSS0TFR28yPPDK1vPIjt4wUnPBfb81i6iiyKvA= github.com/go-ini/ini v1.32.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-resty/resty v1.8.0 h1:vbNCxbHOWCototzwxf3L63PQCKx6xgT6v8SHfoqkp6U= github.com/go-resty/resty v1.8.0/go.mod h1:n37daLLGIHq2FFYHxg+FYQiwA95FpfNI+A9uxoIYGRk= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/googleapis v1.1.0 h1:kFkMAZBNAn4j7K0GiZr8cRYzejq68VbheufiV3YuyFI= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= -github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a h1:ZJu5NB1Bk5ms4vw0Xu4i+jD32SE9jQXyfnOvwhHqlT0= github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -120,22 +91,16 @@ github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhp github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8 h1:L9JPKrtsHMQ4VCRQfHvbbHBfB2Urn8xf6QZeXZ+OrN4= github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c h1:Lh2aW+HnU2Nbe1gqD9SOJLJxW1jBMmQOktN2acDyJk8= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc h1:f8eY6cV/x1x+HLjOp4r72s/31/V2aTUtg5oKRRPf8/Q= github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79 h1:lR9ssWAqp9qL0bALxqEEkuudiP1eweOdv9jsRK3e7lE= github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.6.2/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -143,45 +108,34 @@ github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uP github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/infobloxopen/infoblox-go-client v0.0.0-20180606155407-61dc5f9b0a65 h1:FP5rOFP4ifbtFIjFHJmwhFrsbDyONILK/FNntl/Pou8= github.com/infobloxopen/infoblox-go-client v0.0.0-20180606155407-61dc5f9b0a65/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kubermatic/glog-logrus v0.0.0-20180829085450-3fa5b9870d1d h1:JV46OtdhH2vVt8mJ1EWUE94k99vbN9fZs1WQ8kcEapU= +github.com/kubermatic/glog-logrus v0.0.0-20180829085450-3fa5b9870d1d/go.mod h1:CHQ3o5KBH1PIS2Fb1mRLTIWO5YzP9kSUB3KoCICwlvA= github.com/linki/instrumented_http v0.2.0 h1:zLhcB3Q/McQQqml3qd5kzdZ0cGnL3vquPFIW2338f5Y= github.com/linki/instrumented_http v0.2.0/go.mod h1:pjYbItoegfuVi2GUOMhEqzvm/SJKuEL3H0tc8QRLRFk= github.com/linode/linodego v0.3.0 h1:I83pEPg4owSy5pCPaKix7xkGbWIjPxmAoc/Yu5OYDDY= github.com/linode/linodego v0.3.0/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY= github.com/lyft/protoc-gen-validate v0.0.14 h1:xbdDVIHd0Xq5Bfzu+8JR9s7mFmJPMvNLmfGhgcHJdFU= github.com/lyft/protoc-gen-validate v0.0.14/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -193,7 +147,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= @@ -201,32 +154,25 @@ github.com/nesv/go-dynect v0.6.0 h1:Ow/DiSm4LAISwnFku/FITSQHnU6pBvhQMsUE5Gu6Oq4= github.com/nesv/go-dynect v0.6.0/go.mod h1:GHRBRKzTwjAMhosHJQq/KrZaFkXIFyJ5zRE7thGXXrs= github.com/nic-at/rc0go v1.1.0 h1:k6/Bru/npTjmCSFw65ulYRw/b3ycIS30t6/YM4r42V4= github.com/nic-at/rc0go v1.1.0/go.mod h1:KEa3H5fmDNXCaXSqOeAZxkKnG/8ggr1OHIG25Ve7fjU= -github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= -github.com/openzipkin/zipkin-go v0.1.6 h1:yXiysv1CSK7Q5yjGy1710zZGnsbMUIjluWBxtLXHPBo= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/oracle/oci-go-sdk v1.8.0 h1:4SO45bKV0I3/Mn1os3ANDZmV0eSE5z5CLdSUIkxtyzs= github.com/oracle/oci-go-sdk v1.8.0/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f h1:BVwpUVJDADN2ufcGik7W992pyps0wZ888b/y9GXcLTU= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -235,45 +181,32 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1 h1:/K3IL0Z1quvmJ7X0A1AwNEK7CRkVK3YwfOU/QAL4WGg= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af h1:gu+uRPtBe88sKxUCEXRoeCvVG90TJmwhiqRpvdhQFng= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/sanyu/dynectsoap v0.0.0-20181203081243-b83de5edc4e0 h1:vOcHdR1nu7DO4BAx1rwzdHV7jQTzW3gqcBT5qxHSc6A= github.com/sanyu/dynectsoap v0.0.0-20181203081243-b83de5edc4e0/go.mod h1:FeplEtXXejBYC4NPAFTrs5L7KuK+5RL9bf5nB2vZe9o= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.3 h1:09wy7WZk4AqO03yH85Ex1X+Uo3vDsil3Fa9AgF8Emss= github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9 h1:/Bsw4C+DEdqPjt8vAqaC9LAqpAQnaCQQqmolqq3S1T4= github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9/go.mod h1:RHkNRtSLfOK7qBTHaeSX1D6BNpI3qw7NTxsmNr4RvN8= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8 h1:ndzgwNDnKIqyCvHTXaCqh9KlOWKvBry6nuXMJmonVsE= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.2 h1:JON3E2/GPW2iDNGoSAusl1KDf5TRQ8k8q7Tp097pZGs= github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= -github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780 h1:vG/gY/PxA3v3l04qxe3tDjXyu3bozii8ulSlIPOYKhI= github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yl2chen/cidranger v0.0.0-20180214081945-928b519e5268 h1:lkoOjizoHqOcEFsvYGE5c8Ykdijjnd0R3r1yDYHzLno= github.com/yl2chen/cidranger v0.0.0-20180214081945-928b519e5268/go.mod h1:mq0zhomp/G6rRTb0dvHWXRHr/2+Qgeq5hMXfJ670+i4= -go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A= go.opencensus.io v0.19.2 h1:ZZpq6xI6kv/LuE/5s5UQvBU5vMjvRnPb8PvJrIntAnc= @@ -287,14 +220,11 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4 h1:c2HOrn5iMezYjSlGPncknSEr/8x5LELb/ilJbXi9DEA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 h1:x/bBzNauLQAlE3fLku/xy92Y8QwKX5HZymrMz2IiKFc= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f h1:hX65Cu3JDlGH3uEdK7I99Ii+9kjD6mvnnpfLdEAH0x4= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -303,14 +233,12 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI= @@ -328,7 +256,6 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -341,17 +268,14 @@ golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384 h1:TFlARGu6Czu1z7q93HTxcP1P+/ZFC/IKythI5RzrnRg= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.2.0/go.mod h1:IfRCZScioGtypHNTlz3gFk67J8uePVW7uDTBzXuIkhU= google.golang.org/api v0.3.0 h1:UIJY20OEo3+tK5MBlcdx37kmdH6EnRjGkW78mc6+EeA= google.golang.org/api v0.3.0/go.mod h1:IuvZyQh8jgscv8qWfQ4ABd8m7hEudgBFM/EdhA3BnXw= -google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181219182458-5a97ab628bfb/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= @@ -361,32 +285,23 @@ google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9M google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 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.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099 h1:XJP7lxbSxWLOMNdBE4B/STaqVy6L73o0knwj2vIlxnw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= istio.io/api v0.0.0-20190321180614-db16d82d3672 h1:luY97pBVarSo1v++zf2kgb84Q55G5hv/ult2A4KPQuk= istio.io/api v0.0.0-20190321180614-db16d82d3672/go.mod h1:hhLFQmpHia8zgaM37vb2ml9iS5NfNfqZGRt1pS9aVEo= @@ -400,7 +315,5 @@ k8s.io/apimachinery v0.0.0-20180621070125-103fd098999d h1:MZjlsu9igBoVPZkXpIGoxI k8s.io/apimachinery v0.0.0-20180621070125-103fd098999d/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= k8s.io/client-go v8.0.0+incompatible h1:tTI4hRmb1DRMl4fG6Vclfdi6nTM82oIrTT7HfitmxC4= k8s.io/client-go v8.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= -k8s.io/kube-openapi v0.0.0-20190401085232-94e1e7b7574c h1:kJCzg2vGCzah5icgkKR7O1Dzn0NA2iGlym27sb0ZfGE= k8s.io/kube-openapi v0.0.0-20190401085232-94e1e7b7574c/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= -launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54= launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= From b7c10bb80fd94f530f0f9361a09e11727c03dc68 Mon Sep 17 00:00:00 2001 From: mburtless Date: Tue, 23 Apr 2019 11:27:25 -0400 Subject: [PATCH 115/136] Add tutorial for NS1 and link in README --- README.md | 1 + docs/tutorials/ns1.md | 200 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 docs/tutorials/ns1.md diff --git a/README.md b/README.md index a369e159a..356cd91d1 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ The following tutorials are provided: * [Oracle Cloud Infrastructure (OCI) DNS](docs/tutorials/oracle.md) * [Linode](docs/tutorials/linode.md) * [RFC2136](docs/tutorials/rfc2136.md) +* [NS1](docs/tutorials/ns1.md) ### Running Locally diff --git a/docs/tutorials/ns1.md b/docs/tutorials/ns1.md new file mode 100644 index 000000000..607cf8221 --- /dev/null +++ b/docs/tutorials/ns1.md @@ -0,0 +1,200 @@ +# Setting up ExternalDNS for Services on NS1 + +This tutorial describes how to setup ExternalDNS for use within a +Kubernetes cluster using NS1 DNS. + +Make sure to use **>=0.5** version of ExternalDNS for this tutorial. + +## Creating a zone with NS1 DNS + +If you are new to NS1, we recommend you first read the following +instructions for creating a zone. + +[Creating a zone using the NS1 +portal](https://ns1.com/knowledgebase/creating-a-zone) + +[Creating a zone using the NS1 +API](https://ns1.com/api#put-create-a-new-dns-zone) + +## Creating NS1 Credentials + +All NS1 products are API-first, meaning everything that can be done on +the portal---including managing zones and records, data sources and +feeds, and account settings and users---can be done via API. + +The NS1 API is a standard REST API with JSON responses. The environment +var `NS1_APIKEY` will be needed to run ExternalDNS with NS1. + +### To add or delete an API key + +1. Log into the NS1 portal at [my.nsone.net](http://my.nsone.net). + +2. Click your username in the upper-right corner, and navigate to **Account Settings** \> **Users & Teams**. + +3. Navigate to the _API Keys_ tab, and click **Add Key**. + +4. Enter the name of the application and modify permissions and settings as desired. Once complete, click **Create Key**. The new API key appears in the list. + + Note: Set the permissions for your API keys just as you would for a user or team associated with your organization's NS1 account. For more information, refer to the article [Creating and Managing API Keys](https://ns1.com/knowledgebase/creating-and-managing-users) in the NS1 Knowledge Base. + +## Deploy ExternalDNS + +Connect your `kubectl` client to the cluster with which you want to test ExternalDNS, and then apply one of the following manifest files for deployment: + +### Manifest (for clusters without RBAC enabled) + +```yaml +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: external-dns +spec: + strategy: + type: Recreate + template: + metadata: + labels: + app: external-dns + spec: + containers: + - name: external-dns + image: registry.opensource.zalan.do/teapot/external-dns:latest + 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. + - --provider=ns1 + env: + - name: NS1_APIKEY + value: "YOUR_NS1_API_KEY" +``` + +### Manifest (for clusters with RBAC enabled) + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: external-dns +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: external-dns +rules: +- apiGroups: [""] + resources: ["services"] + verbs: ["get","watch","list"] +- apiGroups: [""] + resources: ["pods"] + verbs: ["get","watch","list"] +- apiGroups: ["extensions"] + resources: ["ingresses"] + verbs: ["get","watch","list"] +- apiGroups: [""] + resources: ["nodes"] + verbs: ["list"] +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: external-dns-viewer +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: external-dns +subjects: +- kind: ServiceAccount + name: external-dns + namespace: default +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: external-dns +spec: + strategy: + type: Recreate + template: + metadata: + labels: + app: external-dns + spec: + serviceAccountName: external-dns + containers: + - name: external-dns + image: registry.opensource.zalan.do/teapot/external-dns:latest + 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. + - --provider=ns1 + env: + - name: NS1_APIKEY + value: "YOUR_NS1_API_KEY" +``` + +## Deploying an Nginx Service + +Create a service file called 'nginx.yaml' with the following contents: + +```yaml +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: nginx +spec: + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: nginx + annotations: + external-dns.alpha.kubernetes.io/hostname: example.com + external-dns.alpha.kubernetes.io/ttl: "120" #optional +spec: + selector: + app: nginx + type: LoadBalancer + ports: + - protocol: TCP + port: 80 + targetPort: 80 +``` + +**A note about annotations** + +Verify that the annotation on the service uses the same hostname as the NS1 DNS zone created above. The annotation may also be a subdomain of the DNS zone (e.g. 'www.example.com'). + +The TTL annotation can be used to configure the TTL on DNS records managed by ExternalDNS and is optional. If this annotation is not set, the TTL on records managed by ExternalDNS will default to 10. + +ExternalDNS uses the hostname annotation to determine which services should be registered with DNS. Removing the hostname annotation will cause ExternalDNS to remove the corresponding DNS records. + +### Create the deployment and service + +``` +$ kubectl create -f nginx.yaml +``` + +Depending on where you run your service, it may take some time for your cloud provider to create an external IP for the service. Once an external IP is assigned, ExternalDNS detects the new service IP address and synchronizes the NS1 DNS records. + +## Verifying NS1 DNS records + +Use the NS1 portal or API to verify that the A record for your domain shows the external IP address of the services. + +## Cleanup + +Once you successfully configure and verify record management via ExternalDNS, you can delete the tutorial's example: + +``` +$ kubectl delete -f nginx.yaml +$ kubectl delete -f externaldns.yaml +``` From 4fdeef3f2fecec6a0a33ae3c5fa01320a5dc39d2 Mon Sep 17 00:00:00 2001 From: mburtless Date: Mon, 8 Apr 2019 15:15:02 -0400 Subject: [PATCH 116/136] Add flags for configuring custom NS1 endpoint and ignoring SSL verification for PrivateDNS support --- main.go | 2 ++ pkg/apis/externaldns/types.go | 6 ++++++ pkg/apis/externaldns/types_test.go | 6 ++++++ provider/ns1.go | 25 ++++++++++++++++++++++++- 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 174e03bba..e1bc7161d 100644 --- a/main.go +++ b/main.go @@ -206,6 +206,8 @@ func main() { provider.NS1Config{ DomainFilter: domainFilter, ZoneIDFilter: zoneIDFilter, + NS1Endpoint: cfg.NS1Endpoint, + NS1IgnoreSSL: cfg.NS1IgnoreSSL, DryRun: cfg.DryRun, }, ) diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 18ba6e00e..77db9ffc6 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -113,6 +113,8 @@ type Config struct { RFC2136TSIGSecret string `secure:"yes"` RFC2136TSIGSecretAlg string RFC2136TAXFR bool + NS1Endpoint string + NS1IgnoreSSL bool } var defaultConfig = &Config{ @@ -186,6 +188,8 @@ var defaultConfig = &Config{ RFC2136TSIGSecret: "", RFC2136TSIGSecretAlg: "", RFC2136TAXFR: true, + NS1Endpoint: "", + NS1IgnoreSSL: false, } // NewConfig returns new Config object @@ -288,6 +292,8 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("pdns-server", "When using the PowerDNS/PDNS provider, specify the URL to the pdns server (required when --provider=pdns)").Default(defaultConfig.PDNSServer).StringVar(&cfg.PDNSServer) app.Flag("pdns-api-key", "When using the PowerDNS/PDNS provider, specify the API key to use to authorize requests (required when --provider=pdns)").Default(defaultConfig.PDNSAPIKey).StringVar(&cfg.PDNSAPIKey) app.Flag("pdns-tls-enabled", "When using the PowerDNS/PDNS provider, specify whether to use TLS (default: false, requires --tls-ca, optionally specify --tls-client-cert and --tls-client-cert-key)").Default(strconv.FormatBool(defaultConfig.PDNSTLSEnabled)).BoolVar(&cfg.PDNSTLSEnabled) + app.Flag("ns1-endpoint", "When using the NS1 provider, specify the endpoint to target if not using Managed DNS (optional)").Default(defaultConfig.NS1Endpoint).StringVar(&cfg.NS1Endpoint) + app.Flag("ns1-ignoressl", "When using the NS1 provider, specify whether to verify the SSL certificate (default: false)").Default(strconv.FormatBool(defaultConfig.NS1IgnoreSSL)).BoolVar(&cfg.NS1IgnoreSSL) // Flags related to TLS communication app.Flag("tls-ca", "When using TLS communication, the path to the certificate authority to verify server communications (optionally specify --tls-client-cert for two-way TLS)").Default(defaultConfig.TLSCA).StringVar(&cfg.TLSCA) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index ab474b498..ae802f49d 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -143,6 +143,8 @@ var ( CRDSourceAPIVersion: "test.k8s.io/v1alpha1", CRDSourceKind: "Endpoint", RcodezeroTXTEncrypt: true, + NS1Endpoint: "https://api.example.com/v1", + NS1IgnoreSSL: true, } // minimal config with istio gateway source and multiple ingressgateway load balancer services @@ -284,6 +286,8 @@ func TestParseFlags(t *testing.T) { "--crd-source-apiversion=test.k8s.io/v1alpha1", "--crd-source-kind=Endpoint", "--rcodezero-txt-encrypt", + "--ns1-endpoint=https://api.example.com/v1", + "--ns1-ignoressl", }, envVars: map[string]string{}, expected: overriddenConfig, @@ -349,6 +353,8 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_CRD_SOURCE_APIVERSION": "test.k8s.io/v1alpha1", "EXTERNAL_DNS_CRD_SOURCE_KIND": "Endpoint", "EXTERNAL_DNS_RCODEZERO_TXT_ENCRYPT": "1", + "EXTERNAL_DNS_NS1_ENDPOINT": "https://api.example.com/v1", + "EXTERNAL_DNS_NS1_IGNORESSL": "1", }, expected: overriddenConfig, }, diff --git a/provider/ns1.go b/provider/ns1.go index 31341f0d9..9ac7e2200 100644 --- a/provider/ns1.go +++ b/provider/ns1.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "crypto/tls" "fmt" "net/http" "os" @@ -85,6 +86,8 @@ func (n NS1DomainService) ListZones() ([]*dns.Zone, *http.Response, error) { type NS1Config struct { DomainFilter DomainFilter ZoneIDFilter ZoneIDFilter + NS1Endpoint string + NS1IgnoreSSL bool DryRun bool } @@ -106,8 +109,28 @@ func newNS1ProviderWithHTTPClient(config NS1Config, client *http.Client) (*NS1Pr if !ok { return nil, fmt.Errorf("NS1_APIKEY environment variable is not set") } + clientArgs := []func(*api.Client){api.SetAPIKey(token)} + if config.NS1Endpoint != "" { + log.Infof("ns1-endpoint flag is set, targeting endpoint at %s", config.NS1Endpoint) + clientArgs = append(clientArgs, api.SetEndpoint(config.NS1Endpoint)) + } - apiClient := api.NewClient(client, api.SetAPIKey(token)) + if config.NS1IgnoreSSL == true { + log.Info("ns1-ignoressl flag is True, skipping SSL verification") + defaultTransport := http.DefaultTransport.(*http.Transport) + tr := &http.Transport{ + Proxy: defaultTransport.Proxy, + DialContext: defaultTransport.DialContext, + MaxIdleConns: defaultTransport.MaxIdleConns, + IdleConnTimeout: defaultTransport.IdleConnTimeout, + ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout, + TLSHandshakeTimeout: defaultTransport.TLSHandshakeTimeout, + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client.Transport = tr + } + + apiClient := api.NewClient(client, clientArgs...) provider := &NS1Provider{ client: NS1DomainService{apiClient}, From 171e87d9ec9b44a7d06fde20ba249751f6ce62cd Mon Sep 17 00:00:00 2001 From: mburtless Date: Fri, 26 Apr 2019 11:12:26 -0400 Subject: [PATCH 117/136] Fix wording on flag description --- 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 77db9ffc6..9bd8a9c51 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -292,7 +292,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("pdns-server", "When using the PowerDNS/PDNS provider, specify the URL to the pdns server (required when --provider=pdns)").Default(defaultConfig.PDNSServer).StringVar(&cfg.PDNSServer) app.Flag("pdns-api-key", "When using the PowerDNS/PDNS provider, specify the API key to use to authorize requests (required when --provider=pdns)").Default(defaultConfig.PDNSAPIKey).StringVar(&cfg.PDNSAPIKey) app.Flag("pdns-tls-enabled", "When using the PowerDNS/PDNS provider, specify whether to use TLS (default: false, requires --tls-ca, optionally specify --tls-client-cert and --tls-client-cert-key)").Default(strconv.FormatBool(defaultConfig.PDNSTLSEnabled)).BoolVar(&cfg.PDNSTLSEnabled) - app.Flag("ns1-endpoint", "When using the NS1 provider, specify the endpoint to target if not using Managed DNS (optional)").Default(defaultConfig.NS1Endpoint).StringVar(&cfg.NS1Endpoint) + app.Flag("ns1-endpoint", "When using the NS1 provider, specify the URL of the API endpoint to target (default: https://api.nsone.net/v1/)").Default(defaultConfig.NS1Endpoint).StringVar(&cfg.NS1Endpoint) app.Flag("ns1-ignoressl", "When using the NS1 provider, specify whether to verify the SSL certificate (default: false)").Default(strconv.FormatBool(defaultConfig.NS1IgnoreSSL)).BoolVar(&cfg.NS1IgnoreSSL) // Flags related to TLS communication From 4fc13e10f450239fc511ab84da4f351294bb2872 Mon Sep 17 00:00:00 2001 From: Raffaele Di Fazio Date: Sun, 28 Apr 2019 11:26:29 +0200 Subject: [PATCH 118/136] add docker image faq --- docs/faq.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/faq.md b/docs/faq.md index 1c92dd4e6..7aa584a72 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -251,3 +251,18 @@ Yes, give it the correct cross-account/assume-role permissions and use the `--aw ### How do I provide multiple values to the annotation `external-dns.alpha.kubernetes.io/hostname`? Separate them by `,`. + + +### Are there official Docker images provided? + +When we tag a new release, we push a Docker image on Zalando's public Docker registry with the following name: + +``` +registry.opensource.zalan.do/teapot/external-dns +``` + +As tags, you can use your version of choice or use `latest` that always resolves to the latest tag. + +If you wish to build your own image, you can use the provided [Dockerfile](../Dockerfile) as a starting point. + +We are currently working with the Kubernetes community to provide official images for the project similarly to what is done with the other official Kubernetes projects, but we don't have an ETA on when those images will be available. \ No newline at end of file From a2b07c1383839fcc9559481234d83ea49189a591 Mon Sep 17 00:00:00 2001 From: Reinier Schoof Date: Sun, 28 Apr 2019 14:42:07 +0200 Subject: [PATCH 119/136] added TransIP provider --- README.md | 4 + docs/faq.md | 1 + docs/ttl.md | 6 +- docs/tutorials/transip.md | 182 +++++++++++++++++ go.mod | 1 + go.sum | 5 + main.go | 2 + pkg/apis/externaldns/types.go | 10 +- provider/transip.go | 373 ++++++++++++++++++++++++++++++++++ provider/transip_test.go | 215 ++++++++++++++++++++ 10 files changed, 797 insertions(+), 2 deletions(-) create mode 100644 docs/tutorials/transip.md create mode 100644 provider/transip.go create mode 100644 provider/transip_test.go diff --git a/README.md b/README.md index 356cd91d1..90187a937 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,8 @@ ExternalDNS' current release is `v0.5`. This version allows you to keep selected * [Linode DNS](https://www.linode.com/docs/networking/dns/) * [RFC2136](https://tools.ietf.org/html/rfc2136) * [NS1](https://ns1.com/) +* [TransIP](https://www.transip.eu/domain-name/) + From this release, ExternalDNS can become aware of the records it is managing (enabled via `--registry=txt`), therefore ExternalDNS can safely manage non-empty hosted zones. We strongly encourage you to use `v0.5` (or greater) with `--registry=txt` enabled and `--txt-owner-id` set to a unique value that doesn't change for the lifetime of your cluster. You might also want to run ExternalDNS in a dry run mode (`--dry-run` flag) to see the changes to be submitted to your DNS Provider API. @@ -80,6 +82,7 @@ The following table clarifies the current status of the providers according to t | Linode DNS | Alpha | | RFC2136 | Alpha | | NS1 | Alpha | +| TransIP | Alpha | ## Running ExternalDNS: @@ -111,6 +114,7 @@ The following tutorials are provided: * [Linode](docs/tutorials/linode.md) * [RFC2136](docs/tutorials/rfc2136.md) * [NS1](docs/tutorials/ns1.md) +* [TransIP](docs/tutorials/transip.md) ### Running Locally diff --git a/docs/faq.md b/docs/faq.md index 7aa584a72..9017738c7 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -45,6 +45,7 @@ Currently, the following providers are supported: - Oracle Cloud Infrastructure DNS - Linode DNS - RFC2136 +- TransIP As stated in the README, we are currently looking for stable maintainers for those providers, to ensure that bugfixes and new features will be available for all of those. diff --git a/docs/ttl.md b/docs/ttl.md index c290ea266..35295e90b 100644 --- a/docs/ttl.md +++ b/docs/ttl.md @@ -27,6 +27,7 @@ Providers - [x] Google - [ ] InMemory - [x] Linode +- [x] TransIP PRs welcome! @@ -51,4 +52,7 @@ For the moment, it is impossible to use a TTL value of 0 with the AWS, DigitalOc This behavior may change in the future. ### Linode Provider -The Linode Provider default TTL is used when the TTL is 0. The default is 24 hours \ No newline at end of file +The Linode Provider default TTL is used when the TTL is 0. The default is 24 hours + +### TransIP Provider +The TransIP Provider minimal TTL is used when the TTL is 0. The minimal TTL is 60s. diff --git a/docs/tutorials/transip.md b/docs/tutorials/transip.md new file mode 100644 index 000000000..1e98d3259 --- /dev/null +++ b/docs/tutorials/transip.md @@ -0,0 +1,182 @@ +# Setting up ExternalDNS for Services on TransIP + +This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using TransIP. + +Make sure to use **>=0.5.5** version of ExternalDNS for this tutorial, have at least 1 domain registered at TransIP and enabled the API. + +## Enable TransIP API and prepare your API key + +To use the TransIP API you need an account at TransIP and enable API usage as described in the [knowledge base](https://www.transip.eu/knowledgebase/entry/77-want-use-the-transip-api/). With the private key generated by the API, we create a kubernetes secret: + +```console +$ kubectl create secret generic transip-api-key --from-file=transip-api-key=/path/to/private.key +secret/transip-api-key created +``` + +## Deploy ExternalDNS + +Below are example manifests, for both cluster without or with RBAC enabled. Don't forget to replace `YOUR_TRANSIP_ACCOUNT_NAME` with your TransIP account name. In these examples, an example domain-filter is defined. Such a filter can be used to prevent ExternalDNS from touching any domain not listed in the filter. Refer to the docs for any other command-line parameters you might want to use. + +### Manifest (for clusters without RBAC enabled) + +```yaml +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: external-dns +spec: + strategy: + type: Recreate + template: + metadata: + labels: + app: external-dns + spec: + containers: + - name: external-dns + image: registry.opensource.zalan.do/teapot/external-dns:latest + args: + - --source=service # ingress is also possible + - --domain-filter=example.com # (optional) limit to only example.com domains + - --provider=transip + - --transip-account=YOUR_TRANSIP_ACCOUNT_NAME + - --transip-keyfile=/transip/transip-api-key + volumeMounts: + - mountPath: /transip + name: transip-api-key + readOnly: true + volumes: + - name: transip-api-key + secret: + secretName: transip-api-key +``` + +### Manifest (for clusters with RBAC enabled) + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: external-dns +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: external-dns +rules: +- apiGroups: [""] + resources: ["services"] + verbs: ["get","watch","list"] +- apiGroups: [""] + resources: ["pods"] + verbs: ["get","watch","list"] +- apiGroups: ["extensions"] + resources: ["ingresses"] + verbs: ["get","watch","list"] +- apiGroups: [""] + resources: ["nodes"] + verbs: ["list"] +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: external-dns-viewer +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: external-dns +subjects: +- kind: ServiceAccount + name: external-dns + namespace: default +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: external-dns +spec: + strategy: + type: Recreate + template: + metadata: + labels: + app: external-dns + spec: + serviceAccountName: external-dns + containers: + - name: external-dns + image: registry.opensource.zalan.do/teapot/external-dns:latest + args: + - --source=service # ingress is also possible + - --domain-filter=example.com # (optional) limit to only example.com domains + - --provider=transip + - --transip-account=YOUR_TRANSIP_ACCOUNT_NAME + - --transip-keyfile=/transip/transip-api-key + volumeMounts: + - mountPath: /transip + name: transip-api-key + readOnly: true + volumes: + - name: transip-api-key + secret: + secretName: transip-api-key +``` + +## Deploying an Nginx Service + +Create a service file called 'nginx.yaml' with the following contents: + +```yaml +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: nginx +spec: + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: nginx + annotations: + external-dns.alpha.kubernetes.io/hostname: my-app.example.com +spec: + selector: + app: nginx + type: LoadBalancer + ports: + - protocol: TCP + port: 80 + targetPort: 80 +``` + +Note the annotation on the service; this is the name ExternalDNS will create and manage DNS records for. + +ExternalDNS uses this annotation to determine what services should be registered with DNS. Removing the annotation will cause ExternalDNS to remove the corresponding DNS records. + +Create the deployment and service: + +```console +$ kubectl create -f nginx.yaml +``` + +Depending where you run your service it can take a little while for your cloud provider to create an external IP for the service. + +Once the service has an external IP assigned, ExternalDNS will notice the new service IP address and synchronize the TransIP DNS records. + +## Verifying TransIP DNS records + +Check your [TransIP Control Panel](https://transip.eu/cp) to view the records for your TransIP DNS zone. + +Click on the zone for the one created above if a different domain was used. + +This should show the external IP address of the service as the A record for your domain. diff --git a/go.mod b/go.mod index 52703b19b..c0e6a5f5d 100644 --- a/go.mod +++ b/go.mod @@ -77,6 +77,7 @@ require ( github.com/stretchr/testify v1.2.2 github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9 // indirect github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8 // indirect + github.com/transip/gotransip v5.8.2+incompatible github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780 // indirect github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect github.com/yl2chen/cidranger v0.0.0-20180214081945-928b519e5268 // indirect diff --git a/go.sum b/go.sum index 54159572d..d548f309e 100644 --- a/go.sum +++ b/go.sum @@ -169,6 +169,7 @@ github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA= @@ -197,11 +198,15 @@ github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9 h1:/Bsw4C+DEdqPjt8vAqaC9LAqpAQnaCQQqmolqq3S1T4= github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9/go.mod h1:RHkNRtSLfOK7qBTHaeSX1D6BNpI3qw7NTxsmNr4RvN8= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/transip/gotransip v5.8.2+incompatible h1:aNJhw/w/3QBqFcHAIPz1ytoK5FexeMzbUCGrrhWr3H0= +github.com/transip/gotransip v5.8.2+incompatible/go.mod h1:uacMoJVmrfOcscM4Bi5NVg708b7c6rz2oDTWqa7i2Ic= github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= diff --git a/main.go b/main.go index 174e03bba..7d2a8de96 100644 --- a/main.go +++ b/main.go @@ -209,6 +209,8 @@ func main() { DryRun: cfg.DryRun, }, ) + case "transip": + p, err = provider.NewTransIPProvider(cfg.TransIPAccountName, cfg.TransIPPrivateKeyFile, domainFilter, cfg.DryRun) default: log.Fatalf("unknown dns provider: %s", cfg.Provider) } diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 18ba6e00e..ff8db22e8 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -113,6 +113,8 @@ type Config struct { RFC2136TSIGSecret string `secure:"yes"` RFC2136TSIGSecretAlg string RFC2136TAXFR bool + TransIPAccountName string + TransIPPrivateKeyFile string } var defaultConfig = &Config{ @@ -186,6 +188,8 @@ var defaultConfig = &Config{ RFC2136TSIGSecret: "", RFC2136TSIGSecretAlg: "", RFC2136TAXFR: true, + TransIPAccountName: "", + TransIPPrivateKeyFile: "", } // NewConfig returns new Config object @@ -253,7 +257,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("service-type-filter", "The service types to take care about (default: all, expected: ClusterIP, NodePort, LoadBalancer or ExternalName)").StringsVar(&cfg.ServiceTypeFilter) // Flags related to providers - app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, google, azure, cloudflare, rcodezero, digitalocean, dnsimple, infoblox, dyn, designate, coredns, skydns, inmemory, pdns, oci, exoscale, linode, rfc2136, ns1)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1") + app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, google, azure, cloudflare, rcodezero, digitalocean, dnsimple, infoblox, dyn, designate, coredns, skydns, inmemory, pdns, oci, exoscale, linode, rfc2136, ns1, transip)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip") app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter) app.Flag("zone-id-filter", "Filter target zones by hosted zone id; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.ZoneIDFilter) app.Flag("google-project", "When using the Google provider, current project is auto-detected, when running on GCP. Specify other project with this. Must be specified when running outside GCP.").Default(defaultConfig.GoogleProject).StringVar(&cfg.GoogleProject) @@ -308,6 +312,10 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("rfc2136-tsig-secret-alg", "When using the RFC2136 provider, specify the TSIG (base64) value to attached to DNS messages (required when --rfc2136-insecure=false)").Default(defaultConfig.RFC2136TSIGSecretAlg).StringVar(&cfg.RFC2136TSIGSecretAlg) app.Flag("rfc2136-tsig-axfr", "When using the RFC2136 provider, specify the TSIG (base64) value to attached to DNS messages (required when --rfc2136-insecure=false)").BoolVar(&cfg.RFC2136TAXFR) + // Flags related to TransIP provider + app.Flag("transip-account", "When using the TransIP provider, specify the account name (required when --provider=transip)").Default(defaultConfig.TransIPAccountName).StringVar(&cfg.TransIPAccountName) + app.Flag("transip-keyfile", "When using the TransIP provider, specify the path to the private key file (required when --provider=transip)").Default(defaultConfig.TransIPPrivateKeyFile).StringVar(&cfg.TransIPPrivateKeyFile) + // Flags related to policies app.Flag("policy", "Modify how DNS records are synchronized between sources and providers (default: sync, options: sync, upsert-only)").Default(defaultConfig.Policy).EnumVar(&cfg.Policy, "sync", "upsert-only") diff --git a/provider/transip.go b/provider/transip.go new file mode 100644 index 000000000..5e5884b8b --- /dev/null +++ b/provider/transip.go @@ -0,0 +1,373 @@ +package provider + +import ( + "errors" + "fmt" + "strings" + + "github.com/kubernetes-incubator/external-dns/endpoint" + "github.com/kubernetes-incubator/external-dns/plan" + log "github.com/sirupsen/logrus" + "github.com/transip/gotransip" + transip "github.com/transip/gotransip/domain" +) + +const ( + // 60 seconds is the current minimal TTL for TransIP and will replace unconfigured + // TTL's for Endpoints + transipMinimalValidTTL = 60 +) + +// TransIPProvider is an implementation of Provider for TransIP. +type TransIPProvider struct { + client gotransip.SOAPClient + domainFilter DomainFilter + dryRun bool +} + +// NewTransIPProvider initializes a new TransIP Provider. +func NewTransIPProvider(accountName, privateKeyFile string, domainFilter DomainFilter, dryRun bool) (*TransIPProvider, error) { + // check given arguments + if accountName == "" { + return nil, errors.New("required --transip-account not set") + } + + if privateKeyFile == "" { + return nil, errors.New("required --transip-keyfile not set") + } + + var apiMode gotransip.APIMode + if dryRun { + apiMode = gotransip.APIModeReadOnly + } else { + apiMode = gotransip.APIModeReadWrite + } + + // create new TransIP API client + c, err := gotransip.NewSOAPClient(gotransip.ClientConfig{ + AccountName: accountName, + PrivateKeyPath: privateKeyFile, + Mode: apiMode, + }) + if err != nil { + return nil, fmt.Errorf("could not setup TransIP API client: %s", err.Error()) + } + + // return tipCloud struct + return &TransIPProvider{ + client: c, + domainFilter: domainFilter, + dryRun: dryRun, + }, nil +} + +// ApplyChanges applies a given set of changes in a given zone. +func (p *TransIPProvider) ApplyChanges(changes *plan.Changes) error { + // build zonefinder with all our zones so we can use FindZone + // and a mapping of zones and their domain name + zones, err := p.fetchZones() + if err != nil { + return err + } + + zoneNameMapper := zoneIDName{} + zonesByName := make(map[string]transip.Domain) + updatedZones := make(map[string]bool) + for _, zone := range zones { + // TransIP API doesn't expose a unique identifier for zones, other than than + // the domain name itself + zoneNameMapper.Add(zone.Name, zone.Name) + zonesByName[zone.Name] = zone + } + + zoneForZoneName := func(name string, m zoneIDName, z map[string]transip.Domain) (zone transip.Domain, err error) { + _, zoneName := m.FindZone(name) + if zoneName == "" { + err = fmt.Errorf("could not find zoneName for %s", name) + return + } + + var ok bool + zone, ok = zonesByName[zoneName] + if !ok { + err = fmt.Errorf("could not find zone for %s", zoneName) + } + + return + } + + // first see if we need to delete anything + for _, ep := range changes.Delete { + log.WithFields(log.Fields{"record": ep.DNSName, "type": ep.RecordType}).Info("endpoint has to go") + + zone, err := zoneForZoneName(ep.DNSName, zoneNameMapper, zonesByName) + if err != nil { + fmt.Printf(err.Error()) + continue + } + + log.Debugf("removing records for %s", zone.Name) + + // remove current records from DNS entry set + entries := p.removeEndpointFromEntries(ep, zone) + + // update zone in zone map + zone.DNSEntries = entries + zonesByName[zone.Name] = zone + // flag zone for updating + updatedZones[zone.Name] = true + } + + for _, ep := range changes.Create { + log.WithFields(log.Fields{"record": ep.DNSName, "type": ep.RecordType}).Info("endpoint is missing") + + zone, err := zoneForZoneName(ep.DNSName, zoneNameMapper, zonesByName) + if err != nil { + fmt.Printf(err.Error()) + continue + } + + log.Debugf("creating records for %s", zone.Name) + + // add new entries to set + zone.DNSEntries = p.addEndpointToEntries(ep, zone, zone.DNSEntries) + + // update zone in zone map + zonesByName[zone.Name] = zone + // flag zone for updating + updatedZones[zone.Name] = true + log.WithFields(log.Fields{"zone": zone.Name}).Debug("flagging for update") + } + + for _, ep := range changes.UpdateNew { + log.WithFields(log.Fields{"record": ep.DNSName, "type": ep.RecordType}).Debug("needs updating") + + zone, err := zoneForZoneName(ep.DNSName, zoneNameMapper, zonesByName) + if err != nil { + log.WithFields(log.Fields{"record": ep.DNSName}).Warn(err.Error()) + continue + } + + // updating the records is basically finding all matching records according + // to the name and the type, removing them from the set and add the new + // records + log.WithFields(log.Fields{ + "zone": zone.Name, + "dnsname": ep.DNSName, + "recordtype": ep.RecordType, + }).Debug("removing matching entries") + + // remove current records from DNS entry set + entries := p.removeEndpointFromEntries(ep, zone) + + // add new entries to set + entries = p.addEndpointToEntries(ep, zone, entries) + + // check to see if actually anything changed in the DNSEntry set + if p.dnsEntriesAreEqual(entries, zone.DNSEntries) { + log.WithFields(log.Fields{"zone": zone.Name}).Debug("not updating identical entries") + continue + } + + // update zone in zone map + zone.DNSEntries = entries + zonesByName[zone.Name] = zone + // flag zone for updating + updatedZones[zone.Name] = true + + log.WithFields(log.Fields{"zone": zone.Name}).Debug("flagging for update") + } + + // go over all updated zones and set new DNSEntry set + for uz, _ := range updatedZones { + zone, ok := zonesByName[uz] + if !ok { + log.WithFields(log.Fields{"zone": uz}).Debug("updated zone no longer found") + continue + } + + if p.dryRun { + log.WithFields(log.Fields{"zone": zone.Name}).Info("not updating in dry-run mode") + continue + } + + log.WithFields(log.Fields{"zone": zone.Name}).Info("updating DNS entries") + if err := transip.SetDNSEntries(p.client, zone.Name, zone.DNSEntries); err != nil { + log.WithFields(log.Fields{"zone": zone.Name, "error": err.Error()}).Warn("failed to update") + } + } + + return nil +} + +// fetchZones returns a list of all domains within the account +func (p *TransIPProvider) fetchZones() ([]transip.Domain, error) { + domainNames, err := transip.GetDomainNames(p.client) + if err != nil { + return nil, err + } + + domains, err := transip.BatchGetInfo(p.client, domainNames) + if err != nil { + return nil, err + } + + var zones []transip.Domain + for _, d := range domains { + if !p.domainFilter.Match(d.Name) { + continue + } + + zones = append(zones, d) + } + + return zones, nil +} + +// Zones returns the list of hosted zones. +func (p *TransIPProvider) Zones() ([]transip.Domain, error) { + zones, err := p.fetchZones() + if err != nil { + return nil, err + } + + return zones, nil +} + +// Records returns the list of records in a given zone. +func (p *TransIPProvider) Records() ([]*endpoint.Endpoint, error) { + zones, err := p.Zones() + if err != nil { + return nil, err + } + + var endpoints []*endpoint.Endpoint + var name string + // go over all zones and their DNS entries and create endpoints for them + for _, zone := range zones { + for _, r := range zone.DNSEntries { + if !supportedRecordType(string(r.Type)) { + continue + } + + name = p.endpointNameForRecord(r, zone) + endpoints = append(endpoints, endpoint.NewEndpointWithTTL(name, string(r.Type), endpoint.TTL(r.TTL), r.Content)) + } + } + + return endpoints, nil +} + +// endpointNameForRecord returns "www.example.org" for DNSEntry with Name "www" and +// Doman with Name "example.org" +func (p *TransIPProvider) endpointNameForRecord(r transip.DNSEntry, d transip.Domain) string { + // root name is identified by "@" and should be translated to domain name for + // the endpoint entry. + if r.Name == "@" { + return d.Name + } + + return fmt.Sprintf("%s.%s", r.Name, d.Name) +} + +// recordNameForEndpoint returns "www" for Endpoint with DNSName "www.example.org" +// and Domain with Name "example.org" +func (p *TransIPProvider) recordNameForEndpoint(ep *endpoint.Endpoint, d transip.Domain) string { + // root name is identified by "@" and should be translated to domain name for + // the endpoint entry. + if ep.DNSName == d.Name { + return "@" + } + + return strings.TrimSuffix(ep.DNSName, "."+d.Name) +} + +// getMinimalValidTTL returns max between given Endpoint's RecordTTL and +// transipMinimalValidTTL +func (p *TransIPProvider) getMinimalValidTTL(ep *endpoint.Endpoint) int64 { + // TTL cannot be lower than transipMinimalValidTTL + if ep.RecordTTL < transipMinimalValidTTL { + return transipMinimalValidTTL + } + + return int64(ep.RecordTTL) +} + +// dnsEntriesAreEqual compares the entries in 2 sets and returns true if the +// content of the entries is equal +func (p *TransIPProvider) dnsEntriesAreEqual(a, b transip.DNSEntries) bool { + if len(a) != len(b) { + return false + } + + match := 0 + for _, aa := range a { + for _, bb := range b { + if aa.Content != bb.Content { + continue + } + + if aa.Name != bb.Name { + continue + } + + if aa.TTL != bb.TTL { + continue + } + + if aa.Type != bb.Type { + continue + } + + match += 1 + } + } + + return (len(a) == match) +} + +// removeEndpointFromEntries removes DNS entries from zone's set that match the +// type and name from given endpoint and returns the resulting DNS entry set +func (p *TransIPProvider) removeEndpointFromEntries(ep *endpoint.Endpoint, zone transip.Domain) transip.DNSEntries { + // create new entry set + entries := transip.DNSEntries{} + // go over each DNS entry to see if it is a match + for _, e := range zone.DNSEntries { + // if we have match, don't copy it to the new entry set + if p.endpointNameForRecord(e, zone) == ep.DNSName && string(e.Type) == ep.RecordType { + log.WithFields(log.Fields{ + "name": e.Name, + "content": e.Content, + "type": e.Type, + }).Debug("found match") + continue + } + + entries = append(entries, e) + } + + return entries +} + +// addEndpointToEntries creates DNS entries for given endpoint and returns +// resulting DNS entry set +func (p *TransIPProvider) addEndpointToEntries(ep *endpoint.Endpoint, zone transip.Domain, entries transip.DNSEntries) transip.DNSEntries { + ttl := p.getMinimalValidTTL(ep) + for _, target := range ep.Targets { + log.WithFields(log.Fields{ + "zone": zone.Name, + "dnsname": ep.DNSName, + "recordtype": ep.RecordType, + "ttl": ttl, + "target": target, + }).Debugf("adding new record") + entries = append(entries, transip.DNSEntry{ + Name: p.recordNameForEndpoint(ep, zone), + TTL: ttl, + Type: transip.DNSEntryType(ep.RecordType), + Content: target, + }) + } + + return entries +} diff --git a/provider/transip_test.go b/provider/transip_test.go new file mode 100644 index 000000000..b195a246d --- /dev/null +++ b/provider/transip_test.go @@ -0,0 +1,215 @@ +package provider + +import ( + "testing" + + "github.com/kubernetes-incubator/external-dns/endpoint" + "github.com/stretchr/testify/assert" + transip "github.com/transip/gotransip/domain" +) + +func TestTransIPDnsEntriesAreEqual(t *testing.T) { + p := TransIPProvider{} + // test with equal set + a := transip.DNSEntries{ + transip.DNSEntry{ + Name: "www.example.org", + Type: transip.DNSEntryTypeCNAME, + TTL: 3600, + Content: "www.example.com", + }, + transip.DNSEntry{ + Name: "www.example.com", + Type: transip.DNSEntryTypeA, + TTL: 3600, + Content: "192.168.0.1", + }, + } + + b := transip.DNSEntries{ + transip.DNSEntry{ + Name: "www.example.com", + Type: transip.DNSEntryTypeA, + TTL: 3600, + Content: "192.168.0.1", + }, + transip.DNSEntry{ + Name: "www.example.org", + Type: transip.DNSEntryTypeCNAME, + TTL: 3600, + Content: "www.example.com", + }, + } + + assert.Equal(t, true, p.dnsEntriesAreEqual(a, b)) + + // change type on one of b's records + b[1].Type = transip.DNSEntryTypeNS + assert.Equal(t, false, p.dnsEntriesAreEqual(a, b)) + b[1].Type = transip.DNSEntryTypeCNAME + + // change ttl on one of b's records + b[1].TTL = 1800 + assert.Equal(t, false, p.dnsEntriesAreEqual(a, b)) + b[1].TTL = 3600 + + // change name on one of b's records + b[1].Name = "example.org" + assert.Equal(t, false, p.dnsEntriesAreEqual(a, b)) + + // remove last entry of b + b = b[:1] + assert.Equal(t, false, p.dnsEntriesAreEqual(a, b)) +} + +func TestTransIPGetMinimalValidTTL(t *testing.T) { + p := TransIPProvider{} + // test with 'unconfigured' TTL + ep := &endpoint.Endpoint{} + assert.Equal(t, int64(transipMinimalValidTTL), p.getMinimalValidTTL(ep)) + + // test with lower than minimal ttl + ep.RecordTTL = (transipMinimalValidTTL - 1) + assert.Equal(t, int64(transipMinimalValidTTL), p.getMinimalValidTTL(ep)) + + // test with higher than minimal ttl + ep.RecordTTL = (transipMinimalValidTTL + 1) + assert.Equal(t, int64(transipMinimalValidTTL+1), p.getMinimalValidTTL(ep)) +} + +func TestTransIPRecordNameForEndpoint(t *testing.T) { + p := TransIPProvider{} + ep := &endpoint.Endpoint{ + DNSName: "example.org", + } + d := transip.Domain{ + Name: "example.org", + } + + assert.Equal(t, "@", p.recordNameForEndpoint(ep, d)) + + ep.DNSName = "www.example.org" + assert.Equal(t, "www", p.recordNameForEndpoint(ep, d)) +} + +func TestTransIPEndpointNameForRecord(t *testing.T) { + p := TransIPProvider{} + r := transip.DNSEntry{ + Name: "@", + } + d := transip.Domain{ + Name: "example.org", + } + + assert.Equal(t, d.Name, p.endpointNameForRecord(r, d)) + + r.Name = "www" + assert.Equal(t, "www.example.org", p.endpointNameForRecord(r, d)) +} + +func TestTransIPAddEndpointToEntries(t *testing.T) { + p := TransIPProvider{} + + // prepare endpoint + ep := &endpoint.Endpoint{ + DNSName: "www.example.org", + RecordType: "A", + RecordTTL: 1800, + Targets: []string{ + "192.168.0.1", + "192.168.0.2", + }, + } + + // prepare zone with DNS entry set + zone := transip.Domain{ + Name: "example.org", + // 2 matching A records + DNSEntries: transip.DNSEntries{ + // 1 non-matching A record + transip.DNSEntry{ + Name: "mail", + Type: transip.DNSEntryTypeA, + Content: "192.168.0.1", + TTL: 3600, + }, + // 1 non-matching MX record + transip.DNSEntry{ + Name: "@", + Type: transip.DNSEntryTypeMX, + Content: "mail.example.org", + TTL: 3600, + }, + }, + } + + // add endpoint to zone's entries + result := p.addEndpointToEntries(ep, zone, zone.DNSEntries) + + assert.Equal(t, 4, len(result)) + assert.Equal(t, "mail", result[0].Name) + assert.Equal(t, transip.DNSEntryTypeA, result[0].Type) + assert.Equal(t, "@", result[1].Name) + assert.Equal(t, transip.DNSEntryTypeMX, result[1].Type) + assert.Equal(t, "www", result[2].Name) + assert.Equal(t, transip.DNSEntryTypeA, result[2].Type) + assert.Equal(t, "192.168.0.1", result[2].Content) + assert.Equal(t, int64(1800), result[2].TTL) + assert.Equal(t, "www", result[3].Name) + assert.Equal(t, transip.DNSEntryTypeA, result[3].Type) + assert.Equal(t, "192.168.0.2", result[3].Content) + assert.Equal(t, int64(1800), result[3].TTL) +} + +func TestTransIPRemoveEndpointFromEntries(t *testing.T) { + p := TransIPProvider{} + + // prepare endpoint + ep := &endpoint.Endpoint{ + DNSName: "www.example.org", + RecordType: "A", + } + + // prepare zone with DNS entry set + zone := transip.Domain{ + Name: "example.org", + // 2 matching A records + DNSEntries: transip.DNSEntries{ + transip.DNSEntry{ + Name: "www", + Type: transip.DNSEntryTypeA, + Content: "192.168.0.1", + TTL: 3600, + }, + transip.DNSEntry{ + Name: "www", + Type: transip.DNSEntryTypeA, + Content: "192.168.0.2", + TTL: 3600, + }, + // 1 non-matching A record + transip.DNSEntry{ + Name: "mail", + Type: transip.DNSEntryTypeA, + Content: "192.168.0.1", + TTL: 3600, + }, + // 1 non-matching MX record + transip.DNSEntry{ + Name: "@", + Type: transip.DNSEntryTypeMX, + Content: "mail.example.org", + TTL: 3600, + }, + }, + } + + // remove endpoint from zone's entries + result := p.removeEndpointFromEntries(ep, zone) + + assert.Equal(t, 2, len(result)) + assert.Equal(t, "mail", result[0].Name) + assert.Equal(t, transip.DNSEntryTypeA, result[0].Type) + assert.Equal(t, "@", result[1].Name) + assert.Equal(t, transip.DNSEntryTypeMX, result[1].Type) +} From 6cb203a02b9a76ec0cd5b79a894c7f26b6dace6d Mon Sep 17 00:00:00 2001 From: Reinier Schoof Date: Sun, 28 Apr 2019 20:30:30 +0200 Subject: [PATCH 120/136] fixed linting issue --- provider/transip.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/provider/transip.go b/provider/transip.go index 5e5884b8b..76e2a0bfe 100644 --- a/provider/transip.go +++ b/provider/transip.go @@ -102,7 +102,7 @@ func (p *TransIPProvider) ApplyChanges(changes *plan.Changes) error { zone, err := zoneForZoneName(ep.DNSName, zoneNameMapper, zonesByName) if err != nil { - fmt.Printf(err.Error()) + log.Errorf("could not find zone for %s: %s", ep.DNSName, err.Error()) continue } @@ -123,7 +123,7 @@ func (p *TransIPProvider) ApplyChanges(changes *plan.Changes) error { zone, err := zoneForZoneName(ep.DNSName, zoneNameMapper, zonesByName) if err != nil { - fmt.Printf(err.Error()) + log.Errorf("could not find zone for %s: %s", ep.DNSName, err.Error()) continue } @@ -179,7 +179,7 @@ func (p *TransIPProvider) ApplyChanges(changes *plan.Changes) error { } // go over all updated zones and set new DNSEntry set - for uz, _ := range updatedZones { + for uz := range updatedZones { zone, ok := zonesByName[uz] if !ok { log.WithFields(log.Fields{"zone": uz}).Debug("updated zone no longer found") From ad0264218dd538a55c001a1aeb2c3d8ea1c0815a Mon Sep 17 00:00:00 2001 From: James Bowes Date: Sat, 27 Apr 2019 09:20:29 -0300 Subject: [PATCH 121/136] DNSimple: Support apex records In DNSimple, apex records are represented with an empty name. Respect this in the provider code. --- go.mod | 1 - go.sum | 51 +++++++++++++++++++++++++++++++++++++-- provider/dnsimple.go | 15 ++++++++++-- provider/dnsimple_test.go | 18 +++++++++++--- 4 files changed, 77 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 52703b19b..68098f2aa 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,6 @@ require ( github.com/go-resty/resty v1.8.0 // indirect github.com/gogo/googleapis v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect - github.com/golang/mock v1.2.0 // indirect github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a // indirect github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect diff --git a/go.sum b/go.sum index 54159572d..8287d39ed 100644 --- a/go.sum +++ b/go.sum @@ -6,13 +6,17 @@ github.com/Azure/azure-sdk-for-go v10.0.4-beta+incompatible h1:FhnlL7/4O3gAB7EBg github.com/Azure/azure-sdk-for-go v10.0.4-beta+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-autorest v10.9.0+incompatible h1:3ccqKLQg+scl0J6krcDgih2Rl+GC1eNuHZeRQYQxKkk= github.com/Azure/go-autorest v10.9.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= +github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo= github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= github.com/alecthomas/kingpin v2.2.5+incompatible h1:umWl1NNd72+ZvRti3T9C0SYean2hPZ7ZhxU8bsgc9BQ= github.com/alecthomas/kingpin v2.2.5+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE= +github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1 h1:GDQdwm/gAcJcLAKQQZGOJ4knlw+7rfEQQcmwTbt4p5E= github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -25,16 +29,21 @@ github.com/aws/aws-sdk-go v1.13.32 h1:AoV2boU+diwKoMaschMtUJim3nmBpM/4y45UqY708F github.com/aws/aws-sdk-go v1.13.32/go.mod h1:ZRmQr0FajVIyZ4ZzBYKG5P3ZqPz9IHG41ZoMu1ADI3k= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cloudflare-go v0.0.0-20190102215809-0c85496d8730 h1:+TK6ytATp7coqI4UlTBboFYD0kSkWZt6L6/T+1yBK6k= github.com/cloudflare/cloudflare-go v0.0.0-20190102215809-0c85496d8730/go.mod h1:qKQ9S///VKEax9N8kFel9/AvmnkYgvb8uiKTnoVFvpg= github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= +github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -44,6 +53,7 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/digitalocean/godo v1.1.1 h1:v0A7yF3xmKLjjdJGIeBbINfMufcrrRhqZsxuVQMoT+U= github.com/digitalocean/godo v1.1.1/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= +github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/dnsimple/dnsimple-go v0.14.0 h1:JGYtVVA/uHc91q0LjDWqR1oVj6EGu9Kn0lMRxjH/w30= github.com/dnsimple/dnsimple-go v0.14.0/go.mod h1:0FYu4qVNv/UcfZPNwa9zi68IkggJu3TIwM54D7rhmI4= @@ -56,6 +66,7 @@ github.com/exoscale/egoscale v0.11.0 h1:g+UBsxLDouKWW2BK/UTgQFAVnM2aHygheF0Dxj0y github.com/exoscale/egoscale v0.11.0/go.mod h1:Ee3U4ZjSDpbbEc9VkQ/jttUU8USE8Nv7L3YzVi03Y1U= 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/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -71,8 +82,7 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -82,6 +92,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a h1:ZJu5NB1Bk5ms4vw0Xu4i+jD32SE9jQXyfnOvwhHqlT0= github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -91,16 +102,22 @@ github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhp github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8 h1:L9JPKrtsHMQ4VCRQfHvbbHBfB2Urn8xf6QZeXZ+OrN4= github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c h1:Lh2aW+HnU2Nbe1gqD9SOJLJxW1jBMmQOktN2acDyJk8= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc h1:f8eY6cV/x1x+HLjOp4r72s/31/V2aTUtg5oKRRPf8/Q= github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79 h1:lR9ssWAqp9qL0bALxqEEkuudiP1eweOdv9jsRK3e7lE= github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.6.2/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -108,24 +125,31 @@ github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uP github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/infobloxopen/infoblox-go-client v0.0.0-20180606155407-61dc5f9b0a65 h1:FP5rOFP4ifbtFIjFHJmwhFrsbDyONILK/FNntl/Pou8= github.com/infobloxopen/infoblox-go-client v0.0.0-20180606155407-61dc5f9b0a65/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kubermatic/glog-logrus v0.0.0-20180829085450-3fa5b9870d1d h1:JV46OtdhH2vVt8mJ1EWUE94k99vbN9fZs1WQ8kcEapU= github.com/kubermatic/glog-logrus v0.0.0-20180829085450-3fa5b9870d1d/go.mod h1:CHQ3o5KBH1PIS2Fb1mRLTIWO5YzP9kSUB3KoCICwlvA= @@ -135,6 +159,7 @@ github.com/linode/linodego v0.3.0 h1:I83pEPg4owSy5pCPaKix7xkGbWIjPxmAoc/Yu5OYDDY github.com/linode/linodego v0.3.0/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY= github.com/lyft/protoc-gen-validate v0.0.14 h1:xbdDVIHd0Xq5Bfzu+8JR9s7mFmJPMvNLmfGhgcHJdFU= github.com/lyft/protoc-gen-validate v0.0.14/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -157,8 +182,10 @@ github.com/nic-at/rc0go v1.1.0/go.mod h1:KEa3H5fmDNXCaXSqOeAZxkKnG/8ggr1OHIG25Ve github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= @@ -169,6 +196,7 @@ github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA= @@ -187,26 +215,37 @@ github.com/sanyu/dynectsoap v0.0.0-20181203081243-b83de5edc4e0 h1:vOcHdR1nu7DO4B github.com/sanyu/dynectsoap v0.0.0-20181203081243-b83de5edc4e0/go.mod h1:FeplEtXXejBYC4NPAFTrs5L7KuK+5RL9bf5nB2vZe9o= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.3 h1:09wy7WZk4AqO03yH85Ex1X+Uo3vDsil3Fa9AgF8Emss= github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9 h1:/Bsw4C+DEdqPjt8vAqaC9LAqpAQnaCQQqmolqq3S1T4= github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9/go.mod h1:RHkNRtSLfOK7qBTHaeSX1D6BNpI3qw7NTxsmNr4RvN8= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8 h1:ndzgwNDnKIqyCvHTXaCqh9KlOWKvBry6nuXMJmonVsE= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.2 h1:JON3E2/GPW2iDNGoSAusl1KDf5TRQ8k8q7Tp097pZGs= github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= +github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780 h1:vG/gY/PxA3v3l04qxe3tDjXyu3bozii8ulSlIPOYKhI= github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yl2chen/cidranger v0.0.0-20180214081945-928b519e5268 h1:lkoOjizoHqOcEFsvYGE5c8Ykdijjnd0R3r1yDYHzLno= github.com/yl2chen/cidranger v0.0.0-20180214081945-928b519e5268/go.mod h1:mq0zhomp/G6rRTb0dvHWXRHr/2+Qgeq5hMXfJ670+i4= +go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A= go.opencensus.io v0.19.2 h1:ZZpq6xI6kv/LuE/5s5UQvBU5vMjvRnPb8PvJrIntAnc= @@ -276,6 +315,7 @@ google.golang.org/api v0.3.0/go.mod h1:IuvZyQh8jgscv8qWfQ4ABd8m7hEudgBFM/EdhA3Bn google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181219182458-5a97ab628bfb/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= @@ -287,16 +327,21 @@ google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 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.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/ns1/ns1-go.v2 v2.0.0-20190322154155-0dafb5275fd1 h1:+fgY/3ngqdBW9oLQCMwL5g+QRkKFPJH05fx2/pipqRQ= gopkg.in/ns1/ns1-go.v2 v2.0.0-20190322154155-0dafb5275fd1/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw= gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 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= @@ -317,5 +362,7 @@ k8s.io/apimachinery v0.0.0-20180621070125-103fd098999d h1:MZjlsu9igBoVPZkXpIGoxI k8s.io/apimachinery v0.0.0-20180621070125-103fd098999d/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= k8s.io/client-go v8.0.0+incompatible h1:tTI4hRmb1DRMl4fG6Vclfdi6nTM82oIrTT7HfitmxC4= k8s.io/client-go v8.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= +k8s.io/kube-openapi v0.0.0-20190401085232-94e1e7b7574c h1:kJCzg2vGCzah5icgkKR7O1Dzn0NA2iGlym27sb0ZfGE= k8s.io/kube-openapi v0.0.0-20190401085232-94e1e7b7574c/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54= launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= diff --git a/provider/dnsimple.go b/provider/dnsimple.go index d7c7c7606..08d9e5347 100644 --- a/provider/dnsimple.go +++ b/provider/dnsimple.go @@ -176,7 +176,13 @@ func (p *dnsimpleProvider) Records() (endpoints []*endpoint.Endpoint, _ error) { default: continue } - endpoints = append(endpoints, endpoint.NewEndpointWithTTL(record.Name+"."+record.ZoneID, record.Type, endpoint.TTL(record.TTL), record.Content)) + // Apex records have an empty string for their name. + // Consider this when creating the endpoint dnsName + dnsName := fmt.Sprintf("%s.%s", record.Name, record.ZoneID) + if record.Name == "" { + dnsName = record.ZoneID + } + endpoints = append(endpoints, endpoint.NewEndpointWithTTL(dnsName, record.Type, endpoint.TTL(record.TTL), record.Content)) } page++ if page > records.Pagination.TotalPages { @@ -234,7 +240,12 @@ func (p *dnsimpleProvider) submitChanges(changes []*dnsimpleChange) error { log.Infof("Changing records: %s %v in zone: %s", change.Action, change.ResourceRecordSet, zone.Name) - change.ResourceRecordSet.Name = strings.TrimSuffix(change.ResourceRecordSet.Name, "."+zone.Name) + if change.ResourceRecordSet.Name == zone.Name { + change.ResourceRecordSet.Name = "" // Apex records have an empty name + } else { + change.ResourceRecordSet.Name = strings.TrimSuffix(change.ResourceRecordSet.Name, fmt.Sprintf(".%s", zone.Name)) + } + if !p.dryRun { switch change.Action { case dnsimpleCreate: diff --git a/provider/dnsimple_test.go b/provider/dnsimple_test.go index b0139c4eb..b6182f535 100644 --- a/provider/dnsimple_test.go +++ b/provider/dnsimple_test.go @@ -84,8 +84,18 @@ func TestDnsimpleServices(t *testing.T) { Priority: 0, Type: "CNAME", } + fourthRecord := dnsimple.ZoneRecord{ + ID: 4, + ZoneID: "example.com", + ParentID: 0, + Name: "", // Apex domain A record + Content: "127.0.0.1", + TTL: 3600, + Priority: 0, + Type: "A", + } - records := []dnsimple.ZoneRecord{firstRecord, secondRecord, thirdRecord} + records := []dnsimple.ZoneRecord{firstRecord, secondRecord, thirdRecord, fourthRecord} dnsimpleListRecordsResponse = dnsimple.ZoneRecordsResponse{ Response: dnsimple.Response{Pagination: &dnsimple.Pagination{}}, Data: records, @@ -115,7 +125,6 @@ func TestDnsimpleServices(t *testing.T) { mockDNS.On("CreateRecord", "1", record.ZoneID, simpleRecord).Return(&dnsimple.ZoneRecordResponse{}, nil) mockDNS.On("DeleteRecord", "1", record.ZoneID, record.ID).Return(&dnsimple.ZoneRecordResponse{}, nil) mockDNS.On("UpdateRecord", "1", record.ZoneID, record.ID, simpleRecord).Return(&dnsimple.ZoneRecordResponse{}, nil) - mockDNS.On("UpdateRecord", "1", record.ZoneID, record.ID, simpleRecord).Return(&dnsimple.ZoneRecordResponse{}, nil) } mockProvider = dnsimpleProvider{client: mockDNS} @@ -157,7 +166,10 @@ func testDnsimpleProviderApplyChanges(t *testing.T) { {DNSName: "custom-ttl.example.com", RecordTTL: 60, Targets: endpoint.Targets{"target"}, RecordType: endpoint.RecordTypeCNAME}, } changes.Delete = []*endpoint.Endpoint{{DNSName: "example-beta.example.com", Targets: endpoint.Targets{"127.0.0.1"}, RecordType: endpoint.RecordTypeA}} - changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "example.example.com", Targets: endpoint.Targets{"target"}, RecordType: endpoint.RecordTypeCNAME}} + changes.UpdateNew = []*endpoint.Endpoint{ + {DNSName: "example.example.com", Targets: endpoint.Targets{"target"}, RecordType: endpoint.RecordTypeCNAME}, + {DNSName: "example.com", Targets: endpoint.Targets{"127.0.0.1"}, RecordType: endpoint.RecordTypeA}, + } mockProvider.accountID = "1" err := mockProvider.ApplyChanges(changes) From ca6d7e870e45b45aacf4d88e6a2c85ed671d6616 Mon Sep 17 00:00:00 2001 From: Josh Harshman Date: Wed, 1 May 2019 10:37:28 -0600 Subject: [PATCH 122/136] scratch - chnage from scratch to distroless --- Dockerfile.scratch | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Dockerfile.scratch b/Dockerfile.scratch index 257525b4d..988fcc818 100644 --- a/Dockerfile.scratch +++ b/Dockerfile.scratch @@ -3,12 +3,6 @@ WORKDIR /external-dns COPY . . RUN make build -FROM alpine:latest as pkgs -RUN apk --update add ca-certificates - -FROM scratch -COPY --from=pkgs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt -COPY --from=pkgs /etc/passwd /etc/passwd +FROM grc.io/distroless/static COPY --from=builder /external-dns/build/external-dns /external-dns -USER nobody ENTRYPOINT ["./external-dns"] From db47517076446974f291eda6083b42c3496d12f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pra=C5=BCak?= Date: Thu, 2 May 2019 09:51:54 +0200 Subject: [PATCH 123/136] Update aws.md Fixes `Failed to watch *v1.Node: unknown (get nodes)` --- docs/tutorials/aws.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/aws.md b/docs/tutorials/aws.md index f10d3d379..88a7a75a2 100644 --- a/docs/tutorials/aws.md +++ b/docs/tutorials/aws.md @@ -119,7 +119,7 @@ rules: verbs: ["get","watch","list"] - apiGroups: [""] resources: ["nodes"] - verbs: ["list"] + verbs: ["list","watch"] --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding From f483b4695ca121b3a8ba03b61972ec5d8e2d447a Mon Sep 17 00:00:00 2001 From: Josh Harshman Date: Fri, 3 May 2019 15:06:27 -0600 Subject: [PATCH 124/136] scratch - use gcr.io/distroless --- Dockerfile.scratch => Dockerfile.mini | 2 +- Makefile | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename Dockerfile.scratch => Dockerfile.mini (85%) diff --git a/Dockerfile.scratch b/Dockerfile.mini similarity index 85% rename from Dockerfile.scratch rename to Dockerfile.mini index 988fcc818..6e0303d99 100644 --- a/Dockerfile.scratch +++ b/Dockerfile.mini @@ -3,6 +3,6 @@ WORKDIR /external-dns COPY . . RUN make build -FROM grc.io/distroless/static +FROM gcr.io/distroless/static COPY --from=builder /external-dns/build/external-dns /external-dns ENTRYPOINT ["./external-dns"] diff --git a/Makefile b/Makefile index 8670e38c4..765fecb9f 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ test: go test -v -race $(shell go list ./... | grep -v /vendor/) # The build targets allow to build the binary and docker image -.PHONY: build build.docker build.scratch +.PHONY: build build.docker build.mini BINARY ?= external-dns SOURCES = $(shell find . -name '*.go') @@ -61,8 +61,8 @@ build.push: build.docker build.docker: docker build --rm --tag "$(IMAGE):$(VERSION)" . -build.scratch: - docker build --rm --tag "$(IMAGE):$(VERSION)" -f Dockerfile.scratch . +build.mini: + docker build --rm --tag "$(IMAGE):$(VERSION)" -f Dockerfile.mini . clean: @rm -rf build From 407f15f63d944f4b9cc839a153e208cbefc2e408 Mon Sep 17 00:00:00 2001 From: Reinier Schoof Date: Tue, 7 May 2019 10:13:57 +0200 Subject: [PATCH 125/136] add TransIP support to roadmap checklist --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 90187a937..83706d3f3 100644 --- a/README.md +++ b/README.md @@ -238,6 +238,7 @@ Here's a rough outline on what is to come (subject to change): - [x] Support for Linode - [x] Support for RcodeZero - [x] Support for NS1 +- [x] Support for TransIP ### v0.6 From eca0025558b53d77b8f2faf7f5f54ad6936a060a Mon Sep 17 00:00:00 2001 From: Reinier Schoof Date: Tue, 7 May 2019 12:01:01 +0200 Subject: [PATCH 126/136] tweaked transip provider tutorial --- docs/tutorials/transip.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/tutorials/transip.md b/docs/tutorials/transip.md index 1e98d3259..8287baebb 100644 --- a/docs/tutorials/transip.md +++ b/docs/tutorials/transip.md @@ -2,7 +2,7 @@ This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using TransIP. -Make sure to use **>=0.5.5** version of ExternalDNS for this tutorial, have at least 1 domain registered at TransIP and enabled the API. +Make sure to use **>=0.5.14** version of ExternalDNS for this tutorial, have at least 1 domain registered at TransIP and enabled the API. ## Enable TransIP API and prepare your API key @@ -10,7 +10,6 @@ To use the TransIP API you need an account at TransIP and enable API usage as de ```console $ kubectl create secret generic transip-api-key --from-file=transip-api-key=/path/to/private.key -secret/transip-api-key created ``` ## Deploy ExternalDNS @@ -75,7 +74,7 @@ rules: verbs: ["get","watch","list"] - apiGroups: [""] resources: ["nodes"] - verbs: ["list"] + verbs: ["watch", "list"] --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding From 18abcf56f1fc4120a387d7e99fba486c209f15c9 Mon Sep 17 00:00:00 2001 From: Reinier Schoof Date: Tue, 7 May 2019 12:02:00 +0200 Subject: [PATCH 127/136] moved zoneForZoneName function into TransIPProvider struct --- provider/transip.go | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/provider/transip.go b/provider/transip.go index 76e2a0bfe..5b8b7f8b8 100644 --- a/provider/transip.go +++ b/provider/transip.go @@ -80,27 +80,11 @@ func (p *TransIPProvider) ApplyChanges(changes *plan.Changes) error { zonesByName[zone.Name] = zone } - zoneForZoneName := func(name string, m zoneIDName, z map[string]transip.Domain) (zone transip.Domain, err error) { - _, zoneName := m.FindZone(name) - if zoneName == "" { - err = fmt.Errorf("could not find zoneName for %s", name) - return - } - - var ok bool - zone, ok = zonesByName[zoneName] - if !ok { - err = fmt.Errorf("could not find zone for %s", zoneName) - } - - return - } - // first see if we need to delete anything for _, ep := range changes.Delete { log.WithFields(log.Fields{"record": ep.DNSName, "type": ep.RecordType}).Info("endpoint has to go") - zone, err := zoneForZoneName(ep.DNSName, zoneNameMapper, zonesByName) + zone, err := p.zoneForZoneName(ep.DNSName, zoneNameMapper, zonesByName) if err != nil { log.Errorf("could not find zone for %s: %s", ep.DNSName, err.Error()) continue @@ -121,7 +105,7 @@ func (p *TransIPProvider) ApplyChanges(changes *plan.Changes) error { for _, ep := range changes.Create { log.WithFields(log.Fields{"record": ep.DNSName, "type": ep.RecordType}).Info("endpoint is missing") - zone, err := zoneForZoneName(ep.DNSName, zoneNameMapper, zonesByName) + zone, err := p.zoneForZoneName(ep.DNSName, zoneNameMapper, zonesByName) if err != nil { log.Errorf("could not find zone for %s: %s", ep.DNSName, err.Error()) continue @@ -142,7 +126,7 @@ func (p *TransIPProvider) ApplyChanges(changes *plan.Changes) error { for _, ep := range changes.UpdateNew { log.WithFields(log.Fields{"record": ep.DNSName, "type": ep.RecordType}).Debug("needs updating") - zone, err := zoneForZoneName(ep.DNSName, zoneNameMapper, zonesByName) + zone, err := p.zoneForZoneName(ep.DNSName, zoneNameMapper, zonesByName) if err != nil { log.WithFields(log.Fields{"record": ep.DNSName}).Warn(err.Error()) continue @@ -371,3 +355,19 @@ func (p *TransIPProvider) addEndpointToEntries(ep *endpoint.Endpoint, zone trans return entries } + +// zoneForZoneName returns the zone mapped to given name or error if zone could +// not be found +func (p *TransIPProvider) zoneForZoneName(name string, m zoneIDName, z map[string]transip.Domain) (transip.Domain, error) { + _, zoneName := m.FindZone(name) + if zoneName == "" { + return transip.Domain{}, fmt.Errorf("could not find zoneName for %s", name) + } + + zone, ok := z[zoneName] + if !ok { + return zone, fmt.Errorf("could not find zone for %s", zoneName) + } + + return zone, nil +} From 55612a2fcdb823333170824e3b34510f49d00b39 Mon Sep 17 00:00:00 2001 From: Reinier Schoof Date: Tue, 7 May 2019 12:51:39 +0200 Subject: [PATCH 128/136] added test for TransIP provider flags --- pkg/apis/externaldns/types_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index ab474b498..459afce1c 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -82,6 +82,8 @@ var ( CRDSourceAPIVersion: "externaldns.k8s.io/v1alpha1", CRDSourceKind: "DNSEndpoint", RcodezeroTXTEncrypt: false, + TransIPAccountName: "", + TransIPPrivateKeyFile: "", } overriddenConfig = &Config{ @@ -143,6 +145,8 @@ var ( CRDSourceAPIVersion: "test.k8s.io/v1alpha1", CRDSourceKind: "Endpoint", RcodezeroTXTEncrypt: true, + TransIPAccountName: "transip", + TransIPPrivateKeyFile: "/path/to/transip.key", } // minimal config with istio gateway source and multiple ingressgateway load balancer services @@ -284,6 +288,8 @@ func TestParseFlags(t *testing.T) { "--crd-source-apiversion=test.k8s.io/v1alpha1", "--crd-source-kind=Endpoint", "--rcodezero-txt-encrypt", + "--transip-account=transip", + "--transip-keyfile=/path/to/transip.key", }, envVars: map[string]string{}, expected: overriddenConfig, @@ -349,6 +355,8 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_CRD_SOURCE_APIVERSION": "test.k8s.io/v1alpha1", "EXTERNAL_DNS_CRD_SOURCE_KIND": "Endpoint", "EXTERNAL_DNS_RCODEZERO_TXT_ENCRYPT": "1", + "EXTERNAL_DNS_TRANSIP_ACCOUNT": "transip", + "EXTERNAL_DNS_TRANSIP_KEYFILE": "/path/to/transip.key", }, expected: overriddenConfig, }, From fab942f486bd21c81b4dceb1c7fb8d831c782baa Mon Sep 17 00:00:00 2001 From: Michael Fraenkel Date: Wed, 24 Apr 2019 21:18:26 -0400 Subject: [PATCH 129/136] Cache the endpoints on the controller loop The controller will retrieve all the endpoints at the beginning of its loop. When changes need to be applied, the provider may need to query the endpoints again. Allow the provider to skip the queries if its data was cached. --- controller/controller.go | 6 +- controller/controller_test.go | 7 +- provider/alibaba_cloud.go | 3 +- provider/alibaba_cloud_test.go | 5 +- provider/aws.go | 13 ++- provider/aws_sd.go | 3 +- provider/aws_sd_test.go | 5 +- provider/aws_test.go | 160 +++++++++++++++++-------------- provider/azure.go | 3 +- provider/azure_test.go | 3 +- provider/cloudflare.go | 2 +- provider/cloudflare_test.go | 4 +- provider/coredns.go | 2 +- provider/coredns_test.go | 5 +- provider/designate.go | 3 +- provider/designate_test.go | 7 +- provider/digital_ocean.go | 3 +- provider/digital_ocean_test.go | 2 +- provider/dnsimple.go | 3 +- provider/dnsimple_test.go | 5 +- provider/dyn.go | 3 +- provider/exoscale.go | 3 +- provider/exoscale_test.go | 3 +- provider/google.go | 3 +- provider/google_test.go | 6 +- provider/infoblox.go | 3 +- provider/infoblox_test.go | 3 +- provider/inmemory.go | 15 +-- provider/inmemory_test.go | 3 +- provider/linode.go | 2 +- provider/linode_test.go | 8 +- provider/ns1.go | 3 +- provider/ns1_test.go | 5 +- provider/oci.go | 3 +- provider/oci_test.go | 2 +- provider/pdns.go | 2 +- provider/provider.go | 14 ++- provider/rcode0.go | 3 +- provider/rcode0_test.go | 3 +- provider/rfc2136.go | 3 +- provider/rfc2136_test.go | 3 +- provider/transip.go | 3 +- registry/aws_sd_registry.go | 5 +- registry/aws_sd_registry_test.go | 5 +- registry/noop.go | 6 +- registry/noop_test.go | 10 +- registry/registry.go | 4 +- registry/txt.go | 9 +- registry/txt_test.go | 29 ++++-- 49 files changed, 252 insertions(+), 156 deletions(-) diff --git a/controller/controller.go b/controller/controller.go index 769c6553a..dad7f3082 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -17,12 +17,14 @@ limitations under the License. package controller import ( + "context" "time" "github.com/prometheus/client_golang/prometheus" log "github.com/sirupsen/logrus" "github.com/kubernetes-incubator/external-dns/plan" + "github.com/kubernetes-incubator/external-dns/provider" "github.com/kubernetes-incubator/external-dns/registry" "github.com/kubernetes-incubator/external-dns/source" ) @@ -89,6 +91,8 @@ func (c *Controller) RunOnce() error { } registryEndpointsTotal.Set(float64(len(records))) + ctx := context.WithValue(context.Background(), provider.RecordsContextKey, records) + endpoints, err := c.Source.Endpoints() if err != nil { sourceErrors.Inc() @@ -104,7 +108,7 @@ func (c *Controller) RunOnce() error { plan = plan.Calculate() - err = c.Registry.ApplyChanges(plan.Changes) + err = c.Registry.ApplyChanges(ctx, plan.Changes) if err != nil { registryErrors.Inc() return err diff --git a/controller/controller_test.go b/controller/controller_test.go index 909c33a78..1cf68bdfd 100644 --- a/controller/controller_test.go +++ b/controller/controller_test.go @@ -17,7 +17,9 @@ limitations under the License. package controller import ( + "context" "errors" + "reflect" "testing" "github.com/kubernetes-incubator/external-dns/endpoint" @@ -42,7 +44,7 @@ func (p *mockProvider) Records() ([]*endpoint.Endpoint, error) { } // ApplyChanges validates that the passed in changes satisfy the assumtions. -func (p *mockProvider) ApplyChanges(changes *plan.Changes) error { +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") } @@ -71,6 +73,9 @@ func (p *mockProvider) ApplyChanges(changes *plan.Changes) error { } } + if !reflect.DeepEqual(ctx.Value(provider.RecordsContextKey), p.RecordsStore) { + return errors.New("context is wrong") + } return nil } diff --git a/provider/alibaba_cloud.go b/provider/alibaba_cloud.go index a0b66734b..6a06949ce 100644 --- a/provider/alibaba_cloud.go +++ b/provider/alibaba_cloud.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "fmt" "io/ioutil" "strings" @@ -291,7 +292,7 @@ func (p *AlibabaCloudProvider) Records() (endpoints []*endpoint.Endpoint, err er // ApplyChanges applies the given changes. // // Returns nil if the operation was successful or an error if the operation failed. -func (p *AlibabaCloudProvider) ApplyChanges(changes *plan.Changes) error { +func (p *AlibabaCloudProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { if changes == nil || len(changes.Create)+len(changes.Delete)+len(changes.UpdateNew) == 0 { // No op return nil diff --git a/provider/alibaba_cloud_test.go b/provider/alibaba_cloud_test.go index defbfad1b..4e86dc537 100644 --- a/provider/alibaba_cloud_test.go +++ b/provider/alibaba_cloud_test.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "testing" "github.com/aliyun/alibaba-cloud-sdk-go/services/alidns" @@ -301,7 +302,7 @@ func TestAlibabaCloudProvider_ApplyChanges(t *testing.T) { }, }, } - p.ApplyChanges(&changes) + p.ApplyChanges(context.Background(), &changes) endpoints, err := p.Records() if err != nil { t.Errorf("Failed to get records: %v", err) @@ -358,7 +359,7 @@ func TestAlibabaCloudProvider_ApplyChanges_PrivateZone(t *testing.T) { }, }, } - p.ApplyChanges(&changes) + p.ApplyChanges(context.Background(), &changes) endpoints, err := p.Records() if err != nil { t.Errorf("Failed to get records: %v", err) diff --git a/provider/aws.go b/provider/aws.go index 37d758768..baabba378 100644 --- a/provider/aws.go +++ b/provider/aws.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "fmt" "sort" "strings" @@ -319,15 +320,19 @@ func (p *AWSProvider) doRecords(action string, endpoints []*endpoint.Endpoint) e } // ApplyChanges applies a given set of changes in a given zone. -func (p *AWSProvider) ApplyChanges(changes *plan.Changes) error { +func (p *AWSProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { zones, err := p.Zones() if err != nil { return err } - records, err := p.records(zones) - if err != nil { - log.Errorf("getting records failed: %v", err) + records, ok := ctx.Value(RecordsContextKey).([]*endpoint.Endpoint) + if !ok { + var err error + records, err = p.records(zones) + if err != nil { + log.Errorf("getting records failed: %v", err) + } } combinedChanges := make([]*route53.Change, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete)) diff --git a/provider/aws_sd.go b/provider/aws_sd.go index 2bd74a132..8f921c7ef 100644 --- a/provider/aws_sd.go +++ b/provider/aws_sd.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "strings" "crypto/sha256" @@ -193,7 +194,7 @@ func (p *AWSSDProvider) instancesToEndpoint(ns *sd.NamespaceSummary, srv *sd.Ser } // ApplyChanges applies Kubernetes changes in endpoints to AWS API -func (p *AWSSDProvider) ApplyChanges(changes *plan.Changes) error { +func (p *AWSSDProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { // return early if there is nothing to change if len(changes.Create) == 0 && len(changes.Delete) == 0 && len(changes.UpdateNew) == 0 { log.Info("All records are already up to date") diff --git a/provider/aws_sd_test.go b/provider/aws_sd_test.go index c567094fb..f25130fc9 100644 --- a/provider/aws_sd_test.go +++ b/provider/aws_sd_test.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "errors" "math/rand" "reflect" @@ -316,7 +317,7 @@ func TestAWSSDProvider_ApplyChanges(t *testing.T) { provider := newTestAWSSDProvider(api, NewDomainFilter([]string{}), "") // apply creates - provider.ApplyChanges(&plan.Changes{ + provider.ApplyChanges(context.Background(), &plan.Changes{ Create: expectedEndpoints, }) @@ -332,7 +333,7 @@ func TestAWSSDProvider_ApplyChanges(t *testing.T) { assert.True(t, testutils.SameEndpoints(expectedEndpoints, endpoints), "expected and actual endpoints don't match, expected=%v, actual=%v", expectedEndpoints, endpoints) // apply deletes - provider.ApplyChanges(&plan.Changes{ + provider.ApplyChanges(context.Background(), &plan.Changes{ Delete: expectedEndpoints, }) diff --git a/provider/aws_test.go b/provider/aws_test.go index 2dcec4d80..8802054c7 100644 --- a/provider/aws_test.go +++ b/provider/aws_test.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "fmt" "net" "sort" @@ -412,79 +413,96 @@ func TestAWSDeleteRecords(t *testing.T) { } func TestAWSApplyChanges(t *testing.T) { - provider, _ := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneIDFilter([]string{}), 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-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"), - }) - - createRecords := []*endpoint.Endpoint{ - endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.8.8"), - endpoint.NewEndpoint("create-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.4.4"), - 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"), + tests := []struct { + name string + setup func(p *AWSProvider) context.Context + listRRSets int + }{ + {"no cache", func(p *AWSProvider) context.Context { return context.Background() }, 3}, + {"cached", func(p *AWSProvider) context.Context { + records, err := p.Records() + require.NoError(t, err) + return context.WithValue(context.Background(), RecordsContextKey, records) + }, 0}, } - currentRecords := []*endpoint.Endpoint{ - endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.8.8"), - endpoint.NewEndpoint("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.4.4"), - 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"), + for _, tt := range tests { + provider, _ := newAWSProvider(t, NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), NewZoneIDFilter([]string{}), 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-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"), + }) + + createRecords := []*endpoint.Endpoint{ + endpoint.NewEndpoint("create-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.8.8"), + endpoint.NewEndpoint("create-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.4.4"), + 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"), + } + + currentRecords := []*endpoint.Endpoint{ + endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.8.8"), + endpoint.NewEndpoint("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.4.4"), + 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"), + } + updatedRecords := []*endpoint.Endpoint{ + endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4"), + endpoint.NewEndpoint("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "4.3.2.1"), + 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"), + } + + deleteRecords := []*endpoint.Endpoint{ + endpoint.NewEndpoint("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.8.8"), + endpoint.NewEndpoint("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.4.4"), + 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"), + } + + changes := &plan.Changes{ + Create: createRecords, + UpdateNew: updatedRecords, + UpdateOld: currentRecords, + Delete: deleteRecords, + } + + ctx := tt.setup(provider) + + counter := NewRoute53APICounter(provider.client) + provider.client = counter + require.NoError(t, provider.ApplyChanges(ctx, changes)) + + assert.Equal(t, 1, counter.calls["ListHostedZonesPages"], tt.name) + assert.Equal(t, tt.listRRSets, counter.calls["ListResourceRecordSetsPages"], tt.name) + + records, err := provider.Records() + 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("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"), + }) } - updatedRecords := []*endpoint.Endpoint{ - endpoint.NewEndpoint("update-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "1.2.3.4"), - endpoint.NewEndpoint("update-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "4.3.2.1"), - 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"), - } - - deleteRecords := []*endpoint.Endpoint{ - endpoint.NewEndpoint("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.8.8"), - endpoint.NewEndpoint("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, "8.8.4.4"), - 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"), - } - - changes := &plan.Changes{ - Create: createRecords, - UpdateNew: updatedRecords, - UpdateOld: currentRecords, - Delete: deleteRecords, - } - - counter := NewRoute53APICounter(provider.client) - provider.client = counter - require.NoError(t, provider.ApplyChanges(changes)) - - assert.Equal(t, 1, counter.calls["ListHostedZonesPages"]) - assert.Equal(t, 3, counter.calls["ListResourceRecordSetsPages"]) - - records, err := provider.Records() - 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), "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("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"), - }) } func TestAWSApplyChangesDryRun(t *testing.T) { @@ -541,7 +559,7 @@ func TestAWSApplyChangesDryRun(t *testing.T) { Delete: deleteRecords, } - require.NoError(t, provider.ApplyChanges(changes)) + require.NoError(t, provider.ApplyChanges(context.Background(), changes)) records, err := provider.Records() require.NoError(t, err) diff --git a/provider/azure.go b/provider/azure.go index 900262048..3f887e555 100644 --- a/provider/azure.go +++ b/provider/azure.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "fmt" "io/ioutil" "strings" @@ -209,7 +210,7 @@ func (p *AzureProvider) Records() (endpoints []*endpoint.Endpoint, _ error) { // ApplyChanges applies the given changes. // // Returns nil if the operation was successful or an error if the operation failed. -func (p *AzureProvider) ApplyChanges(changes *plan.Changes) error { +func (p *AzureProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { zones, err := p.zones() if err != nil { return err diff --git a/provider/azure_test.go b/provider/azure_test.go index 37e5fb138..36d697661 100644 --- a/provider/azure_test.go +++ b/provider/azure_test.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "testing" "github.com/Azure/azure-sdk-for-go/arm/dns" @@ -344,7 +345,7 @@ func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordsClie Delete: deleteRecords, } - if err := provider.ApplyChanges(changes); err != nil { + if err := provider.ApplyChanges(context.Background(), changes); err != nil { t.Fatal(err) } } diff --git a/provider/cloudflare.go b/provider/cloudflare.go index a7705d0bd..85ff411c6 100644 --- a/provider/cloudflare.go +++ b/provider/cloudflare.go @@ -192,7 +192,7 @@ func (p *CloudFlareProvider) Records() ([]*endpoint.Endpoint, error) { } // ApplyChanges applies a given set of changes in a given zone. -func (p *CloudFlareProvider) ApplyChanges(changes *plan.Changes) error { +func (p *CloudFlareProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { proxiedByDefault := p.proxiedByDefault combinedChanges := make([]*cloudFlareChange, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete)) diff --git a/provider/cloudflare_test.go b/provider/cloudflare_test.go index e67d4a7bd..73e32d59a 100644 --- a/provider/cloudflare_test.go +++ b/provider/cloudflare_test.go @@ -542,7 +542,7 @@ func TestApplyChanges(t *testing.T) { changes.Delete = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.zalando.to.", Targets: endpoint.Targets{"target"}}} changes.UpdateOld = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.zalando.to.", Targets: endpoint.Targets{"target-old"}}} changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.zalando.to.", Targets: endpoint.Targets{"target-new"}}} - err := provider.ApplyChanges(changes) + err := provider.ApplyChanges(context.Background(), changes) if err != nil { t.Errorf("should not fail, %s", err) } @@ -553,7 +553,7 @@ func TestApplyChanges(t *testing.T) { changes.UpdateOld = []*endpoint.Endpoint{} changes.UpdateNew = []*endpoint.Endpoint{} - err = provider.ApplyChanges(changes) + err = provider.ApplyChanges(context.Background(), changes) if err != nil { t.Errorf("should not fail, %s", err) } diff --git a/provider/coredns.go b/provider/coredns.go index 4e78bdbf4..bd09f40a1 100644 --- a/provider/coredns.go +++ b/provider/coredns.go @@ -298,7 +298,7 @@ func (p coreDNSProvider) Records() ([]*endpoint.Endpoint, error) { } // ApplyChanges stores changes back to etcd converting them to CoreDNS format and aggregating A/CNAME and TXT records -func (p coreDNSProvider) ApplyChanges(changes *plan.Changes) error { +func (p coreDNSProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { grouped := map[string][]*endpoint.Endpoint{} for _, ep := range changes.Create { grouped[ep.DNSName] = append(grouped[ep.DNSName], ep) diff --git a/provider/coredns_test.go b/provider/coredns_test.go index 147711743..698207a0d 100644 --- a/provider/coredns_test.go +++ b/provider/coredns_test.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "strings" "testing" @@ -227,7 +228,7 @@ func TestCoreDNSApplyChanges(t *testing.T) { endpoint.NewEndpoint("domain2.local", endpoint.RecordTypeCNAME, "site.local"), }, } - coredns.ApplyChanges(changes1) + coredns.ApplyChanges(context.Background(), changes1) expectedServices1 := map[string]*Service{ "/skydns/local/domain1": {Host: "5.5.5.5", Text: "string1"}, @@ -285,7 +286,7 @@ func applyServiceChanges(provider coreDNSProvider, changes *plan.Changes) { } } } - provider.ApplyChanges(changes) + provider.ApplyChanges(context.Background(), changes) } func validateServices(services, expectedServices map[string]*Service, t *testing.T, step int) { diff --git a/provider/designate.go b/provider/designate.go index 48e8ce8a9..50ac157b7 100644 --- a/provider/designate.go +++ b/provider/designate.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "fmt" "net" "net/http" @@ -379,7 +380,7 @@ func addEndpoint(ep *endpoint.Endpoint, recordSets map[string]*recordSet, delete } // ApplyChanges applies a given set of changes in a given zone. -func (p designateProvider) ApplyChanges(changes *plan.Changes) error { +func (p designateProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { managedZones, err := p.getZones() if err != nil { return err diff --git a/provider/designate_test.go b/provider/designate_test.go index db060b92f..3753ed303 100644 --- a/provider/designate_test.go +++ b/provider/designate_test.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "encoding/pem" "fmt" "io/ioutil" @@ -407,7 +408,7 @@ func testDesignateCreateRecords(t *testing.T, client *fakeDesignateClient) []*re expectedCopy := make([]*recordsets.RecordSet, len(expected)) copy(expectedCopy, expected) - err := client.ToProvider().ApplyChanges(&plan.Changes{Create: endpoints}) + err := client.ToProvider().ApplyChanges(context.Background(), &plan.Changes{Create: endpoints}) if err != nil { t.Fatal(err) } @@ -495,7 +496,7 @@ func testDesignateUpdateRecords(t *testing.T, client *fakeDesignateClient) []*re expected[2].Records = []string{"10.3.3.1"} expected[3].Records = []string{"10.2.1.1", "10.3.3.2"} - err := client.ToProvider().ApplyChanges(&plan.Changes{UpdateOld: updatesOld, UpdateNew: updatesNew}) + err := client.ToProvider().ApplyChanges(context.Background(), &plan.Changes{UpdateOld: updatesOld, UpdateNew: updatesNew}) if err != nil { t.Fatal(err) } @@ -553,7 +554,7 @@ func testDesignateDeleteRecords(t *testing.T, client *fakeDesignateClient) { expected[3].Records = []string{"10.3.3.2"} expected = expected[1:] - err := client.ToProvider().ApplyChanges(&plan.Changes{Delete: deletes}) + err := client.ToProvider().ApplyChanges(context.Background(), &plan.Changes{Delete: deletes}) if err != nil { t.Fatal(err) } diff --git a/provider/digital_ocean.go b/provider/digital_ocean.go index 590fa5e4b..00daf60fd 100644 --- a/provider/digital_ocean.go +++ b/provider/digital_ocean.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + goctx "context" "fmt" "os" "strings" @@ -261,7 +262,7 @@ func (p *DigitalOceanProvider) submitChanges(changes []*DigitalOceanChange) erro } // ApplyChanges applies a given set of changes in a given zone. -func (p *DigitalOceanProvider) ApplyChanges(changes *plan.Changes) error { +func (p *DigitalOceanProvider) ApplyChanges(ctx goctx.Context, changes *plan.Changes) error { combinedChanges := make([]*DigitalOceanChange, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete)) combinedChanges = append(combinedChanges, newDigitalOceanChanges(DigitalOceanCreate, changes.Create)...) diff --git a/provider/digital_ocean_test.go b/provider/digital_ocean_test.go index 1ce4a6dac..ab28da215 100644 --- a/provider/digital_ocean_test.go +++ b/provider/digital_ocean_test.go @@ -438,7 +438,7 @@ func TestDigitalOceanApplyChanges(t *testing.T) { changes.Delete = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.bar.com", Targets: endpoint.Targets{"target"}}} changes.UpdateOld = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.bar.de", Targets: endpoint.Targets{"target-old"}}} changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.foo.com", Targets: endpoint.Targets{"target-new"}, RecordType: "CNAME", RecordTTL: 100}} - err := provider.ApplyChanges(changes) + err := provider.ApplyChanges(context.Background(), changes) if err != nil { t.Errorf("should not fail, %s", err) } diff --git a/provider/dnsimple.go b/provider/dnsimple.go index 08d9e5347..2bccfe435 100644 --- a/provider/dnsimple.go +++ b/provider/dnsimple.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "fmt" "os" "strconv" @@ -332,7 +333,7 @@ func (p *dnsimpleProvider) UpdateRecords(endpoints []*endpoint.Endpoint) error { } // ApplyChanges applies a given set of changes -func (p *dnsimpleProvider) ApplyChanges(changes *plan.Changes) error { +func (p *dnsimpleProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { combinedChanges := make([]*dnsimpleChange, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete)) combinedChanges = append(combinedChanges, newDnsimpleChanges(dnsimpleCreate, changes.Create)...) diff --git a/provider/dnsimple_test.go b/provider/dnsimple_test.go index b6182f535..1f30da028 100644 --- a/provider/dnsimple_test.go +++ b/provider/dnsimple_test.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "fmt" "os" "testing" @@ -172,7 +173,7 @@ func testDnsimpleProviderApplyChanges(t *testing.T) { } mockProvider.accountID = "1" - err := mockProvider.ApplyChanges(changes) + err := mockProvider.ApplyChanges(context.Background(), changes) if err != nil { t.Errorf("Failed to apply changes: %v", err) } @@ -185,7 +186,7 @@ func testDnsimpleProviderApplyChangesSkipsUnknown(t *testing.T) { } mockProvider.accountID = "1" - err := mockProvider.ApplyChanges(changes) + err := mockProvider.ApplyChanges(context.Background(), changes) if err != nil { t.Errorf("Failed to ignore unknown zones: %v", err) } diff --git a/provider/dyn.go b/provider/dyn.go index e34999803..52f47ac34 100644 --- a/provider/dyn.go +++ b/provider/dyn.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "fmt" "os" "strconv" @@ -637,7 +638,7 @@ func (d *dynProviderState) Records() ([]*endpoint.Endpoint, error) { // this method does C + 2*Z requests: C=total number of changes, Z = number of // affected zones (1 login + 1 commit) -func (d *dynProviderState) ApplyChanges(changes *plan.Changes) error { +func (d *dynProviderState) ApplyChanges(ctx context.Context, changes *plan.Changes) error { log.Debugf("Processing chages: %+v", changes) if d.DryRun { diff --git a/provider/exoscale.go b/provider/exoscale.go index 4c909c1b3..be9fd8316 100644 --- a/provider/exoscale.go +++ b/provider/exoscale.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "strings" "github.com/exoscale/egoscale" @@ -81,7 +82,7 @@ func (ep *ExoscaleProvider) getZones() (map[int64]string, error) { } // ApplyChanges simply modifies DNS via exoscale API -func (ep *ExoscaleProvider) ApplyChanges(changes *plan.Changes) error { +func (ep *ExoscaleProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { ep.OnApplyChanges(changes) if ep.dryRun { diff --git a/provider/exoscale_test.go b/provider/exoscale_test.go index 4c0c5bcbd..639040ffa 100644 --- a/provider/exoscale_test.go +++ b/provider/exoscale_test.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "strings" "testing" @@ -173,7 +174,7 @@ func TestExoscaleApplyChanges(t *testing.T) { createExoscale = make([]createRecordExoscale, 0) deleteExoscale = make([]deleteRecordExoscale, 0) - provider.ApplyChanges(plan) + provider.ApplyChanges(context.Background(), plan) assert.Equal(t, 1, len(createExoscale)) assert.Equal(t, "foo.com", createExoscale[0].name) diff --git a/provider/google.go b/provider/google.go index 94d933349..67033fc72 100644 --- a/provider/google.go +++ b/provider/google.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + goctx "context" "fmt" "strings" @@ -247,7 +248,7 @@ func (p *GoogleProvider) DeleteRecords(endpoints []*endpoint.Endpoint) error { } // ApplyChanges applies a given set of changes in a given zone. -func (p *GoogleProvider) ApplyChanges(changes *plan.Changes) error { +func (p *GoogleProvider) ApplyChanges(ctx goctx.Context, changes *plan.Changes) error { change := &dns.Change{} change.Additions = append(change.Additions, p.newFilteredRecords(changes.Create)...) diff --git a/provider/google_test.go b/provider/google_test.go index 8f7ea9162..f4f15b5ab 100644 --- a/provider/google_test.go +++ b/provider/google_test.go @@ -387,7 +387,7 @@ func TestGoogleApplyChanges(t *testing.T) { Delete: deleteRecords, } - require.NoError(t, provider.ApplyChanges(changes)) + require.NoError(t, provider.ApplyChanges(context.Background(), changes)) records, err := provider.Records() require.NoError(t, err) @@ -444,7 +444,7 @@ func TestGoogleApplyChangesDryRun(t *testing.T) { Delete: deleteRecords, } - require.NoError(t, provider.ApplyChanges(changes)) + require.NoError(t, provider.ApplyChanges(context.Background(), changes)) records, err := provider.Records() require.NoError(t, err) @@ -454,7 +454,7 @@ func TestGoogleApplyChangesDryRun(t *testing.T) { func TestGoogleApplyChangesEmpty(t *testing.T) { provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), NewZoneIDFilter([]string{""}), false, []*endpoint.Endpoint{}) - assert.NoError(t, provider.ApplyChanges(&plan.Changes{})) + assert.NoError(t, provider.ApplyChanges(context.Background(), &plan.Changes{})) } func TestNewFilteredRecords(t *testing.T) { diff --git a/provider/infoblox.go b/provider/infoblox.go index 5d2038061..667b9f92c 100644 --- a/provider/infoblox.go +++ b/provider/infoblox.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "fmt" "os" "strconv" @@ -177,7 +178,7 @@ func (p *InfobloxProvider) Records() (endpoints []*endpoint.Endpoint, err error) } // ApplyChanges applies the given changes. -func (p *InfobloxProvider) ApplyChanges(changes *plan.Changes) error { +func (p *InfobloxProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { zones, err := p.zones() if err != nil { return err diff --git a/provider/infoblox_test.go b/provider/infoblox_test.go index 6ae80eb92..dc3bd9865 100644 --- a/provider/infoblox_test.go +++ b/provider/infoblox_test.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "encoding/base64" "fmt" "regexp" @@ -469,7 +470,7 @@ func testInfobloxApplyChangesInternal(t *testing.T, dryRun bool, client ibclient Delete: deleteRecords, } - if err := provider.ApplyChanges(changes); err != nil { + if err := provider.ApplyChanges(context.Background(), changes); err != nil { t.Fatal(err) } } diff --git a/provider/inmemory.go b/provider/inmemory.go index 1cf6c5662..790f4c470 100644 --- a/provider/inmemory.go +++ b/provider/inmemory.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "errors" "strings" @@ -45,7 +46,7 @@ type InMemoryProvider struct { domain DomainFilter client *inMemoryClient filter *filter - OnApplyChanges func(changes *plan.Changes) + OnApplyChanges func(ctx context.Context, changes *plan.Changes) OnRecords func() } @@ -55,7 +56,7 @@ type InMemoryOption func(*InMemoryProvider) // InMemoryWithLogging injects logging when ApplyChanges is called func InMemoryWithLogging() InMemoryOption { return func(p *InMemoryProvider) { - p.OnApplyChanges = func(changes *plan.Changes) { + p.OnApplyChanges = func(ctx context.Context, changes *plan.Changes) { for _, v := range changes.Create { log.Infof("CREATE: %v", v) } @@ -94,7 +95,7 @@ func InMemoryInitZones(zones []string) InMemoryOption { func NewInMemoryProvider(opts ...InMemoryOption) *InMemoryProvider { im := &InMemoryProvider{ filter: &filter{}, - OnApplyChanges: func(changes *plan.Changes) {}, + OnApplyChanges: func(ctx context.Context, changes *plan.Changes) {}, OnRecords: func() {}, domain: NewDomainFilter([]string{""}), client: newInMemoryClient(), @@ -142,8 +143,8 @@ func (im *InMemoryProvider) Records() ([]*endpoint.Endpoint, error) { // create record - record should not exist // update/delete record - record should exist // create/update/delete lists should not have overlapping records -func (im *InMemoryProvider) ApplyChanges(changes *plan.Changes) error { - defer im.OnApplyChanges(changes) +func (im *InMemoryProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { + defer im.OnApplyChanges(ctx, changes) perZoneChanges := map[string]*plan.Changes{} @@ -188,7 +189,7 @@ func (im *InMemoryProvider) ApplyChanges(changes *plan.Changes) error { UpdateOld: convertToInMemoryRecord(perZoneChanges[zoneID].UpdateOld), Delete: convertToInMemoryRecord(perZoneChanges[zoneID].Delete), } - err := im.client.ApplyChanges(zoneID, change) + err := im.client.ApplyChanges(ctx, zoneID, change) if err != nil { return err } @@ -293,7 +294,7 @@ func (c *inMemoryClient) CreateZone(zone string) error { return nil } -func (c *inMemoryClient) ApplyChanges(zoneID string, changes *inMemoryChange) error { +func (c *inMemoryClient) ApplyChanges(ctx context.Context, zoneID string, changes *inMemoryChange) error { if err := c.validateChangeBatch(zoneID, changes); err != nil { return err } diff --git a/provider/inmemory_test.go b/provider/inmemory_test.go index 4e6190838..0d61a9480 100644 --- a/provider/inmemory_test.go +++ b/provider/inmemory_test.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "testing" "github.com/kubernetes-incubator/external-dns/endpoint" @@ -773,7 +774,7 @@ func testInMemoryApplyChanges(t *testing.T) { c.zones = getInitData() im.client = c - err := im.ApplyChanges(ti.changes) + err := im.ApplyChanges(context.Background(), ti.changes) if ti.expectError { assert.Error(t, err) } else { diff --git a/provider/linode.go b/provider/linode.go index 7000f1961..05076133e 100644 --- a/provider/linode.go +++ b/provider/linode.go @@ -263,7 +263,7 @@ func getPriority() *int { } // ApplyChanges applies a given set of changes in a given zone. -func (p *LinodeProvider) ApplyChanges(changes *plan.Changes) error { +func (p *LinodeProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { recordsByZoneID := make(map[string][]*linodego.DomainRecord) zones, err := p.fetchZones() diff --git a/provider/linode_test.go b/provider/linode_test.go index 5b7169f05..9abf1950c 100644 --- a/provider/linode_test.go +++ b/provider/linode_test.go @@ -353,7 +353,7 @@ func TestLinodeApplyChanges(t *testing.T) { }, ).Return(&linodego.DomainRecord{}, nil).Once() - err := provider.ApplyChanges(&plan.Changes{ + err := provider.ApplyChanges(context.Background(), &plan.Changes{ Create: []*endpoint.Endpoint{{ DNSName: "create.bar.io", RecordType: "A", @@ -428,7 +428,7 @@ func TestLinodeApplyChangesTargetAdded(t *testing.T) { }, ).Return(&linodego.DomainRecord{}, nil).Once() - err := provider.ApplyChanges(&plan.Changes{ + err := provider.ApplyChanges(context.Background(), &plan.Changes{ // From 1 target to 2 UpdateNew: []*endpoint.Endpoint{{ DNSName: "example.com", @@ -484,7 +484,7 @@ func TestLinodeApplyChangesTargetRemoved(t *testing.T) { 11, ).Return(nil).Once() - err := provider.ApplyChanges(&plan.Changes{ + err := provider.ApplyChanges(context.Background(), &plan.Changes{ // From 2 targets to 1 UpdateNew: []*endpoint.Endpoint{{ DNSName: "example.com", @@ -521,7 +521,7 @@ func TestLinodeApplyChangesNoChanges(t *testing.T) { mock.Anything, ).Return([]*linodego.DomainRecord{{ID: 11, Name: "", Type: "A", Target: "targetA"}}, nil).Once() - err := provider.ApplyChanges(&plan.Changes{}) + err := provider.ApplyChanges(context.Background(), &plan.Changes{}) require.NoError(t, err) mockDomainClient.AssertExpectations(t) diff --git a/provider/ns1.go b/provider/ns1.go index 9ac7e2200..988481d38 100644 --- a/provider/ns1.go +++ b/provider/ns1.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "crypto/tls" "fmt" "net/http" @@ -271,7 +272,7 @@ type ns1Change struct { } // ApplyChanges applies a given set of changes in a given zone. -func (p *NS1Provider) ApplyChanges(changes *plan.Changes) error { +func (p *NS1Provider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { combinedChanges := make([]*ns1Change, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete)) combinedChanges = append(combinedChanges, newNS1Changes(ns1Create, changes.Create)...) diff --git a/provider/ns1_test.go b/provider/ns1_test.go index 7ff0aa9b5..6fc5ea823 100644 --- a/provider/ns1_test.go +++ b/provider/ns1_test.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "fmt" "net/http" "os" @@ -221,14 +222,14 @@ func TestNS1ApplyChanges(t *testing.T) { } changes.Delete = []*endpoint.Endpoint{{DNSName: "test.foo.com", Targets: endpoint.Targets{"target"}}} changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "test.foo.com", Targets: endpoint.Targets{"target-new"}}} - err := provider.ApplyChanges(changes) + err := provider.ApplyChanges(context.Background(), changes) require.NoError(t, err) // empty changes changes.Create = []*endpoint.Endpoint{} changes.Delete = []*endpoint.Endpoint{} changes.UpdateNew = []*endpoint.Endpoint{} - err = provider.ApplyChanges(changes) + err = provider.ApplyChanges(context.Background(), changes) require.NoError(t, err) } diff --git a/provider/oci.go b/provider/oci.go index aa0c03411..cb4f6c23b 100644 --- a/provider/oci.go +++ b/provider/oci.go @@ -201,7 +201,7 @@ func (p *OCIProvider) Records() ([]*endpoint.Endpoint, error) { } // ApplyChanges applies a given set of changes to a given zone. -func (p *OCIProvider) ApplyChanges(changes *plan.Changes) error { +func (p *OCIProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { log.Debugf("Processing chages: %+v", changes) ops := []dns.RecordOperation{} @@ -217,7 +217,6 @@ func (p *OCIProvider) ApplyChanges(changes *plan.Changes) error { return nil } - ctx := context.Background() zones, err := p.zones(ctx) if err != nil { return errors.Wrap(err, "fetching zones") diff --git a/provider/oci_test.go b/provider/oci_test.go index 89056812c..a4a476d96 100644 --- a/provider/oci_test.go +++ b/provider/oci_test.go @@ -829,7 +829,7 @@ func TestOCIApplyChanges(t *testing.T) { NewZoneIDFilter([]string{""}), tc.dryRun, ) - err := provider.ApplyChanges(tc.changes) + err := provider.ApplyChanges(context.Background(), tc.changes) require.Equal(t, tc.err, err) endpoints, err := provider.Records() require.NoError(t, err) diff --git a/provider/pdns.go b/provider/pdns.go index 8bf888daf..4622971f9 100644 --- a/provider/pdns.go +++ b/provider/pdns.go @@ -443,7 +443,7 @@ func (p *PDNSProvider) Records() (endpoints []*endpoint.Endpoint, _ error) { // ApplyChanges takes a list of changes (endpoints) and updates the PDNS server // by sending the correct HTTP PATCH requests to a matching zone -func (p *PDNSProvider) ApplyChanges(changes *plan.Changes) error { +func (p *PDNSProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { startTime := time.Now() diff --git a/provider/provider.go b/provider/provider.go index 23b54e1e2..7c6f1a61e 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "net" "strings" @@ -27,9 +28,20 @@ import ( // Provider defines the interface DNS providers should implement. type Provider interface { Records() ([]*endpoint.Endpoint, error) - ApplyChanges(changes *plan.Changes) error + ApplyChanges(ctx context.Context, changes *plan.Changes) error } +type contextKey struct { + name string +} + +func (k *contextKey) String() string { return "provider context value " + k.name } + +// RecordsContextKey is a context key. It can be used during ApplyChanges +// to access previously cached records. The associated value will be of +// type []*endpoint.Endpoint. +var RecordsContextKey = &contextKey{"records"} + // ensureTrailingDot ensures that the hostname receives a trailing dot if it hasn't already. func ensureTrailingDot(hostname string) string { if net.ParseIP(hostname) != nil { diff --git a/provider/rcode0.go b/provider/rcode0.go index 866dd735c..d9a0adcdb 100644 --- a/provider/rcode0.go +++ b/provider/rcode0.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "fmt" "net/url" "os" @@ -141,7 +142,7 @@ func (p *RcodeZeroProvider) Records() ([]*endpoint.Endpoint, error) { } // ApplyChanges applies a given set of changes in a given zone. -func (p *RcodeZeroProvider) ApplyChanges(changes *plan.Changes) error { +func (p *RcodeZeroProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { combinedChanges := make([]*rc0.RRSetChange, 0, len(changes.Create)+len(changes.UpdateNew)+len(changes.Delete)) diff --git a/provider/rcode0_test.go b/provider/rcode0_test.go index 9355a3733..904d2a8e6 100644 --- a/provider/rcode0_test.go +++ b/provider/rcode0_test.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "fmt" "os" "testing" @@ -102,7 +103,7 @@ func TestRcodeZeroProvider_ApplyChanges(t *testing.T) { changes := mockChanges() - err := provider.ApplyChanges(changes) + err := provider.ApplyChanges(context.Background(), changes) if err != nil { t.Errorf("should not fail, %s", err) diff --git a/provider/rfc2136.go b/provider/rfc2136.go index 0943864e2..8d90d2b10 100644 --- a/provider/rfc2136.go +++ b/provider/rfc2136.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "fmt" "net" "strconv" @@ -195,7 +196,7 @@ func (r rfc2136Provider) List() ([]dns.RR, error) { } // ApplyChanges applies a given set of changes in a given zone. -func (r rfc2136Provider) ApplyChanges(changes *plan.Changes) error { +func (r rfc2136Provider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { log.Debugf("ApplyChanges") for _, ep := range changes.Create { diff --git a/provider/rfc2136_test.go b/provider/rfc2136_test.go index cb9ef8a2b..061fed497 100644 --- a/provider/rfc2136_test.go +++ b/provider/rfc2136_test.go @@ -17,6 +17,7 @@ limitations under the License. package provider import ( + "context" "strings" "testing" @@ -149,7 +150,7 @@ func TestRfc2136ApplyChanges(t *testing.T) { }, } - err = provider.ApplyChanges(p) + err = provider.ApplyChanges(context.Background(), p) assert.NoError(t, err) assert.Equal(t, 2, len(stub.createMsgs)) diff --git a/provider/transip.go b/provider/transip.go index 5b8b7f8b8..1fff1daa2 100644 --- a/provider/transip.go +++ b/provider/transip.go @@ -1,6 +1,7 @@ package provider import ( + "context" "errors" "fmt" "strings" @@ -62,7 +63,7 @@ func NewTransIPProvider(accountName, privateKeyFile string, domainFilter DomainF } // ApplyChanges applies a given set of changes in a given zone. -func (p *TransIPProvider) ApplyChanges(changes *plan.Changes) error { +func (p *TransIPProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { // build zonefinder with all our zones so we can use FindZone // and a mapping of zones and their domain name zones, err := p.fetchZones() diff --git a/registry/aws_sd_registry.go b/registry/aws_sd_registry.go index 52c4b4271..64cd95b34 100644 --- a/registry/aws_sd_registry.go +++ b/registry/aws_sd_registry.go @@ -17,6 +17,7 @@ limitations under the License. package registry import ( + "context" "errors" "github.com/kubernetes-incubator/external-dns/endpoint" @@ -64,7 +65,7 @@ func (sdr *AWSSDRegistry) Records() ([]*endpoint.Endpoint, error) { // ApplyChanges filters out records not owned the External-DNS, additionally it adds the required label // inserted in the AWS SD instance as a CreateID field -func (sdr *AWSSDRegistry) ApplyChanges(changes *plan.Changes) error { +func (sdr *AWSSDRegistry) ApplyChanges(ctx context.Context, changes *plan.Changes) error { filteredChanges := &plan.Changes{ Create: changes.Create, UpdateNew: filterOwnedRecords(sdr.ownerID, changes.UpdateNew), @@ -77,7 +78,7 @@ func (sdr *AWSSDRegistry) ApplyChanges(changes *plan.Changes) error { sdr.updateLabels(filteredChanges.UpdateOld) sdr.updateLabels(filteredChanges.Delete) - return sdr.provider.ApplyChanges(filteredChanges) + return sdr.provider.ApplyChanges(ctx, filteredChanges) } func (sdr *AWSSDRegistry) updateLabels(endpoints []*endpoint.Endpoint) { diff --git a/registry/aws_sd_registry_test.go b/registry/aws_sd_registry_test.go index 938fec5c6..6ebda2e01 100644 --- a/registry/aws_sd_registry_test.go +++ b/registry/aws_sd_registry_test.go @@ -17,6 +17,7 @@ limitations under the License. package registry import ( + "context" "testing" "github.com/kubernetes-incubator/external-dns/endpoint" @@ -35,7 +36,7 @@ func (p *inMemoryProvider) Records() ([]*endpoint.Endpoint, error) { return p.endpoints, nil } -func (p *inMemoryProvider) ApplyChanges(changes *plan.Changes) error { +func (p *inMemoryProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { p.onApplyChanges(changes) return nil } @@ -151,7 +152,7 @@ func TestAWSSDRegistry_Records_ApplyChanges(t *testing.T) { r, err := NewAWSSDRegistry(p, "owner") require.NoError(t, err) - err = r.ApplyChanges(changes) + err = r.ApplyChanges(context.Background(), changes) require.NoError(t, err) } diff --git a/registry/noop.go b/registry/noop.go index aadc801a5..701f01c4e 100644 --- a/registry/noop.go +++ b/registry/noop.go @@ -17,6 +17,8 @@ limitations under the License. package registry import ( + "context" + "github.com/kubernetes-incubator/external-dns/endpoint" "github.com/kubernetes-incubator/external-dns/plan" "github.com/kubernetes-incubator/external-dns/provider" @@ -40,6 +42,6 @@ func (im *NoopRegistry) Records() ([]*endpoint.Endpoint, error) { } // ApplyChanges propagates changes to the dns provider -func (im *NoopRegistry) ApplyChanges(changes *plan.Changes) error { - return im.provider.ApplyChanges(changes) +func (im *NoopRegistry) ApplyChanges(ctx context.Context, changes *plan.Changes) error { + return im.provider.ApplyChanges(ctx, changes) } diff --git a/registry/noop_test.go b/registry/noop_test.go index d728fad65..c1688503c 100644 --- a/registry/noop_test.go +++ b/registry/noop_test.go @@ -17,6 +17,7 @@ limitations under the License. package registry import ( + "context" "testing" "github.com/kubernetes-incubator/external-dns/endpoint" @@ -53,7 +54,7 @@ func testNoopRecords(t *testing.T) { RecordType: endpoint.RecordTypeCNAME, }, } - p.ApplyChanges(&plan.Changes{ + p.ApplyChanges(context.Background(), &plan.Changes{ Create: providerRecords, }) @@ -88,13 +89,14 @@ func testNoopApplyChanges(t *testing.T) { }, } - p.ApplyChanges(&plan.Changes{ + ctx := context.Background() + p.ApplyChanges(ctx, &plan.Changes{ Create: providerRecords, }) // wrong changes r, _ := NewNoopRegistry(p) - err := r.ApplyChanges(&plan.Changes{ + err := r.ApplyChanges(ctx, &plan.Changes{ Create: []*endpoint.Endpoint{ { DNSName: "example.org", @@ -106,7 +108,7 @@ func testNoopApplyChanges(t *testing.T) { assert.EqualError(t, err, provider.ErrRecordAlreadyExists.Error()) //correct changes - require.NoError(t, r.ApplyChanges(&plan.Changes{ + require.NoError(t, r.ApplyChanges(ctx, &plan.Changes{ Create: []*endpoint.Endpoint{ { DNSName: "new-record.org", diff --git a/registry/registry.go b/registry/registry.go index 528d4ecff..71e926341 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -17,6 +17,8 @@ limitations under the License. package registry import ( + "context" + "github.com/kubernetes-incubator/external-dns/endpoint" "github.com/kubernetes-incubator/external-dns/plan" log "github.com/sirupsen/logrus" @@ -28,7 +30,7 @@ import ( // ApplyChanges(changes *plan.Changes) propagates the changes to the DNS Provider API and correspondingly updates ownership depending on type of registry being used type Registry interface { Records() ([]*endpoint.Endpoint, error) - ApplyChanges(changes *plan.Changes) error + ApplyChanges(ctx context.Context, changes *plan.Changes) error } //TODO(ideahitme): consider moving this to Plan diff --git a/registry/txt.go b/registry/txt.go index 528930850..5c5c74663 100644 --- a/registry/txt.go +++ b/registry/txt.go @@ -17,6 +17,7 @@ limitations under the License. package registry import ( + "context" "errors" "time" @@ -117,7 +118,7 @@ func (im *TXTRegistry) Records() ([]*endpoint.Endpoint, error) { // ApplyChanges updates dns provider with the changes // for each created/deleted record it will also take into account TXT records for creation/deletion -func (im *TXTRegistry) ApplyChanges(changes *plan.Changes) error { +func (im *TXTRegistry) ApplyChanges(ctx context.Context, changes *plan.Changes) error { filteredChanges := &plan.Changes{ Create: changes.Create, UpdateNew: filterOwnedRecords(im.ownerID, changes.UpdateNew), @@ -171,7 +172,11 @@ func (im *TXTRegistry) ApplyChanges(changes *plan.Changes) error { } } - return im.provider.ApplyChanges(filteredChanges) + // when caching is enabled, disable the provider from using the cache + if im.cacheInterval > 0 { + ctx = context.WithValue(ctx, provider.RecordsContextKey, nil) + } + return im.provider.ApplyChanges(ctx, filteredChanges) } /** diff --git a/registry/txt_test.go b/registry/txt_test.go index 6ead32165..4489fc864 100644 --- a/registry/txt_test.go +++ b/registry/txt_test.go @@ -17,6 +17,7 @@ limitations under the License. package registry import ( + "context" "reflect" "testing" "time" @@ -68,7 +69,7 @@ func testTXTRegistryRecords(t *testing.T) { func testTXTRegistryRecordsPrefixed(t *testing.T) { p := provider.NewInMemoryProvider() p.CreateZone(testZone) - p.ApplyChanges(&plan.Changes{ + p.ApplyChanges(context.Background(), &plan.Changes{ Create: []*endpoint.Endpoint{ newEndpointWithOwner("foo.test-zone.example.org", "foo.loadbalancer.com", endpoint.RecordTypeCNAME, ""), newEndpointWithOwner("bar.test-zone.example.org", "my-domain.com", endpoint.RecordTypeCNAME, ""), @@ -141,7 +142,7 @@ func testTXTRegistryRecordsPrefixed(t *testing.T) { func testTXTRegistryRecordsNoPrefix(t *testing.T) { p := provider.NewInMemoryProvider() p.CreateZone(testZone) - p.ApplyChanges(&plan.Changes{ + p.ApplyChanges(context.Background(), &plan.Changes{ Create: []*endpoint.Endpoint{ newEndpointWithOwner("foo.test-zone.example.org", "foo.loadbalancer.com", endpoint.RecordTypeCNAME, ""), newEndpointWithOwner("bar.test-zone.example.org", "my-domain.com", endpoint.RecordTypeCNAME, ""), @@ -220,7 +221,12 @@ func testTXTRegistryApplyChanges(t *testing.T) { func testTXTRegistryApplyChangesWithPrefix(t *testing.T) { p := provider.NewInMemoryProvider() p.CreateZone(testZone) - p.ApplyChanges(&plan.Changes{ + ctxEndpoints := []*endpoint.Endpoint{} + ctx := context.WithValue(context.Background(), provider.RecordsContextKey, ctxEndpoints) + p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { + assert.Equal(t, ctxEndpoints, ctx.Value(provider.RecordsContextKey)) + } + p.ApplyChanges(ctx, &plan.Changes{ Create: []*endpoint.Endpoint{ newEndpointWithOwner("foo.test-zone.example.org", "foo.loadbalancer.com", endpoint.RecordTypeCNAME, ""), newEndpointWithOwner("bar.test-zone.example.org", "my-domain.com", endpoint.RecordTypeCNAME, ""), @@ -267,7 +273,7 @@ func testTXTRegistryApplyChangesWithPrefix(t *testing.T) { newEndpointWithOwner("txt.tar.test-zone.example.org", "\"heritage=external-dns,external-dns/owner=owner\"", endpoint.RecordTypeTXT, ""), }, } - p.OnApplyChanges = func(got *plan.Changes) { + p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { mExpected := map[string][]*endpoint.Endpoint{ "Create": expected.Create, "UpdateNew": expected.UpdateNew, @@ -281,15 +287,21 @@ func testTXTRegistryApplyChangesWithPrefix(t *testing.T) { "Delete": got.Delete, } assert.True(t, testutils.SamePlanChanges(mGot, mExpected)) + assert.Equal(t, nil, ctx.Value(provider.RecordsContextKey)) } - err := r.ApplyChanges(changes) + err := r.ApplyChanges(ctx, changes) require.NoError(t, err) } func testTXTRegistryApplyChangesNoPrefix(t *testing.T) { p := provider.NewInMemoryProvider() p.CreateZone(testZone) - p.ApplyChanges(&plan.Changes{ + ctxEndpoints := []*endpoint.Endpoint{} + ctx := context.WithValue(context.Background(), provider.RecordsContextKey, ctxEndpoints) + p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { + assert.Equal(t, ctxEndpoints, ctx.Value(provider.RecordsContextKey)) + } + p.ApplyChanges(ctx, &plan.Changes{ Create: []*endpoint.Endpoint{ newEndpointWithOwner("foo.test-zone.example.org", "foo.loadbalancer.com", endpoint.RecordTypeCNAME, ""), newEndpointWithOwner("bar.test-zone.example.org", "my-domain.com", endpoint.RecordTypeCNAME, ""), @@ -330,7 +342,7 @@ func testTXTRegistryApplyChangesNoPrefix(t *testing.T) { UpdateNew: []*endpoint.Endpoint{}, UpdateOld: []*endpoint.Endpoint{}, } - p.OnApplyChanges = func(got *plan.Changes) { + p.OnApplyChanges = func(ctx context.Context, got *plan.Changes) { mExpected := map[string][]*endpoint.Endpoint{ "Create": expected.Create, "UpdateNew": expected.UpdateNew, @@ -344,8 +356,9 @@ func testTXTRegistryApplyChangesNoPrefix(t *testing.T) { "Delete": got.Delete, } assert.True(t, testutils.SamePlanChanges(mGot, mExpected)) + assert.Equal(t, nil, ctx.Value(provider.RecordsContextKey)) } - err := r.ApplyChanges(changes) + err := r.ApplyChanges(ctx, changes) require.NoError(t, err) } From b529a92d5bd8b8b7193e5d00c27cfeea11fed73b Mon Sep 17 00:00:00 2001 From: Dave Grizzanti Date: Sun, 31 Mar 2019 15:50:44 -0400 Subject: [PATCH 130/136] Add Cloud Foundry routes as a source --- main.go | 3 ++ pkg/apis/externaldns/types.go | 13 +++++++- source/route.go | 58 +++++++++++++++++++++++++++++++++++ source/store.go | 34 ++++++++++++++++++++ 4 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 source/route.go diff --git a/main.go b/main.go index fe8c5bb84..d707b645a 100644 --- a/main.go +++ b/main.go @@ -82,6 +82,9 @@ func main() { KubeMaster: cfg.Master, ServiceTypeFilter: cfg.ServiceTypeFilter, IstioIngressGatewayServices: cfg.IstioIngressGatewayServices, + CFAPIEndpoint: cfg.CFAPIEndpoint, + CFUsername: cfg.CFUsername, + CFPassword: cfg.CFPassword, } // 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 1050d67aa..9bd884375 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -105,6 +105,9 @@ type Config struct { CRDSourceAPIVersion string CRDSourceKind string ServiceTypeFilter []string + CFAPIEndpoint string + CFUsername string + CFPassword string RFC2136Host string RFC2136Port int RFC2136Zone string @@ -182,6 +185,9 @@ var defaultConfig = &Config{ CRDSourceAPIVersion: "externaldns.k8s.io/v1alpha1", CRDSourceKind: "DNSEndpoint", ServiceTypeFilter: []string{}, + CFAPIEndpoint: "", + CFUsername: "", + CFPassword: "" RFC2136Host: "", RFC2136Port: 0, RFC2136Zone: "", @@ -245,8 +251,13 @@ func (cfg *Config) ParseFlags(args []string) error { // Flags related to Istio app.Flag("istio-ingress-gateway", "The fully-qualified name of the Istio ingress gateway service. Flag can be specified multiple times (default: istio-system/istio-ingressgateway)").Default("istio-system/istio-ingressgateway").StringsVar(&cfg.IstioIngressGatewayServices) + // 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) + app.Flag("cf-username", "The username to log into the cloud foundry API").Default(defaultConfig.CFUsername).StringVar(&cfg.CFUsername) + app.Flag("cf-password", "The password to log into the cloud foundry API").Default(defaultConfig.CFPassword).StringVar(&cfg.CFPassword) + // Flags related to processing sources - app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, fake, connector, istio-gateway, crd").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "istio-gateway", "fake", "connector", "crd") + app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, fake, connector, istio-gateway, crd").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "istio-gateway", "route", "fake", "connector", "crd") 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("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) diff --git a/source/route.go b/source/route.go new file mode 100644 index 000000000..8b914634f --- /dev/null +++ b/source/route.go @@ -0,0 +1,58 @@ +/* +Copyright 2018 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 ( + "net/url" + + cfclient "github.com/cloudfoundry-community/go-cfclient" + "github.com/kubernetes-incubator/external-dns/endpoint" +) + +type routeSource struct { + client *cfclient.Client +} + +// NewRouteSource creates a new routeSource with the given config +func NewRouteSource(cfClient *cfclient.Client) (Source, error) { + return &routeSource{ + client: cfClient, + }, nil +} + +// Endpoints returns endpoint objects +func (rs *routeSource) Endpoints() ([]*endpoint.Endpoint, error) { + endpoints := []*endpoint.Endpoint{} + + u, err := url.Parse(rs.client.Config.ApiAddress) + if err != nil { + panic(err) + } + + domains, _ := rs.client.ListDomains() + for _, domain := range domains { + q := url.Values{} + q.Set("q", "domain_guid:"+domain.Guid) + routes, _ := rs.client.ListRoutesByQuery(q) + for _, element := range routes { + endpoints = append(endpoints, + endpoint.NewEndpointWithTTL(element.Host+"."+domain.Name, endpoint.RecordTypeCNAME, 300, u.Host)) + } + } + + return endpoints, nil +} diff --git a/source/store.go b/source/store.go index 4a143e13d..25baf2426 100644 --- a/source/store.go +++ b/source/store.go @@ -25,6 +25,7 @@ import ( "sync" + cfclient "github.com/cloudfoundry-community/go-cfclient" "github.com/linki/instrumented_http" log "github.com/sirupsen/logrus" istiocrd "istio.io/istio/pilot/pkg/config/kube/crd" @@ -53,12 +54,16 @@ type Config struct { KubeMaster string ServiceTypeFilter []string IstioIngressGatewayServices []string + CFAPIEndpoint string + CFUsername string + CFPassword string } // ClientGenerator provides clients type ClientGenerator interface { KubeClient() (kubernetes.Interface, error) IstioClient() (istiomodel.ConfigStore, error) + CloudFoundryClient(cfAPPEndpoint string, cfUsername string, cfPassword string) (*cfclient.Client, error) } // SingletonClientGenerator stores provider clients and guarantees that only one instance of client @@ -69,8 +74,10 @@ type SingletonClientGenerator struct { RequestTimeout time.Duration kubeClient kubernetes.Interface istioClient istiomodel.ConfigStore + cfClient *cfclient.Client kubeOnce sync.Once istioOnce sync.Once + cfOnce sync.Once } // KubeClient generates a kube client if it was not created before @@ -91,6 +98,27 @@ func (p *SingletonClientGenerator) IstioClient() (istiomodel.ConfigStore, error) return p.istioClient, err } +// CloudFoundryClient generates a cf client if it was not created before +func (p *SingletonClientGenerator) CloudFoundryClient(cfAPIEndpoint string, cfUsername string, cfPassword string) (*cfclient.Client, error) { + var err error + p.cfOnce.Do(func() { + p.cfClient, err = NewCFClient(cfAPIEndpoint, cfUsername, cfPassword) + }) + return p.cfClient, err +} + +// NewCFClient return a new CF client object. +func NewCFClient(cfAPIEndpoint string, cfUsername string, cfPassword string) (*cfclient.Client, error) { + c := &cfclient.Config{ + ApiAddress: "https://" + cfAPIEndpoint, + Username: cfUsername, + Password: cfPassword, + } + client, _ := cfclient.NewClient(c) + + return client, nil +} + // ByNames returns multiple Sources given multiple names. func ByNames(p ClientGenerator, names []string, cfg *Config) ([]Source, error) { sources := []Source{} @@ -130,6 +158,12 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err return nil, err } return NewIstioGatewaySource(kubernetesClient, istioClient, cfg.IstioIngressGatewayServices, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation) + case "route": + cfClient, err := p.CloudFoundryClient(cfg.CFAPIEndpoint, cfg.CFUsername, cfg.CFPassword) + if err != nil { + return nil, err + } + return NewRouteSource(cfClient) case "fake": return NewFakeSource(cfg.FQDNTemplate) case "connector": From b9b87113fa6ffdd51a0bcb52dbac8cf5f7221028 Mon Sep 17 00:00:00 2001 From: Dave Grizzanti Date: Tue, 2 Apr 2019 10:30:08 -0400 Subject: [PATCH 131/136] Add missing , --- 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 9bd884375..6f327184a 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -187,7 +187,7 @@ var defaultConfig = &Config{ ServiceTypeFilter: []string{}, CFAPIEndpoint: "", CFUsername: "", - CFPassword: "" + CFPassword: "", RFC2136Host: "", RFC2136Port: 0, RFC2136Zone: "", From 81a3fde458a2a8e9335c165e03b015fb2607fd67 Mon Sep 17 00:00:00 2001 From: Dave Grizzanti Date: Tue, 2 Apr 2019 13:45:22 -0400 Subject: [PATCH 132/136] Removing changes to main --- main.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/main.go b/main.go index d707b645a..fe8c5bb84 100644 --- a/main.go +++ b/main.go @@ -82,9 +82,6 @@ func main() { KubeMaster: cfg.Master, ServiceTypeFilter: cfg.ServiceTypeFilter, IstioIngressGatewayServices: cfg.IstioIngressGatewayServices, - CFAPIEndpoint: cfg.CFAPIEndpoint, - CFUsername: cfg.CFUsername, - CFPassword: cfg.CFPassword, } // Lookup all the selected sources by names and pass them the desired configuration. From 668a557dea49db6e027a4eb34ca7c9980c89df48 Mon Sep 17 00:00:00 2001 From: Dave Grizzanti Date: Fri, 5 Apr 2019 16:06:12 -0400 Subject: [PATCH 133/136] Update for failing tests --- source/route.go | 5 +++-- source/route_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ source/store_test.go | 15 +++++++++++++-- 3 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 source/route_test.go diff --git a/source/route.go b/source/route.go index 8b914634f..fa22a1d3b 100644 --- a/source/route.go +++ b/source/route.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 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. @@ -25,6 +25,7 @@ import ( type routeSource struct { client *cfclient.Client + config Config } // NewRouteSource creates a new routeSource with the given config @@ -38,7 +39,7 @@ func NewRouteSource(cfClient *cfclient.Client) (Source, error) { func (rs *routeSource) Endpoints() ([]*endpoint.Endpoint, error) { endpoints := []*endpoint.Endpoint{} - u, err := url.Parse(rs.client.Config.ApiAddress) + u, err := url.Parse(rs.config.CFAPIEndpoint) if err != nil { panic(err) } diff --git a/source/route_test.go b/source/route_test.go new file mode 100644 index 000000000..02a3d0baa --- /dev/null +++ b/source/route_test.go @@ -0,0 +1,42 @@ +/* +Copyright 2019 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 ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +type RouteSuite struct { + suite.Suite +} + +func (suite *RouteSuite) SetupTest() { + +} + +func TestRouteSource(t *testing.T) { + suite.Run(t, new(RouteSuite)) + t.Run("Interface", testRouteSourceImplementsSource) +} + +// testRouteSourceImplementsSource tests that routeSource is a valid Source. +func testRouteSourceImplementsSource(t *testing.T) { + require.Implements(t, (*Source)(nil), new(routeSource)) +} diff --git a/source/store_test.go b/source/store_test.go index 08045a0a8..176ac9bbd 100644 --- a/source/store_test.go +++ b/source/store_test.go @@ -24,14 +24,16 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" + cfclient "github.com/cloudfoundry-community/go-cfclient" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" ) type MockClientGenerator struct { mock.Mock - kubeClient kubernetes.Interface - istioClient istiomodel.ConfigStore + kubeClient kubernetes.Interface + istioClient istiomodel.ConfigStore + cloudFoundryClient *cfclient.Client } func (m *MockClientGenerator) KubeClient() (kubernetes.Interface, error) { @@ -52,6 +54,15 @@ func (m *MockClientGenerator) IstioClient() (istiomodel.ConfigStore, error) { return nil, args.Error(1) } +func (m *MockClientGenerator) CloudFoundryClient(cfAPIEndpoint string, cfUsername string, cfPassword string) (*cfclient.Client, error) { + args := m.Called() + if args.Error(1) == nil { + m.cloudFoundryClient = args.Get(0).(*cfclient.Client) + return m.cloudFoundryClient, nil + } + return nil, args.Error(1) +} + type ByNamesTestSuite struct { suite.Suite } From ef88346b9bcec01443cf35d1157e037cf5e9298d Mon Sep 17 00:00:00 2001 From: Dave Grizzanti Date: Mon, 29 Apr 2019 16:09:01 -0400 Subject: [PATCH 134/136] Rebase for go modules --- go.mod | 3 ++- go.sum | 17 +++++++++++++++-- main.go | 3 +++ source/route.go | 2 +- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 0172153c5..1c69777d4 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/aws/aws-sdk-go v1.13.32 github.com/cenkalti/backoff v2.1.1+incompatible // indirect github.com/cloudflare/cloudflare-go v0.0.0-20190102215809-0c85496d8730 + github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381 github.com/coreos/bbolt v1.3.2 // indirect github.com/coreos/etcd v3.3.10+incompatible github.com/coreos/go-semver v0.2.0 // indirect @@ -63,7 +64,7 @@ require ( github.com/onsi/gomega v1.5.0 // indirect github.com/oracle/oci-go-sdk v1.8.0 github.com/peterbourgon/diskv v2.0.1+incompatible // indirect - github.com/pkg/errors v0.8.0 + github.com/pkg/errors v0.8.1 github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 github.com/sanyu/dynectsoap v0.0.0-20181203081243-b83de5edc4e0 github.com/satori/go.uuid v1.2.0 // indirect diff --git a/go.sum b/go.sum index 2389ca51f..d04744c8e 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f h1:UrKzEwTgeiff9vxdrfdqxibzpWjxLnuXDI5m6z3GJAk= +code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f/go.mod h1:sk5LnIjB/nIEU7yP5sDQExVm62wu0pBh3yrElngUisI= git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/Azure/azure-sdk-for-go v10.0.4-beta+incompatible h1:FhnlL7/4O3gAB7EBgN43vA3Bb0fAlCBIMm9avXbcHlE= github.com/Azure/azure-sdk-for-go v10.0.4-beta+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= @@ -8,6 +10,8 @@ github.com/Azure/go-autorest v10.9.0+incompatible h1:3ccqKLQg+scl0J6krcDgih2Rl+G github.com/Azure/go-autorest v10.9.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= +github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= @@ -34,8 +38,11 @@ github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QH github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cloudflare-go v0.0.0-20190102215809-0c85496d8730 h1:+TK6ytATp7coqI4UlTBboFYD0kSkWZt6L6/T+1yBK6k= github.com/cloudflare/cloudflare-go v0.0.0-20190102215809-0c85496d8730/go.mod h1:qKQ9S///VKEax9N8kFel9/AvmnkYgvb8uiKTnoVFvpg= +github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381 h1:rdRS5BT13Iae9ssvcslol66gfOOXjaLYwqerEn/cl9s= +github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381/go.mod h1:e5+USP2j8Le2M0Jo3qKPFnNhuo1wueU4nWHCXBOfQ14= github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -74,6 +81,7 @@ github.com/go-ini/ini v1.32.0 h1:/MArBHSS0TFR28yPPDK1vPIjt4wUnPBfb81i6iiyKvA= github.com/go-ini/ini v1.32.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-resty/resty v1.8.0 h1:vbNCxbHOWCototzwxf3L63PQCKx6xgT6v8SHfoqkp6U= github.com/go-resty/resty v1.8.0/go.mod h1:n37daLLGIHq2FFYHxg+FYQiwA95FpfNI+A9uxoIYGRk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -160,6 +168,7 @@ github.com/linode/linodego v0.3.0/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZi github.com/lyft/protoc-gen-validate v0.0.14 h1:xbdDVIHd0Xq5Bfzu+8JR9s7mFmJPMvNLmfGhgcHJdFU= github.com/lyft/protoc-gen-validate v0.0.14/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= +github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11/go.mod h1:Ah2dBMoxZEqk118as2T4u4fjfXarE0pPnMJaArZQZsI= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -191,11 +200,14 @@ github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTm github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/oracle/oci-go-sdk v1.8.0 h1:4SO45bKV0I3/Mn1os3ANDZmV0eSE5z5CLdSUIkxtyzs= github.com/oracle/oci-go-sdk v1.8.0/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= +github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2/go.mod h1:L3UMQOThbttwfYRNFOWLLVXMhk5Lkio4GGOtw5UrxS0= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -219,9 +231,9 @@ github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180725160413-e900ae048470/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= +github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.3 h1:09wy7WZk4AqO03yH85Ex1X+Uo3vDsil3Fa9AgF8Emss= github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -282,6 +294,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJV golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/main.go b/main.go index fe8c5bb84..d707b645a 100644 --- a/main.go +++ b/main.go @@ -82,6 +82,9 @@ func main() { KubeMaster: cfg.Master, ServiceTypeFilter: cfg.ServiceTypeFilter, IstioIngressGatewayServices: cfg.IstioIngressGatewayServices, + CFAPIEndpoint: cfg.CFAPIEndpoint, + CFUsername: cfg.CFUsername, + CFPassword: cfg.CFPassword, } // Lookup all the selected sources by names and pass them the desired configuration. diff --git a/source/route.go b/source/route.go index fa22a1d3b..2908771a9 100644 --- a/source/route.go +++ b/source/route.go @@ -39,7 +39,7 @@ func NewRouteSource(cfClient *cfclient.Client) (Source, error) { func (rs *routeSource) Endpoints() ([]*endpoint.Endpoint, error) { endpoints := []*endpoint.Endpoint{} - u, err := url.Parse(rs.config.CFAPIEndpoint) + u, err := url.Parse(rs.client.Config.ApiAddress) if err != nil { panic(err) } From 002a85e92a0295d05c0f79a5a3dcd0f4adc9883c Mon Sep 17 00:00:00 2001 From: Dave Grizzanti Date: Fri, 10 May 2019 12:09:54 -0400 Subject: [PATCH 135/136] Rebase and address PR comments --- go.mod | 1 + go.sum | 6 ++++-- pkg/apis/externaldns/types.go | 2 +- source/{route.go => cloudfoundry.go} | 10 +++++----- source/route_test.go | 4 ---- source/store.go | 9 ++++++--- 6 files changed, 17 insertions(+), 15 deletions(-) rename source/{route.go => cloudfoundry.go} (82%) diff --git a/go.mod b/go.mod index 1c69777d4..abc39dfd8 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,7 @@ require ( github.com/go-resty/resty v1.8.0 // indirect github.com/gogo/googleapis v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect + github.com/golang/mock v1.2.0 // indirect github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a // indirect github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect diff --git a/go.sum b/go.sum index d04744c8e..a2ee94344 100644 --- a/go.sum +++ b/go.sum @@ -41,8 +41,8 @@ github.com/cloudflare/cloudflare-go v0.0.0-20190102215809-0c85496d8730/go.mod h1 github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381 h1:rdRS5BT13Iae9ssvcslol66gfOOXjaLYwqerEn/cl9s= github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381/go.mod h1:e5+USP2j8Le2M0Jo3qKPFnNhuo1wueU4nWHCXBOfQ14= github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= -github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -110,6 +110,7 @@ github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhp github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8 h1:L9JPKrtsHMQ4VCRQfHvbbHBfB2Urn8xf6QZeXZ+OrN4= github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= @@ -147,6 +148,7 @@ github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -167,8 +169,8 @@ github.com/linode/linodego v0.3.0 h1:I83pEPg4owSy5pCPaKix7xkGbWIjPxmAoc/Yu5OYDDY github.com/linode/linodego v0.3.0/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY= github.com/lyft/protoc-gen-validate v0.0.14 h1:xbdDVIHd0Xq5Bfzu+8JR9s7mFmJPMvNLmfGhgcHJdFU= github.com/lyft/protoc-gen-validate v0.0.14/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11/go.mod h1:Ah2dBMoxZEqk118as2T4u4fjfXarE0pPnMJaArZQZsI= +github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 6f327184a..0b3c4dde5 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -257,7 +257,7 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("cf-password", "The password to log into the cloud foundry API").Default(defaultConfig.CFPassword).StringVar(&cfg.CFPassword) // Flags related to processing sources - app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, fake, connector, istio-gateway, crd").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "istio-gateway", "route", "fake", "connector", "crd") + app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, fake, connector, istio-gateway, cloudfoundry, crd").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "istio-gateway", "cloudfoundry", "fake", "connector", "crd") 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("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) diff --git a/source/route.go b/source/cloudfoundry.go similarity index 82% rename from source/route.go rename to source/cloudfoundry.go index 2908771a9..8d8270c4c 100644 --- a/source/route.go +++ b/source/cloudfoundry.go @@ -23,20 +23,20 @@ import ( "github.com/kubernetes-incubator/external-dns/endpoint" ) -type routeSource struct { +type cloudfoundrySource struct { client *cfclient.Client config Config } -// NewRouteSource creates a new routeSource with the given config -func NewRouteSource(cfClient *cfclient.Client) (Source, error) { - return &routeSource{ +// NewCloudFoundrySource creates a new cloudfoundrySource with the given config +func NewCloudFoundrySource(cfClient *cfclient.Client) (Source, error) { + return &cloudfoundrySource{ client: cfClient, }, nil } // Endpoints returns endpoint objects -func (rs *routeSource) Endpoints() ([]*endpoint.Endpoint, error) { +func (rs *cloudfoundrySource) Endpoints() ([]*endpoint.Endpoint, error) { endpoints := []*endpoint.Endpoint{} u, err := url.Parse(rs.client.Config.ApiAddress) diff --git a/source/route_test.go b/source/route_test.go index 02a3d0baa..915a89f6f 100644 --- a/source/route_test.go +++ b/source/route_test.go @@ -27,10 +27,6 @@ type RouteSuite struct { suite.Suite } -func (suite *RouteSuite) SetupTest() { - -} - func TestRouteSource(t *testing.T) { suite.Run(t, new(RouteSuite)) t.Run("Interface", testRouteSourceImplementsSource) diff --git a/source/store.go b/source/store.go index 25baf2426..5491085e8 100644 --- a/source/store.go +++ b/source/store.go @@ -114,7 +114,10 @@ func NewCFClient(cfAPIEndpoint string, cfUsername string, cfPassword string) (*c Username: cfUsername, Password: cfPassword, } - client, _ := cfclient.NewClient(c) + client, err := cfclient.NewClient(c) + if err != nil { + return nil, err + } return client, nil } @@ -158,12 +161,12 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err return nil, err } return NewIstioGatewaySource(kubernetesClient, istioClient, cfg.IstioIngressGatewayServices, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation) - case "route": + case "cloudfoundry": cfClient, err := p.CloudFoundryClient(cfg.CFAPIEndpoint, cfg.CFUsername, cfg.CFPassword) if err != nil { return nil, err } - return NewRouteSource(cfClient) + return NewCloudFoundrySource(cfClient) case "fake": return NewFakeSource(cfg.FQDNTemplate) case "connector": From 7a6d233bd776f2bd02e3df4c6e393e913c34b632 Mon Sep 17 00:00:00 2001 From: Dave Grizzanti Date: Fri, 10 May 2019 12:28:32 -0400 Subject: [PATCH 136/136] fix route/cloudfoundry test --- source/{route_test.go => cloudfoundry_test.go} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename source/{route_test.go => cloudfoundry_test.go} (85%) diff --git a/source/route_test.go b/source/cloudfoundry_test.go similarity index 85% rename from source/route_test.go rename to source/cloudfoundry_test.go index 915a89f6f..59efc8e6e 100644 --- a/source/route_test.go +++ b/source/cloudfoundry_test.go @@ -32,7 +32,7 @@ func TestRouteSource(t *testing.T) { t.Run("Interface", testRouteSourceImplementsSource) } -// testRouteSourceImplementsSource tests that routeSource is a valid Source. +// testRouteSourceImplementsSource tests that cloudfoundrySource is a valid Source. func testRouteSourceImplementsSource(t *testing.T) { - require.Implements(t, (*Source)(nil), new(routeSource)) + require.Implements(t, (*Source)(nil), new(cloudfoundrySource)) }