From 1a721ac5cedf9384ebed083fae6a186ef50c1c0f Mon Sep 17 00:00:00 2001 From: David Schneider Date: Fri, 29 Mar 2019 17:19:04 +0100 Subject: [PATCH 01/59] Add --infoblox-max-results setting The number of objects returned by the infoblox api is limited to 1000 objects (see https://ipam.illinois.edu/wapidoc). If there are more then 1000 objects the API returns an error. By setting max-results one can raise the limit. --- main.go | 1 + pkg/apis/externaldns/types.go | 3 +++ pkg/apis/externaldns/types_test.go | 4 +++ provider/infoblox.go | 42 +++++++++++++++++++++++++++++- provider/infoblox_test.go | 19 ++++++++++++++ 5 files changed, 68 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 5e2354cd4..0470fb794 100644 --- a/main.go +++ b/main.go @@ -154,6 +154,7 @@ func main() { Version: cfg.InfobloxWapiVersion, SSLVerify: cfg.InfobloxSSLVerify, View: cfg.InfobloxView, + MaxResults: cfg.InfobloxMaxResults, DryRun: cfg.DryRun, }, ) diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index ad497fb87..9dc1d27d2 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -76,6 +76,7 @@ type Config struct { InfobloxWapiVersion string InfobloxSSLVerify bool InfobloxView string + InfobloxMaxResults int DynCustomerName string DynUsername string DynPassword string `secure:"yes"` @@ -153,6 +154,7 @@ var defaultConfig = &Config{ InfobloxWapiVersion: "2.3.1", InfobloxSSLVerify: true, InfobloxView: "", + InfobloxMaxResults: 0, OCIConfigFile: "/etc/kubernetes/oci.yaml", InMemoryZones: []string{}, PDNSServer: "http://localhost:8081", @@ -277,6 +279,7 @@ func (cfg *Config) ParseFlags(args []string) error { 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(defaultConfig.InfobloxView).StringVar(&cfg.InfobloxView) + app.Flag("infoblox-max-results", "Add _max_results as query parameter to the url on all api requests. the default is 0 which means _max_results is not set and the default of the server is used.").Default(strconv.Itoa(defaultConfig.InfobloxMaxResults)).IntVar(&cfg.InfobloxMaxResults) 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/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index ab474b498..98a52a371 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -60,6 +60,7 @@ var ( InfobloxWapiVersion: "2.3.1", InfobloxView: "", InfobloxSSLVerify: true, + InfobloxMaxResults: 0, OCIConfigFile: "/etc/kubernetes/oci.yaml", InMemoryZones: []string{""}, PDNSServer: "http://localhost:8081", @@ -117,6 +118,7 @@ var ( InfobloxWapiVersion: "2.6.1", InfobloxView: "internal", InfobloxSSLVerify: false, + InfobloxMaxResults: 2000, OCIConfigFile: "oci.yaml", InMemoryZones: []string{"example.org", "company.com"}, PDNSServer: "http://ns.example.com:8081", @@ -245,6 +247,7 @@ func TestParseFlags(t *testing.T) { "--infoblox-wapi-password=infoblox", "--infoblox-wapi-version=2.6.1", "--infoblox-view=internal", + "--infoblox-max-results=2000", "--inmemory-zone=example.org", "--inmemory-zone=company.com", "--pdns-server=http://ns.example.com:8081", @@ -314,6 +317,7 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_INFOBLOX_WAPI_VERSION": "2.6.1", "EXTERNAL_DNS_INFOBLOX_VIEW": "internal", "EXTERNAL_DNS_INFOBLOX_SSL_VERIFY": "0", + "EXTERNAL_DNS_INFOBLOX_MAX_RESULTS": "2000", "EXTERNAL_DNS_OCI_CONFIG_FILE": "oci.yaml", "EXTERNAL_DNS_INMEMORY_ZONE": "example.org\ncompany.com", "EXTERNAL_DNS_DOMAIN_FILTER": "example.org\ncompany.com", diff --git a/provider/infoblox.go b/provider/infoblox.go index 5d2038061..5ec871cf1 100644 --- a/provider/infoblox.go +++ b/provider/infoblox.go @@ -18,6 +18,7 @@ package provider import ( "fmt" + "net/http" "os" "strconv" "strings" @@ -40,6 +41,7 @@ type InfobloxConfig struct { SSLVerify bool DryRun bool View string + MaxResults int } // InfobloxProvider implements the DNS provider for Infoblox. @@ -56,6 +58,33 @@ type infobloxRecordSet struct { res interface{} } +// CustomQueryRequestBuilder implements a HttpRequestBuilder which supports to +// pass query paramters with each request +type CustomQueryRequestBuilder struct { + queryParams map[string]string + ibclient.WapiRequestBuilder +} + +// NewCustomQueryRequestBuilder returns a CustomQueryRequestBuilder which adds +// queryParams to all requests +func NewCustomQueryRequestBuilder(queryParams map[string]string) *CustomQueryRequestBuilder { + return &CustomQueryRequestBuilder{ + queryParams: queryParams, + } +} + +// BuildRequest prepares the api request. it uses BuildRequest of +// WapiRequestBuilder and then add all queryParams +func (cqb *CustomQueryRequestBuilder) BuildRequest(t ibclient.RequestType, obj ibclient.IBObject, ref string, queryParams ibclient.QueryParams) (req *http.Request, err error) { + req, err = cqb.WapiRequestBuilder.BuildRequest(t, obj, ref, queryParams) + query := req.URL.Query() + for key, value := range cqb.queryParams { + query.Set(key, value) + } + req.URL.RawQuery = query.Encode() + return +} + // NewInfobloxProvider creates a new Infoblox provider. func NewInfobloxProvider(infobloxConfig InfobloxConfig) (*InfobloxProvider, error) { hostConfig := ibclient.HostConfig{ @@ -75,7 +104,18 @@ func NewInfobloxProvider(infobloxConfig InfobloxConfig) (*InfobloxProvider, erro httpPoolConnections, ) - requestBuilder := &ibclient.WapiRequestBuilder{} + var requestBuilder ibclient.HttpRequestBuilder + if infobloxConfig.MaxResults != 0 { + // use our own HttpRequestBuilder which allows to set additional query parameters + query := map[string]string{ + "_max_results": strconv.Itoa(infobloxConfig.MaxResults), + } + requestBuilder = NewCustomQueryRequestBuilder(query) + } else { + // use the default HttpRequestBuilder of the infoblox client + requestBuilder = &ibclient.WapiRequestBuilder{} + } + requestor := &ibclient.WapiHttpRequestor{} client, err := ibclient.NewConnector(hostConfig, transportConfig, requestBuilder, requestor) diff --git a/provider/infoblox_test.go b/provider/infoblox_test.go index 6ae80eb92..e60239c4a 100644 --- a/provider/infoblox_test.go +++ b/provider/infoblox_test.go @@ -495,3 +495,22 @@ func TestInfobloxZones(t *testing.T) { assert.Equal(t, provider.findZone(zones, "lvl2-2.lvl1-1.example.com").Fqdn, "lvl1-1.example.com") assert.Equal(t, provider.findZone(zones, "lvl2-2.lvl1-2.example.com").Fqdn, "example.com") } + +func TestCustomQueryRequestBuilder(t *testing.T) { + hostConfig := ibclient.HostConfig{ + Host: "localhost", + Port: "8080", + Username: "user", + Password: "abcd", + Version: "2.3.1", + } + + requestBuilder := NewCustomQueryRequestBuilder(map[string]string{"myKey": "myValue"}) + requestBuilder.Init(hostConfig) + + obj := ibclient.NewRecordCNAME(ibclient.RecordCNAME{Zone: "foo.bar.com"}) + + req, _ := requestBuilder.BuildRequest(ibclient.GET, obj, "", ibclient.QueryParams{}) + + assert.True(t, req.URL.Query().Get("myKey") == "myValue") +} From b575f1dbddcdbde1204f4bd93d14a2869f1291f2 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 11 Apr 2019 21:19:36 +0200 Subject: [PATCH 02/59] Infoblox: Add _max_results parameter only on GET requests --- provider/infoblox.go | 39 ++++++++++++++++++--------------------- provider/infoblox_test.go | 10 +++++++--- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/provider/infoblox.go b/provider/infoblox.go index 5ec871cf1..805264abd 100644 --- a/provider/infoblox.go +++ b/provider/infoblox.go @@ -58,30 +58,30 @@ type infobloxRecordSet struct { res interface{} } -// CustomQueryRequestBuilder implements a HttpRequestBuilder which supports to -// pass query paramters with each request -type CustomQueryRequestBuilder struct { - queryParams map[string]string +// MaxResultsRequestBuilder implements a HttpRequestBuilder which sets the +// _max_results query parameter on all get requests +type MaxResultsRequestBuilder struct { + maxResults int ibclient.WapiRequestBuilder } -// NewCustomQueryRequestBuilder returns a CustomQueryRequestBuilder which adds -// queryParams to all requests -func NewCustomQueryRequestBuilder(queryParams map[string]string) *CustomQueryRequestBuilder { - return &CustomQueryRequestBuilder{ - queryParams: queryParams, +// NewMaxResultsRequestBuilder returns a MaxResultsRequestBuilder which adds +// _max_results query parameter to all GET requests +func NewMaxResultsRequestBuilder(maxResults int) *MaxResultsRequestBuilder { + return &MaxResultsRequestBuilder{ + maxResults: maxResults, } } // BuildRequest prepares the api request. it uses BuildRequest of -// WapiRequestBuilder and then add all queryParams -func (cqb *CustomQueryRequestBuilder) BuildRequest(t ibclient.RequestType, obj ibclient.IBObject, ref string, queryParams ibclient.QueryParams) (req *http.Request, err error) { - req, err = cqb.WapiRequestBuilder.BuildRequest(t, obj, ref, queryParams) - query := req.URL.Query() - for key, value := range cqb.queryParams { - query.Set(key, value) +// WapiRequestBuilder and then add the _max_requests parameter +func (mrb *MaxResultsRequestBuilder) BuildRequest(t ibclient.RequestType, obj ibclient.IBObject, ref string, queryParams ibclient.QueryParams) (req *http.Request, err error) { + req, err = mrb.WapiRequestBuilder.BuildRequest(t, obj, ref, queryParams) + if req.Method == "GET" { + query := req.URL.Query() + query.Set("_max_results", strconv.Itoa(mrb.maxResults)) + req.URL.RawQuery = query.Encode() } - req.URL.RawQuery = query.Encode() return } @@ -106,11 +106,8 @@ func NewInfobloxProvider(infobloxConfig InfobloxConfig) (*InfobloxProvider, erro var requestBuilder ibclient.HttpRequestBuilder if infobloxConfig.MaxResults != 0 { - // use our own HttpRequestBuilder which allows to set additional query parameters - query := map[string]string{ - "_max_results": strconv.Itoa(infobloxConfig.MaxResults), - } - requestBuilder = NewCustomQueryRequestBuilder(query) + // use our own HttpRequestBuilder which sets _max_results paramter on GET requests + requestBuilder = NewMaxResultsRequestBuilder(infobloxConfig.MaxResults) } else { // use the default HttpRequestBuilder of the infoblox client requestBuilder = &ibclient.WapiRequestBuilder{} diff --git a/provider/infoblox_test.go b/provider/infoblox_test.go index e60239c4a..cf5fa9a81 100644 --- a/provider/infoblox_test.go +++ b/provider/infoblox_test.go @@ -496,7 +496,7 @@ func TestInfobloxZones(t *testing.T) { assert.Equal(t, provider.findZone(zones, "lvl2-2.lvl1-2.example.com").Fqdn, "example.com") } -func TestCustomQueryRequestBuilder(t *testing.T) { +func TestMaxResultsRequestBuilder(t *testing.T) { hostConfig := ibclient.HostConfig{ Host: "localhost", Port: "8080", @@ -505,12 +505,16 @@ func TestCustomQueryRequestBuilder(t *testing.T) { Version: "2.3.1", } - requestBuilder := NewCustomQueryRequestBuilder(map[string]string{"myKey": "myValue"}) + requestBuilder := NewMaxResultsRequestBuilder(54321) requestBuilder.Init(hostConfig) obj := ibclient.NewRecordCNAME(ibclient.RecordCNAME{Zone: "foo.bar.com"}) req, _ := requestBuilder.BuildRequest(ibclient.GET, obj, "", ibclient.QueryParams{}) - assert.True(t, req.URL.Query().Get("myKey") == "myValue") + assert.True(t, req.URL.Query().Get("_max_results") == "54321") + + req, _ = requestBuilder.BuildRequest(ibclient.CREATE, obj, "", ibclient.QueryParams{}) + + assert.True(t, req.URL.Query().Get("_max_results") == "") } From 0d608fd0188b740b3985ba75c3029b080859daa0 Mon Sep 17 00:00:00 2001 From: Yaroslav Verbin Date: Tue, 16 Apr 2019 19:13:26 +0300 Subject: [PATCH 03/59] 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 1798dbdfb5e7649ad45c21f1959467cb037e0456 Mon Sep 17 00:00:00 2001 From: Yaroslav Verbin Date: Thu, 18 Apr 2019 11:15:01 +0300 Subject: [PATCH 04/59] 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 2b1e1d9effb75e267dc26725ae42f3934d816fdd Mon Sep 17 00:00:00 2001 From: Yaroslav Verbin Date: Wed, 17 Apr 2019 15:01:31 +0300 Subject: [PATCH 05/59] related to https://github.com/kubernetes-incubator/external-dns/issues/602 --- source/service.go | 69 ++++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/source/service.go b/source/service.go index 7ae70997b..25ab08246 100644 --- a/source/service.go +++ b/source/service.go @@ -30,7 +30,6 @@ import ( log "github.com/sirupsen/logrus" v1 "k8s.io/api/core/v1" - "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" @@ -163,12 +162,6 @@ func (sc *serviceSource) Endpoints() ([]*endpoint.Endpoint, error) { services = sc.filterByServiceType(services) } - // get the ip addresses of all the nodes and cache them for this run - nodeTargets, err := sc.extractNodeTargets() - if err != nil { - return nil, err - } - endpoints := []*endpoint.Endpoint{} for _, svc := range services { @@ -180,7 +173,7 @@ func (sc *serviceSource) Endpoints() ([]*endpoint.Endpoint, error) { continue } - svcEndpoints := sc.endpoints(svc, nodeTargets) + svcEndpoints := sc.endpoints(svc) // process legacy annotations if no endpoints were returned and compatibility mode is enabled. if len(svcEndpoints) == 0 && sc.compatibility != "" { @@ -189,7 +182,7 @@ func (sc *serviceSource) Endpoints() ([]*endpoint.Endpoint, error) { // 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) if err != nil { return nil, err } @@ -280,7 +273,7 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri return endpoints } -func (sc *serviceSource) endpointsFromTemplate(svc *v1.Service, nodeTargets endpoint.Targets) ([]*endpoint.Endpoint, error) { +func (sc *serviceSource) endpointsFromTemplate(svc *v1.Service) ([]*endpoint.Endpoint, error) { var endpoints []*endpoint.Endpoint // Process the whole template string @@ -293,21 +286,21 @@ func (sc *serviceSource) endpointsFromTemplate(svc *v1.Service, nodeTargets endp providerSpecific := getProviderSpecificAnnotations(svc.Annotations) hostnameList := strings.Split(strings.Replace(buf.String(), " ", "", -1), ",") for _, hostname := range hostnameList { - endpoints = append(endpoints, sc.generateEndpoints(svc, hostname, nodeTargets, providerSpecific)...) + endpoints = append(endpoints, sc.generateEndpoints(svc, hostname, providerSpecific)...) } return endpoints, nil } // endpointsFromService extracts the endpoints from a service object -func (sc *serviceSource) endpoints(svc *v1.Service, nodeTargets endpoint.Targets) []*endpoint.Endpoint { +func (sc *serviceSource) endpoints(svc *v1.Service) []*endpoint.Endpoint { var endpoints []*endpoint.Endpoint // Skip endpoints if we do not want entries from annotations if !sc.ignoreHostnameAnnotation { providerSpecific := getProviderSpecificAnnotations(svc.Annotations) hostnameList := getHostnamesFromAnnotations(svc.Annotations) for _, hostname := range hostnameList { - endpoints = append(endpoints, sc.generateEndpoints(svc, hostname, nodeTargets, providerSpecific)...) + endpoints = append(endpoints, sc.generateEndpoints(svc, hostname, providerSpecific)...) } } return endpoints @@ -363,7 +356,7 @@ func (sc *serviceSource) setResourceLabel(service *v1.Service, endpoints []*endp } } -func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, nodeTargets endpoint.Targets, providerSpecific endpoint.ProviderSpecific) []*endpoint.Endpoint { +func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, providerSpecific endpoint.ProviderSpecific) []*endpoint.Endpoint { hostname = strings.TrimSuffix(hostname, ".") ttl, err := getTTLFromAnnotations(svc.Annotations) if err != nil { @@ -403,8 +396,12 @@ func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, nod } case v1.ServiceTypeNodePort: // add the nodeTargets and extract an SRV endpoint - targets = append(targets, nodeTargets...) - endpoints = append(endpoints, sc.extractNodePortEndpoints(svc, nodeTargets, hostname, ttl)...) + targets, err = sc.extractNodePortTargets(svc) + if err != nil { + log.Errorf("Unable to extract targets from service %s/%s error: %v", svc.Namespace, svc.Name, err) + return endpoints + } + endpoints = append(endpoints, sc.extractNodePortEndpoints(svc, targets, hostname, ttl)...) } for _, t := range targets { @@ -449,20 +446,44 @@ func extractLoadBalancerTargets(svc *v1.Service) endpoint.Targets { return targets } -func (sc *serviceSource) extractNodeTargets() (endpoint.Targets, error) { +func (sc *serviceSource) extractNodePortTargets(svc *v1.Service) (endpoint.Targets, error) { var ( internalIPs endpoint.Targets externalIPs endpoint.Targets + nodes []*v1.Node + err error ) - 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. - log.Debugf("Unable to list nodes (Forbidden), returning empty list of targets (NodePort services will be skipped)") - return endpoint.Targets{}, nil + switch svc.Spec.ExternalTrafficPolicy { + case v1.ServiceExternalTrafficPolicyTypeLocal: + labelSelector, err := metav1.ParseToLabelSelector(labels.Set(svc.Spec.Selector).AsSelectorPreValidated().String()) + if err != nil { + return nil, err + } + selector, err := metav1.LabelSelectorAsSelector(labelSelector) + if err != nil { + return nil, err + } + pods, err := sc.podInformer.Lister().Pods(svc.Namespace).List(selector) + if err != nil { + return nil, err + } + + for _, v := range pods { + if v.Status.Phase == v1.PodRunning { + node, err := sc.nodeInformer.Lister().Get(v.Spec.NodeName) + if err != nil { + log.Debugf("Unable to find node where Pod %s is running", v.Spec.Hostname) + continue + } + nodes = append(nodes, node) + } + } + default: + nodes, err = sc.nodeInformer.Lister().List(labels.Everything()) + if err != nil { + return nil, err } - return nil, err } for _, node := range nodes { From 0d4d475a4efc266240c3f8ec1b4fc5bdd646595e Mon Sep 17 00:00:00 2001 From: Yaroslav Verbin Date: Thu, 18 Apr 2019 18:08:04 +0300 Subject: [PATCH 06/59] add test case --- source/service_test.go | 92 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/source/service_test.go b/source/service_test.go index 650dda759..2f4e66924 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -1282,6 +1282,7 @@ func TestNodePortServices(t *testing.T) { svcNamespace string svcName string svcType v1.ServiceType + svcTrafficPolicy v1.ServiceExternalTrafficPolicyType compatibility string fqdnTemplate string ignoreHostnameAnnotation bool @@ -1291,6 +1292,9 @@ func TestNodePortServices(t *testing.T) { expected []*endpoint.Endpoint expectError bool nodes []*v1.Node + podnames []string + nodeIndex []int + phases []v1.PodPhase }{ { "annotated NodePort services return an endpoint with IP addresses of the cluster's nodes", @@ -1299,6 +1303,7 @@ func TestNodePortServices(t *testing.T) { "testing", "foo", v1.ServiceTypeNodePort, + v1.ServiceExternalTrafficPolicyTypeCluster, "", "", false, @@ -1333,6 +1338,9 @@ func TestNodePortServices(t *testing.T) { }, }, }}, + []string{}, + []int{}, + []v1.PodPhase{}, }, { "hostname annotated NodePort services are ignored", @@ -1341,6 +1349,7 @@ func TestNodePortServices(t *testing.T) { "testing", "foo", v1.ServiceTypeNodePort, + v1.ServiceExternalTrafficPolicyTypeCluster, "", "", true, @@ -1372,6 +1381,9 @@ func TestNodePortServices(t *testing.T) { }, }, }}, + []string{}, + []int{}, + []v1.PodPhase{}, }, { "non-annotated NodePort services with set fqdnTemplate return an endpoint with target IP", @@ -1380,6 +1392,7 @@ func TestNodePortServices(t *testing.T) { "testing", "foo", v1.ServiceTypeNodePort, + v1.ServiceExternalTrafficPolicyTypeCluster, "", "{{.Name}}.bar.example.com", false, @@ -1412,6 +1425,9 @@ func TestNodePortServices(t *testing.T) { }, }, }}, + []string{}, + []int{}, + []v1.PodPhase{}, }, { "annotated NodePort services return an endpoint with IP addresses of the private cluster's nodes", @@ -1420,6 +1436,7 @@ func TestNodePortServices(t *testing.T) { "testing", "foo", v1.ServiceTypeNodePort, + v1.ServiceExternalTrafficPolicyTypeCluster, "", "", false, @@ -1452,6 +1469,55 @@ func TestNodePortServices(t *testing.T) { }, }, }}, + []string{}, + []int{}, + []v1.PodPhase{}, + }, + { + "annotated NodePort services with ExternalTrafficPolicy=Local return an endpoint with IP addresses of the cluster's nodes where pods is running only", + "", + "", + "testing", + "foo", + v1.ServiceTypeNodePort, + v1.ServiceExternalTrafficPolicyTypeLocal, + "", + "", + false, + map[string]string{}, + map[string]string{ + hostnameAnnotationKey: "foo.example.org.", + }, + nil, + []*endpoint.Endpoint{ + {DNSName: "_30192._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV}, + {DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.2"}, RecordType: endpoint.RecordTypeA}, + }, + 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"}, + }, + }, + }}, + []string{"master-0"}, + []int{1}, + []v1.PodPhase{v1.PodRunning}, }, } { t.Run(tc.title, func(t *testing.T) { @@ -1465,10 +1531,34 @@ func TestNodePortServices(t *testing.T) { } } + // Create pods + for i, podname := range tc.podnames { + pod := &v1.Pod{ + Spec: v1.PodSpec{ + Containers: []v1.Container{}, + Hostname: podname, + NodeName: tc.nodes[tc.nodeIndex[i]].Name, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: tc.svcNamespace, + Name: podname, + Labels: tc.labels, + Annotations: tc.annotations, + }, + Status: v1.PodStatus{ + Phase: tc.phases[i], + }, + } + + _, err := kubernetes.CoreV1().Pods(tc.svcNamespace).Create(pod) + require.NoError(t, err) + } + // Create a service to test against service := &v1.Service{ Spec: v1.ServiceSpec{ - Type: tc.svcType, + Type: tc.svcType, + ExternalTrafficPolicy: tc.svcTrafficPolicy, Ports: []v1.ServicePort{ { NodePort: 30192, From 0c68309f806448c94ecfbdd866275e06159da67a Mon Sep 17 00:00:00 2001 From: Josh Harshman Date: Fri, 19 Apr 2019 09:49:56 -0600 Subject: [PATCH 07/59] 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 08/59] 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 09/59] 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 10/59] 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 11/59] 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 12/59] 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 13/59] 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 14/59] 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 15/59] 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 16/59] 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 17/59] 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 18/59] 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 19/59] 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 20/59] 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 21/59] 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 22/59] 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 23/59] 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 24/59] 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 25/59] 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 26/59] 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 27/59] 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 28/59] 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 29/59] 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 30/59] 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 31/59] 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 32/59] 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 33/59] 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 34/59] 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 35/59] 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 36/59] 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 37/59] 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 38/59] 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 39/59] 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 40/59] 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 41/59] 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 42/59] 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 43/59] 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)) } From 84e7108956a7bf29087a92708972fd70d8e28895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nick=20J=C3=BCttner?= Date: Tue, 14 May 2019 17:20:59 +0200 Subject: [PATCH 44/59] Changelog for v0.5.14 (#1024) --- CHANGELOG.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32d36d8c8..9cddb9d4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,16 @@ -## v0.5.14 - 2019-04-23 +## v0.5.14 - 2019-05-14 + - Docs: Update aws.md (#1009) @pawelprazak + - New provider TransIP (#1007) @skoef + - Docs: Add docker image faq (#1006) @Raffo + - DNSimple: Support apex records (#1004) @jbowes + - NS1: Add --ns1-endpoint and --ns1-ignoressl flags (#1002) @mburtless + - AWS: Cache the endpoints on the controller loop (#1001) @fraenkel - Core: Supress Kubernetes logs (#991) @njuettner + - Core: distroless/static image (#989) @jharshman + - Core: Headless service missing DNS entry (#984) @yverbin + - New provider NS1 (#963) @mburtless + - Core: Add Cloud Foundry routes as a source (#955) @dgrizzanti ## v0.5.13 - 2019-04-18 From ef77161ab0e521a6c65e31e84848e20dad3a5923 Mon Sep 17 00:00:00 2001 From: Christian Simon Date: Thu, 16 May 2019 14:06:13 +0100 Subject: [PATCH 45/59] Google zones should be filter by their ID and Name Before we only filter by the zone ID, which is an integer value, that is not exposed in the GCP Console and by the related terraform resource. This allows to filter by either ID or Name whatever matches. --- provider/google.go | 2 +- provider/google_test.go | 57 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/provider/google.go b/provider/google.go index 67033fc72..3ba73354f 100644 --- a/provider/google.go +++ b/provider/google.go @@ -161,7 +161,7 @@ func (p *GoogleProvider) Zones() (map[string]*dns.ManagedZone, error) { f := func(resp *dns.ManagedZonesListResponse) error { for _, zone := range resp.ManagedZones { - if p.domainFilter.Match(zone.DnsName) && p.zoneIDFilter.Match(fmt.Sprintf("%v", zone.Id)) { + if p.domainFilter.Match(zone.DnsName) && (p.zoneIDFilter.Match(fmt.Sprintf("%v", zone.Id)) || p.zoneIDFilter.Match(fmt.Sprintf("%v", zone.Name))) { zones[zone.Name] = zone log.Debugf("Matched %s (zone: %s)", zone.DnsName, zone.Name) } else { diff --git a/provider/google_test.go b/provider/google_test.go index f4f15b5ab..a45d81cab 100644 --- a/provider/google_test.go +++ b/provider/google_test.go @@ -192,6 +192,28 @@ func hasTrailingDot(target string) bool { return strings.HasSuffix(target, ".") } +func TestGoogleZonesIDFilter(t *testing.T) { + provider := newGoogleProviderZoneOverlap(t, NewDomainFilter([]string{"cluster.local."}), NewZoneIDFilter([]string{"10002"}), false, []*endpoint.Endpoint{}) + + zones, err := provider.Zones() + require.NoError(t, err) + + validateZones(t, zones, map[string]*dns.ManagedZone{ + "internal-2": {Name: "internal-2", DnsName: "cluster.local.", Id: 10002}, + }) +} + +func TestGoogleZonesNameFilter(t *testing.T) { + provider := newGoogleProviderZoneOverlap(t, NewDomainFilter([]string{"cluster.local."}), NewZoneIDFilter([]string{"internal-2"}), false, []*endpoint.Endpoint{}) + + zones, err := provider.Zones() + require.NoError(t, err) + + validateZones(t, zones, map[string]*dns.ManagedZone{ + "internal-2": {Name: "internal-2", DnsName: "cluster.local.", Id: 10002}, + }) +} + func TestGoogleZones(t *testing.T) { provider := newGoogleProvider(t, NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), NewZoneIDFilter([]string{""}), false, []*endpoint.Endpoint{}) @@ -562,6 +584,41 @@ func validateChangeRecord(t *testing.T, record *dns.ResourceRecordSet, expected assert.Equal(t, expected.Type, record.Type) } +func newGoogleProviderZoneOverlap(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, + resourceRecordSetsClient: &mockResourceRecordSetsClient{}, + managedZonesClient: &mockManagedZonesClient{}, + changesClient: &mockChangesClient{}, + } + + createZone(t, provider, &dns.ManagedZone{ + Name: "internal-1", + DnsName: "cluster.local.", + Id: 10001, + }) + + createZone(t, provider, &dns.ManagedZone{ + Name: "internal-2", + DnsName: "cluster.local.", + Id: 10002, + }) + + createZone(t, provider, &dns.ManagedZone{ + Name: "internal-3", + DnsName: "cluster.local.", + Id: 10003, + }) + + provider.dryRun = dryRun + + return provider + +} + func newGoogleProvider(t *testing.T, domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, dryRun bool, records []*endpoint.Endpoint) *GoogleProvider { provider := &GoogleProvider{ project: "zalando-external-dns-test", From f2eebfa9d7d3f69447ba26807b9db924046efadf Mon Sep 17 00:00:00 2001 From: Tariq Ibrahim Date: Thu, 16 May 2019 16:10:57 -0700 Subject: [PATCH 46/59] sanitize dockerfiles for external-dns --- Dockerfile | 4 ++-- Dockerfile.mini | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index f987cf7d4..d12d600c9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ # limitations under the License. # builder image -FROM golang:1.12.4 as builder +FROM golang:1.12.5 as builder WORKDIR /github.com/kubernetes-incubator/external-dns COPY . . @@ -22,7 +22,7 @@ RUN make test RUN make build # final image -FROM registry.opensource.zalan.do/stups/alpine:latest +FROM alpine:3.9 LABEL maintainer="Team Teapot @ Zalando SE " COPY --from=builder /github.com/kubernetes-incubator/external-dns/build/external-dns /bin/external-dns diff --git a/Dockerfile.mini b/Dockerfile.mini index 6e0303d99..f3eef891c 100644 --- a/Dockerfile.mini +++ b/Dockerfile.mini @@ -1,4 +1,4 @@ -FROM golang:1.12.4 as builder +FROM golang:1.12.5 as builder WORKDIR /external-dns COPY . . RUN make build From edfc413e48e00d227d94ab635c8229c26a2bc4cd Mon Sep 17 00:00:00 2001 From: Anand Patel Date: Thu, 16 May 2019 18:08:23 -0400 Subject: [PATCH 47/59] add empty source --- docs/contributing/sources-and-providers.md | 1 + pkg/apis/externaldns/types.go | 2 +- source/empty.go | 32 ++++++++++++++++++++ source/empty_test.go | 35 ++++++++++++++++++++++ 4 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 source/empty.go create mode 100644 source/empty_test.go diff --git a/docs/contributing/sources-and-providers.md b/docs/contributing/sources-and-providers.md index 3d51b3598..37c9e1163 100644 --- a/docs/contributing/sources-and-providers.md +++ b/docs/contributing/sources-and-providers.md @@ -25,6 +25,7 @@ All sources live in package `source`. * `FakeSource`: returns a random list of Endpoints for the purpose of testing providers without having access to a Kubernetes cluster. * `ConnectorSource`: returns a list of Endpoint objects which are served by a tcp server configured through `connector-source-server` flag. * `CRDSource`: returns a list of Endpoint objects sourced from the spec of CRD objects. For more details refer to [CRD source](../crd-source.md) documentation. +* `EmptySource`: returns an empty list of Endpoint objects for the purpose of testing and cleaning out entries. ### Providers diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 0b3c4dde5..142c0ce37 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, cloudfoundry, crd").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "istio-gateway", "cloudfoundry", "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, empty").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "istio-gateway", "cloudfoundry", "fake", "connector", "crd", "empty") 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/empty.go b/source/empty.go new file mode 100644 index 000000000..c219581d3 --- /dev/null +++ b/source/empty.go @@ -0,0 +1,32 @@ +/* +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 "github.com/kubernetes-incubator/external-dns/endpoint" + +// emptySource is a Source that returns no endpoints. +type emptySource struct{} + +// Endpoints collects endpoints of all nested Sources and returns them in a single slice. +func (e *emptySource) Endpoints() ([]*endpoint.Endpoint, error) { + return []*endpoint.Endpoint{}, nil +} + +// NewEmptySource creates a new emptySource. +func NewEmptySource() Source { + return &emptySource{} +} diff --git a/source/empty_test.go b/source/empty_test.go new file mode 100644 index 000000000..f82f1a39d --- /dev/null +++ b/source/empty_test.go @@ -0,0 +1,35 @@ +/* +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" +) + +func TestEmptySourceReturnsEmpty(t *testing.T) { + e := NewEmptySource() + + endpoints, err := e.Endpoints() + if err != nil { + t.Errorf("Expected no error but got %s", err.Error()) + } + + count := len(endpoints) + if count != 0 { + t.Errorf("Expected 0 endpoints but got %d", count) + } +} From 095c0dd650e4e0eaa1be72c9858cdc0d0755e769 Mon Sep 17 00:00:00 2001 From: Lim Yue Chuan Date: Sat, 18 May 2019 01:23:34 +0800 Subject: [PATCH 48/59] Fix CloudFlare provider to return a single endpoint for each name/type. --- provider/cloudflare.go | 46 +++++++- provider/cloudflare_test.go | 222 ++++++++++++++++++++++++++++++++++++ 2 files changed, 263 insertions(+), 5 deletions(-) diff --git a/provider/cloudflare.go b/provider/cloudflare.go index 85ff411c6..cecb810c3 100644 --- a/provider/cloudflare.go +++ b/provider/cloudflare.go @@ -181,11 +181,10 @@ func (p *CloudFlareProvider) Records() ([]*endpoint.Endpoint, error) { return nil, err } - for _, r := range records { - if supportedRecordType(r.Type) { - endpoints = append(endpoints, endpoint.NewEndpointWithTTL(r.Name, r.Type, endpoint.TTL(r.TTL), r.Content).WithProviderSpecific(source.CloudflareProxiedKey, strconv.FormatBool(r.Proxied))) - } - } + // As CloudFlare does not support "sets" of targets, but instead returns + // a single entry for each name/type/target, we have to group by name + // and record to allow the planner to calculate the correct plan. See #992. + endpoints = append(endpoints, groupByNameAndType(records)...) } return endpoints, nil @@ -354,3 +353,40 @@ func shouldBeProxied(endpoint *endpoint.Endpoint, proxiedByDefault bool) bool { } return proxied } + +func groupByNameAndType(records []cloudflare.DNSRecord) []*endpoint.Endpoint { + endpoints := []*endpoint.Endpoint{} + + // group supported records by name and type + groups := map[string][]cloudflare.DNSRecord{} + + for _, r := range records { + if !supportedRecordType(r.Type) { + continue + } + + groupBy := r.Name + r.Type + if _, ok := groups[groupBy]; !ok { + groups[groupBy] = []cloudflare.DNSRecord{} + } + + groups[groupBy] = append(groups[groupBy], r) + } + + // create single endpoint with all the targets for each name/type + for _, records := range groups { + targets := make([]string, len(records)) + for i, record := range records { + targets[i] = record.Content + } + endpoints = append(endpoints, + endpoint.NewEndpointWithTTL( + records[0].Name, + records[0].Type, + endpoint.TTL(records[0].TTL), + targets...). + WithProviderSpecific(source.CloudflareProxiedKey, strconv.FormatBool(records[0].Proxied))) + } + + return endpoints +} diff --git a/provider/cloudflare_test.go b/provider/cloudflare_test.go index 73e32d59a..1c3b68854 100644 --- a/provider/cloudflare_test.go +++ b/provider/cloudflare_test.go @@ -595,3 +595,225 @@ func validateCloudFlareZones(t *testing.T, zones []cloudflare.Zone, expected []c assert.Equal(t, expected[i].Name, zone.Name) } } + +func TestGroupByNameAndType(t *testing.T) { + testCases := []struct { + Name string + Records []cloudflare.DNSRecord + ExpectedEndpoints []*endpoint.Endpoint + }{ + { + Name: "empty", + Records: []cloudflare.DNSRecord{}, + ExpectedEndpoints: []*endpoint.Endpoint{}, + }, + { + Name: "single record - single target", + Records: []cloudflare.DNSRecord{ + { + Name: "foo.com", + Type: endpoint.RecordTypeA, + Content: "10.10.10.1", + TTL: defaultCloudFlareRecordTTL, + }, + }, + ExpectedEndpoints: []*endpoint.Endpoint{ + { + DNSName: "foo.com", + Targets: endpoint.Targets{"10.10.10.1"}, + RecordType: endpoint.RecordTypeA, + RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), + Labels: endpoint.Labels{}, + ProviderSpecific: endpoint.ProviderSpecific{ + { + Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied", + Value: "false", + }, + }, + }, + }, + }, + { + Name: "single record - multiple targets", + Records: []cloudflare.DNSRecord{ + { + Name: "foo.com", + Type: endpoint.RecordTypeA, + Content: "10.10.10.1", + TTL: defaultCloudFlareRecordTTL, + }, + { + Name: "foo.com", + Type: endpoint.RecordTypeA, + Content: "10.10.10.2", + TTL: defaultCloudFlareRecordTTL, + }, + }, + ExpectedEndpoints: []*endpoint.Endpoint{ + { + DNSName: "foo.com", + Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"}, + RecordType: endpoint.RecordTypeA, + RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), + Labels: endpoint.Labels{}, + ProviderSpecific: endpoint.ProviderSpecific{ + { + Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied", + Value: "false", + }, + }, + }, + }, + }, + { + Name: "multiple record - multiple targets", + Records: []cloudflare.DNSRecord{ + { + Name: "foo.com", + Type: endpoint.RecordTypeA, + Content: "10.10.10.1", + TTL: defaultCloudFlareRecordTTL, + }, + { + Name: "foo.com", + Type: endpoint.RecordTypeA, + Content: "10.10.10.2", + TTL: defaultCloudFlareRecordTTL, + }, + { + Name: "bar.de", + Type: endpoint.RecordTypeA, + Content: "10.10.10.1", + TTL: defaultCloudFlareRecordTTL, + }, + { + Name: "bar.de", + Type: endpoint.RecordTypeA, + Content: "10.10.10.2", + TTL: defaultCloudFlareRecordTTL, + }, + }, + ExpectedEndpoints: []*endpoint.Endpoint{ + { + DNSName: "foo.com", + Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"}, + RecordType: endpoint.RecordTypeA, + RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), + Labels: endpoint.Labels{}, + ProviderSpecific: endpoint.ProviderSpecific{ + { + Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied", + Value: "false", + }, + }, + }, + { + DNSName: "bar.de", + Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"}, + RecordType: endpoint.RecordTypeA, + RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), + Labels: endpoint.Labels{}, + ProviderSpecific: endpoint.ProviderSpecific{ + { + Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied", + Value: "false", + }, + }, + }, + }, + }, + { + Name: "multiple record - mixed single/multiple targets", + Records: []cloudflare.DNSRecord{ + { + Name: "foo.com", + Type: endpoint.RecordTypeA, + Content: "10.10.10.1", + TTL: defaultCloudFlareRecordTTL, + }, + { + Name: "foo.com", + Type: endpoint.RecordTypeA, + Content: "10.10.10.2", + TTL: defaultCloudFlareRecordTTL, + }, + { + Name: "bar.de", + Type: endpoint.RecordTypeA, + Content: "10.10.10.1", + TTL: defaultCloudFlareRecordTTL, + }, + }, + ExpectedEndpoints: []*endpoint.Endpoint{ + { + DNSName: "foo.com", + Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"}, + RecordType: endpoint.RecordTypeA, + RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), + Labels: endpoint.Labels{}, + ProviderSpecific: endpoint.ProviderSpecific{ + { + Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied", + Value: "false", + }, + }, + }, + { + DNSName: "bar.de", + Targets: endpoint.Targets{"10.10.10.1"}, + RecordType: endpoint.RecordTypeA, + RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), + Labels: endpoint.Labels{}, + ProviderSpecific: endpoint.ProviderSpecific{ + { + Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied", + Value: "false", + }, + }, + }, + }, + }, + { + Name: "unsupported record type", + Records: []cloudflare.DNSRecord{ + { + Name: "foo.com", + Type: endpoint.RecordTypeA, + Content: "10.10.10.1", + TTL: defaultCloudFlareRecordTTL, + }, + { + Name: "foo.com", + Type: endpoint.RecordTypeA, + Content: "10.10.10.2", + TTL: defaultCloudFlareRecordTTL, + }, + { + Name: "bar.de", + Type: "NOT SUPPORTED", + Content: "10.10.10.1", + TTL: defaultCloudFlareRecordTTL, + }, + }, + ExpectedEndpoints: []*endpoint.Endpoint{ + { + DNSName: "foo.com", + Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"}, + RecordType: endpoint.RecordTypeA, + RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), + Labels: endpoint.Labels{}, + ProviderSpecific: endpoint.ProviderSpecific{ + { + Name: "external-dns.alpha.kubernetes.io/cloudflare-proxied", + Value: "false", + }, + }, + }, + }, + }, + } + + for _, tc := range testCases { + assert.Equal(t, groupByNameAndType(tc.Records), tc.ExpectedEndpoints) + } +} From ccf3a2adc7d5a0fc43e9e62a35602ddc048b647e Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 19 May 2019 12:13:16 +0200 Subject: [PATCH 49/59] Update --infoblox-max-results parameter help text Co-Authored-By: Raffaele Di Fazio --- 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 9dc1d27d2..88102522b 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -279,7 +279,7 @@ func (cfg *Config) ParseFlags(args []string) error { 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(defaultConfig.InfobloxView).StringVar(&cfg.InfobloxView) - app.Flag("infoblox-max-results", "Add _max_results as query parameter to the url on all api requests. the default is 0 which means _max_results is not set and the default of the server is used.").Default(strconv.Itoa(defaultConfig.InfobloxMaxResults)).IntVar(&cfg.InfobloxMaxResults) + app.Flag("infoblox-max-results", "Add _max_results as query parameter to the URL on all API requests. The default is 0 which means _max_results is not set and the default of the server is used.").Default(strconv.Itoa(defaultConfig.InfobloxMaxResults)).IntVar(&cfg.InfobloxMaxResults) 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 6e0407d1b988ba065c09013235ad64fd2f21d62e Mon Sep 17 00:00:00 2001 From: Dmitrii Balakhonskii Date: Sun, 19 May 2019 17:32:46 +0200 Subject: [PATCH 50/59] Install ca-certificates Related to #1037 --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index d12d600c9..457b9e78d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,6 +25,7 @@ RUN make build FROM alpine:3.9 LABEL maintainer="Team Teapot @ Zalando SE " +RUN apk add ca-certificates && update-ca-certificates COPY --from=builder /github.com/kubernetes-incubator/external-dns/build/external-dns /bin/external-dns USER nobody From a98637aa025e21c9b1cc46b44bc1f1493946cdc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lecorvaisier?= Date: Mon, 20 May 2019 22:38:58 -0400 Subject: [PATCH 51/59] docs(cloudflare): set ttl annotation for cloudflare proxied entries to 1 --- docs/tutorials/cloudflare.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/tutorials/cloudflare.md b/docs/tutorials/cloudflare.md index e4c191690..d1bce19e2 100644 --- a/docs/tutorials/cloudflare.md +++ b/docs/tutorials/cloudflare.md @@ -165,6 +165,7 @@ 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. +For Cloudflare proxied entries, set the TTL annotation to 1 (automatic), or do not set it. 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. From cbb5d25f932757fe0b69c16a2aa10a4aa213047b Mon Sep 17 00:00:00 2001 From: Graeme Lawes Date: Tue, 21 May 2019 19:07:46 -0400 Subject: [PATCH 52/59] Log RR adds/deletes as Info in provider/rfc2136 --- provider/rfc2136.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/provider/rfc2136.go b/provider/rfc2136.go index 8d90d2b10..b2d1d3515 100644 --- a/provider/rfc2136.go +++ b/provider/rfc2136.go @@ -243,7 +243,7 @@ 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) + log.Infof("Adding RR: %s", newRR) rr, err := dns.NewRR(newRR) if err != nil { @@ -270,7 +270,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("Removing RR: %s", newRR) + log.Infof("Removing RR: %s", newRR) rr, err := dns.NewRR(newRR) if err != nil { From 63816deb81d24918c2c8dd5c879ead16f3a1e8a2 Mon Sep 17 00:00:00 2001 From: Curtis Mattoon Date: Sun, 30 Dec 2018 14:18:47 -0500 Subject: [PATCH 53/59] Add Config.ExcludeDomains --- pkg/apis/externaldns/types.go | 3 +++ pkg/apis/externaldns/types_test.go | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index a462e65ce..10586e66c 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -54,6 +54,7 @@ type Config struct { Provider string GoogleProject string DomainFilter []string + ExcludeDomains []string ZoneIDFilter []string AlibabaCloudConfigFile string AlibabaCloudZoneType string @@ -141,6 +142,7 @@ var defaultConfig = &Config{ Provider: "", GoogleProject: "", DomainFilter: []string{}, + ExcludeDomains: []string{}, AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json", AWSZoneType: "", AWSZoneTagFilter: []string{}, @@ -276,6 +278,7 @@ func (cfg *Config) ParseFlags(args []string) error { // 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, 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("exclude-domains", "Exclude subdomains (optional)").Default("").StringsVar(&cfg.ExcludeDomains) 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) 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) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index 5bc9ac629..dc580a036 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -40,6 +40,7 @@ var ( Provider: "google", GoogleProject: "", DomainFilter: []string{""}, + ExcludeDomains: []string{""}, ZoneIDFilter: []string{""}, AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json", AWSZoneType: "", @@ -100,6 +101,7 @@ var ( Provider: "google", GoogleProject: "project", DomainFilter: []string{"example.org", "company.com"}, + ExcludeDomains: []string{"xapi.example.org", "xapi.company.com"}, ZoneIDFilter: []string{"/hostedzone/ZTST1", "/hostedzone/ZTST2"}, AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json", AWSZoneType: "private", @@ -166,6 +168,7 @@ var ( Provider: "google", GoogleProject: "", DomainFilter: []string{""}, + ExcludeDomains: []string{""}, ZoneIDFilter: []string{""}, AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json", AWSZoneType: "", @@ -266,6 +269,8 @@ func TestParseFlags(t *testing.T) { "--no-infoblox-ssl-verify", "--domain-filter=example.org", "--domain-filter=company.com", + "--exclude-domains=xapi.example.org", + "--exclude-domains=xapi.company.com", "--zone-id-filter=/hostedzone/ZTST1", "--zone-id-filter=/hostedzone/ZTST2", "--aws-zone-type=private", @@ -331,6 +336,7 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_OCI_CONFIG_FILE": "oci.yaml", "EXTERNAL_DNS_INMEMORY_ZONE": "example.org\ncompany.com", "EXTERNAL_DNS_DOMAIN_FILTER": "example.org\ncompany.com", + "EXTERNAL_DNS_EXCLUDE_DOMAINS": "xapi.example.org\nxapi.company.com", "EXTERNAL_DNS_PDNS_SERVER": "http://ns.example.com:8081", "EXTERNAL_DNS_PDNS_API_KEY": "some-secret-key", "EXTERNAL_DNS_PDNS_TLS_ENABLED": "1", From e23513ee4c2b150e55e70e683d8e57c01e1035ad Mon Sep 17 00:00:00 2001 From: Curtis Mattoon Date: Sun, 30 Dec 2018 15:07:16 -0500 Subject: [PATCH 54/59] Adds DomainFilter.exclusions --- provider/domain_filter.go | 40 +++++++++++------- provider/domain_filter_test.go | 76 ++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 14 deletions(-) diff --git a/provider/domain_filter.go b/provider/domain_filter.go index 4bfdef90f..fa5c3837a 100644 --- a/provider/domain_filter.go +++ b/provider/domain_filter.go @@ -23,29 +23,42 @@ import ( // DomainFilter holds a lists of valid domain names type DomainFilter struct { filters []string + exclude []string +} + +// prepareFilters provides consistent trimming for filters/exclude params +func prepareFilters(filters []string) []string { + fs := make([]string, len(filters)) + for i, domain := range filters { + fs[i] = strings.TrimSuffix(strings.TrimSpace(domain), ".") + } + return fs +} + +// NewDomainFilterWithExclusions returns a new DomainFilter, given a list of matches and exclusions +func NewDomainFilterWithExclusions(domainFilters []string, excludeDomains []string) DomainFilter { + return DomainFilter{prepareFilters(domainFilters), prepareFilters(excludeDomains)} } // NewDomainFilter returns a new DomainFilter given a comma separated list of domains func NewDomainFilter(domainFilters []string) DomainFilter { - filters := make([]string, len(domainFilters)) - - // user can define filter domains either with trailing dot or without, we remove all trailing periods from - // the internal representation - for i, domain := range domainFilters { - filters[i] = strings.TrimSuffix(strings.TrimSpace(domain), ".") - } - - return DomainFilter{filters} + return DomainFilter{prepareFilters(domainFilters), []string{}} } // Match checks whether a domain can be found in the DomainFilter. func (df DomainFilter) Match(domain string) bool { - // return always true, if not filter is specified - if len(df.filters) == 0 { - return true + return df.matchFilter(df.filters, domain, true) && !df.matchFilter(df.exclude, domain, false) +} + +// matchFilter determines if any `filters` match `domain`. +// If no `filters` are provided, behavior depends on `emptyval` +// (empty `df.filters` matches everything, while empty `df.exclude` excludes nothing) +func (df DomainFilter) matchFilter(filters []string, domain string, emptyval bool) bool { + if len(filters) == 0 { + return emptyval } - for _, filter := range df.filters { + for _, filter := range filters { strippedDomain := strings.TrimSuffix(domain, ".") if filter == "" { @@ -60,7 +73,6 @@ func (df DomainFilter) Match(domain string) bool { return true } } - return false } diff --git a/provider/domain_filter_test.go b/provider/domain_filter_test.go index 12a562664..294a159f2 100644 --- a/provider/domain_filter_test.go +++ b/provider/domain_filter_test.go @@ -24,6 +24,7 @@ import ( type domainFilterTest struct { domainFilter []string + exclusions []string domains []string expected bool } @@ -31,108 +32,173 @@ type domainFilterTest struct { var domainFilterTests = []domainFilterTest{ { []string{"google.com.", "exaring.de", "inovex.de"}, + []string{}, []string{"google.com", "exaring.de", "inovex.de"}, true, }, { []string{"google.com.", "exaring.de", "inovex.de"}, + []string{}, []string{"google.com", "exaring.de", "inovex.de"}, true, }, { []string{"google.com.", "exaring.de.", "inovex.de"}, + []string{}, []string{"google.com", "exaring.de", "inovex.de"}, true, }, { []string{"foo.org. "}, + []string{}, []string{"foo.org"}, true, }, { []string{" foo.org"}, + []string{}, []string{"foo.org"}, true, }, { []string{"foo.org."}, + []string{}, []string{"foo.org"}, true, }, { []string{"foo.org."}, + []string{}, []string{"baz.org"}, false, }, { []string{"baz.foo.org."}, + []string{}, []string{"foo.org"}, false, }, { []string{"", "foo.org."}, + []string{}, []string{"foo.org"}, true, }, { []string{"", "foo.org."}, []string{}, + []string{}, true, }, { []string{""}, + []string{}, []string{"foo.org"}, true, }, { []string{""}, []string{}, + []string{}, true, }, { []string{" "}, []string{}, + []string{}, true, }, { []string{"bar.sub.example.org"}, + []string{}, []string{"foo.bar.sub.example.org"}, true, }, { []string{"example.org"}, + []string{}, []string{"anexample.org", "test.anexample.org"}, false, }, { []string{".example.org"}, + []string{}, []string{"anexample.org", "test.anexample.org"}, false, }, { []string{".example.org"}, + []string{}, []string{"example.org"}, false, }, { []string{".example.org"}, + []string{}, []string{"test.example.org"}, true, }, { []string{"anexample.org"}, + []string{}, []string{"example.org", "test.example.org"}, false, }, { []string{".org"}, + []string{}, []string{"example.org", "test.example.org", "foo.test.example.org"}, true, }, + { + []string{"example.org"}, + []string{"api.example.org"}, + []string{"example.org", "test.example.org", "foo.test.example.org"}, + true, + }, + { + []string{"example.org"}, + []string{"api.example.org"}, + []string{"foo.api.example.org", "api.example.org"}, + false, + }, + { + []string{" example.org. "}, + []string{" .api.example.org "}, + []string{"foo.api.example.org", "bar.baz.api.example.org."}, + false, + }, + { + []string{"example.org."}, + []string{"api.example.org"}, + []string{"dev-api.example.org", "qa-api.example.org"}, + true, + }, + { + []string{"example.org."}, + []string{"api.example.org"}, + []string{"dev.api.example.org", "qa.api.example.org"}, + false, + }, + { + []string{"example.org", "api.example.org"}, + []string{"internal.api.example.org"}, + []string{"foo.api.example.org"}, + true, + }, + { + []string{"example.org", "api.example.org"}, + []string{"internal.api.example.org"}, + []string{"foo.internal.api.example.org"}, + false, + }, } func TestDomainFilterMatch(t *testing.T) { for i, tt := range domainFilterTests { + if len(tt.exclusions) > 0 { + t.Skip("NewDomainFilter() doesn't support exclusions") + } domainFilter := NewDomainFilter(tt.domainFilter) for _, domain := range tt.domains { assert.Equal(t, tt.expected, domainFilter.Match(domain), "should not fail: %v in test-case #%v", domain, i) @@ -141,6 +207,16 @@ func TestDomainFilterMatch(t *testing.T) { } } +func TestDomainFilterWithExclusions(t *testing.T) { + for i, tt := range domainFilterTests { + domainFilter := NewDomainFilterWithExclusions(tt.domainFilter, tt.exclusions) + for _, domain := range tt.domains { + assert.Equal(t, tt.expected, domainFilter.Match(domain), "should not fail: %v in test-case #%v", domain, i) + assert.Equal(t, tt.expected, domainFilter.Match(domain+"."), "should not fail: %v in test-case #%v", domain+".", i) + } + } +} + func TestDomainFilterMatchWithEmptyFilter(t *testing.T) { for _, tt := range domainFilterTests { domainFilter := DomainFilter{} From 4b1082eaa6d120c3f7cf8415c14bac7e7815b84c Mon Sep 17 00:00:00 2001 From: Curtis Mattoon Date: Sun, 30 Dec 2018 15:25:34 -0500 Subject: [PATCH 55/59] Add tests for provider/domain_filter --- provider/domain_filter_test.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/provider/domain_filter_test.go b/provider/domain_filter_test.go index 294a159f2..3095a6c0e 100644 --- a/provider/domain_filter_test.go +++ b/provider/domain_filter_test.go @@ -226,3 +226,35 @@ func TestDomainFilterMatchWithEmptyFilter(t *testing.T) { } } } + +func TestPrepareFiltersStripsWhitespaceAndDotSuffix(t *testing.T) { + for _, tt := range []struct { + input []string + output []string + }{ + { + []string{" ", " ", ""}, + []string{"", "", ""}, + }, + { + []string{" foo ", " bar. ", "baz."}, + []string{"foo", "bar", "baz"}, + }, + { + []string{"foo.bar", " foo.bar. ", " foo.bar.baz ", " foo.bar.baz. "}, + []string{"foo.bar", "foo.bar", "foo.bar.baz", "foo.bar.baz"}, + }, + } { + t.Run("test string", func(t *testing.T) { + assert.Equal(t, tt.output, prepareFilters(tt.input)) + }) + } +} + +func TestMatchFilterReturnsProperEmptyVal(t *testing.T) { + emptyFilters := []string{} + + df := NewDomainFilterWithExclusions(emptyFilters, emptyFilters) + assert.Equal(t, true, df.matchFilter(emptyFilters, "somedomain.com", true)) + assert.Equal(t, false, df.matchFilter(emptyFilters, "somedomain.com", false)) +} From 111ad4f72f0519543cd5eb75ae97ef59bc57fe0a Mon Sep 17 00:00:00 2001 From: Curtis Mattoon Date: Sun, 30 Dec 2018 15:46:34 -0500 Subject: [PATCH 56/59] Add tests for DomainFilter.IsConfigured --- main.go | 2 +- provider/domain_filter_test.go | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 8d2c61da9..ad78f0276 100644 --- a/main.go +++ b/main.go @@ -100,7 +100,7 @@ func main() { // Combine multiple sources into a single, deduplicated source. endpointsSource := source.NewDedupSource(source.NewMultiSource(sources)) - domainFilter := provider.NewDomainFilter(cfg.DomainFilter) + domainFilter := provider.NewDomainFilterWithExclusions(cfg.DomainFilter, cfg.ExcludeDomains) zoneIDFilter := provider.NewZoneIDFilter(cfg.ZoneIDFilter) zoneTypeFilter := provider.NewZoneTypeFilter(cfg.AWSZoneType) zoneTagFilter := provider.NewZoneTagFilter(cfg.AWSZoneTagFilter) diff --git a/provider/domain_filter_test.go b/provider/domain_filter_test.go index 3095a6c0e..9752cd1c0 100644 --- a/provider/domain_filter_test.go +++ b/provider/domain_filter_test.go @@ -258,3 +258,47 @@ func TestMatchFilterReturnsProperEmptyVal(t *testing.T) { assert.Equal(t, true, df.matchFilter(emptyFilters, "somedomain.com", true)) assert.Equal(t, false, df.matchFilter(emptyFilters, "somedomain.com", false)) } + +func TestDomainFilterIsConfigured(t *testing.T) { + for _, tt := range []struct { + filters []string + exclude []string + expected bool + }{ + { + []string{""}, + []string{""}, + false, + }, + { + []string{" "}, + []string{" "}, + false, + }, + { + []string{"", ""}, + []string{""}, + true, + }, + { + []string{" . "}, + []string{" . "}, + false, + }, + { + []string{" notempty.com "}, + []string{" "}, + true, + }, + { + []string{" notempty.com "}, + []string{" thisdoesntmatter.com "}, + true, + }, + } { + t.Run("test IsConfigured", func(t *testing.T) { + df := NewDomainFilterWithExclusions(tt.filters, tt.exclude) + assert.Equal(t, tt.expected, df.IsConfigured()) + }) + } +} From 788df8ac849c438cb80ff5cc294ac13977a87d3f Mon Sep 17 00:00:00 2001 From: Curtis Mattoon Date: Wed, 22 May 2019 15:37:06 -0400 Subject: [PATCH 57/59] Ensure case-insensitive matching --- provider/domain_filter.go | 4 ++-- provider/domain_filter_test.go | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/provider/domain_filter.go b/provider/domain_filter.go index fa5c3837a..b5f904e3f 100644 --- a/provider/domain_filter.go +++ b/provider/domain_filter.go @@ -30,7 +30,7 @@ type DomainFilter struct { func prepareFilters(filters []string) []string { fs := make([]string, len(filters)) for i, domain := range filters { - fs[i] = strings.TrimSuffix(strings.TrimSpace(domain), ".") + fs[i] = strings.ToLower(strings.TrimSuffix(strings.TrimSpace(domain), ".")) } return fs } @@ -59,7 +59,7 @@ func (df DomainFilter) matchFilter(filters []string, domain string, emptyval boo } for _, filter := range filters { - strippedDomain := strings.TrimSuffix(domain, ".") + strippedDomain := strings.ToLower(strings.TrimSuffix(domain, ".")) if filter == "" { return true diff --git a/provider/domain_filter_test.go b/provider/domain_filter_test.go index 9752cd1c0..a168ff085 100644 --- a/provider/domain_filter_test.go +++ b/provider/domain_filter_test.go @@ -192,6 +192,24 @@ var domainFilterTests = []domainFilterTest{ []string{"foo.internal.api.example.org"}, false, }, + { + []string{"eXaMPle.ORG", "API.example.ORG"}, + []string{"Foo-Bar.Example.Org"}, + []string{"FoOoo.Api.Example.Org"}, + true, + }, + { + []string{"eXaMPle.ORG", "API.example.ORG"}, + []string{"api.example.org"}, + []string{"foobar.Example.Org"}, + true, + }, + { + []string{"eXaMPle.ORG", "API.example.ORG"}, + []string{"api.example.org"}, + []string{"foobar.API.Example.Org"}, + false, + }, } func TestDomainFilterMatch(t *testing.T) { From 409ebc7bd0f55dd391b2e2b40afbeb36c275328d Mon Sep 17 00:00:00 2001 From: Curtis Mattoon Date: Wed, 22 May 2019 15:48:43 -0400 Subject: [PATCH 58/59] Move 'matchFilter' to a function --- provider/domain_filter.go | 4 ++-- provider/domain_filter_test.go | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/provider/domain_filter.go b/provider/domain_filter.go index b5f904e3f..c59a71c8c 100644 --- a/provider/domain_filter.go +++ b/provider/domain_filter.go @@ -47,13 +47,13 @@ func NewDomainFilter(domainFilters []string) DomainFilter { // Match checks whether a domain can be found in the DomainFilter. func (df DomainFilter) Match(domain string) bool { - return df.matchFilter(df.filters, domain, true) && !df.matchFilter(df.exclude, domain, false) + return matchFilter(df.filters, domain, true) && !matchFilter(df.exclude, domain, false) } // matchFilter determines if any `filters` match `domain`. // If no `filters` are provided, behavior depends on `emptyval` // (empty `df.filters` matches everything, while empty `df.exclude` excludes nothing) -func (df DomainFilter) matchFilter(filters []string, domain string, emptyval bool) bool { +func matchFilter(filters []string, domain string, emptyval bool) bool { if len(filters) == 0 { return emptyval } diff --git a/provider/domain_filter_test.go b/provider/domain_filter_test.go index a168ff085..0edc3c392 100644 --- a/provider/domain_filter_test.go +++ b/provider/domain_filter_test.go @@ -271,10 +271,8 @@ func TestPrepareFiltersStripsWhitespaceAndDotSuffix(t *testing.T) { func TestMatchFilterReturnsProperEmptyVal(t *testing.T) { emptyFilters := []string{} - - df := NewDomainFilterWithExclusions(emptyFilters, emptyFilters) - assert.Equal(t, true, df.matchFilter(emptyFilters, "somedomain.com", true)) - assert.Equal(t, false, df.matchFilter(emptyFilters, "somedomain.com", false)) + assert.Equal(t, true, matchFilter(emptyFilters, "somedomain.com", true)) + assert.Equal(t, false, matchFilter(emptyFilters, "somedomain.com", false)) } func TestDomainFilterIsConfigured(t *testing.T) { From 58a8fe0dc40e82893f916e38da1ff496d7dfd5ed Mon Sep 17 00:00:00 2001 From: Mikkel Oscar Lyderik Larsen Date: Sun, 2 Jun 2019 23:10:33 +0200 Subject: [PATCH 59/59] Update aws-go-sdk to be compatible with kube-aws-iam-controller Updates the aws-go-sdk dependency to latest release (`v1.19.41`) to be compatible with [kube-aws-iam-controller](https://github.com/mikkeloscar/kube-aws-iam-controller) (requires `>=v1.16.2`) Ref: https://github.com/mikkeloscar/kube-aws-iam-controller/blob/master/docs/sdk-configuration.md#golang-aws-sdk Signed-off-by: Mikkel Oscar Lyderik Larsen --- go.mod | 6 +----- go.sum | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index abc39dfd8..2e270938d 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( 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/aws/aws-sdk-go v1.19.41 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 @@ -28,11 +28,9 @@ require ( 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 @@ -47,7 +45,6 @@ require ( 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 @@ -91,7 +88,6 @@ require ( 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/ns1/ns1-go.v2 v2.0.0-20190322154155-0dafb5275fd1 gopkg.in/yaml.v2 v2.2.2 diff --git a/go.sum b/go.sum index a2ee94344..66b359161 100644 --- a/go.sum +++ b/go.sum @@ -29,8 +29,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF 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/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/aws/aws-sdk-go v1.19.41 h1:veutzvQP/lOmYmtX26S9mTFJLO6sp7/UsxFcCjglu4A= +github.com/aws/aws-sdk-go v1.19.41/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= 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= @@ -41,6 +41,7 @@ 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/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= 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= @@ -77,10 +78,9 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV 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/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 h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk= 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= @@ -142,8 +142,8 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH 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/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/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= @@ -169,6 +169,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/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11 h1:YFh+sjyJTMQSYjKwM4dFKhJPJC/wfo98tPUc17HdoYw= 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= @@ -202,6 +203,7 @@ 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 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso= 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= @@ -234,8 +236,10 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm 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-20180725160413-e900ae048470/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +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-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +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= @@ -350,8 +354,6 @@ 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=