From ba6c82e043b9dd31c5a218fa34bffc462658fad2 Mon Sep 17 00:00:00 2001 From: cliedeman Date: Wed, 15 Aug 2018 16:07:21 +0200 Subject: [PATCH 1/2] Set Linode user agent and change to linode/linodego package --- Gopkg.lock | 18 +++++++++--------- Gopkg.toml | 4 ++-- main.go | 2 +- provider/linode.go | 5 +++-- provider/linode_test.go | 6 +++--- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index f8fbb2310..b70cef095 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -107,14 +107,6 @@ pruneopts = "" revision = "4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9" -[[projects]] - digest = "1:e62a9159a86473538057a322c49b65824cc32da865ace6cfc5ef1cacf3245226" - name = "github.com/chiefy/linodego" - packages = ["."] - pruneopts = "" - revision = "e7bd44d89fc5db41b43b8bce7278550a589d7ab6" - version = "v0.2.0" - [[projects]] digest = "1:85fd00554a6ed5b33687684b76635d532c74141508b5bce2843d85e8a3c9dc91" name = "github.com/cloudflare/cloudflare-go" @@ -367,6 +359,14 @@ revision = "508103cfb3b315fa9752b5bcd4fb2d97bbc26d89" version = "v0.2.0" +[[projects]] + digest = "1:1c41354ef11c9dbae2fe1ceee8369fcb2634977ba07e701e19ea53e8742c5420" + name = "github.com/linode/linodego" + packages = ["."] + pruneopts = "" + revision = "7edbc87f0140b7561dbc20458877a56bdded5eb8" + version = "v0.3.0" + [[projects]] branch = "master" digest = "1:63722a4b1e1717be7b98fc686e0b30d5e7f734b9e93d7dee86293b6deab7ea28" @@ -838,7 +838,6 @@ "github.com/aws/aws-sdk-go/aws/session", "github.com/aws/aws-sdk-go/service/route53", "github.com/aws/aws-sdk-go/service/servicediscovery", - "github.com/chiefy/linodego", "github.com/cloudflare/cloudflare-go", "github.com/coreos/etcd/client", "github.com/digitalocean/godo", @@ -854,6 +853,7 @@ "github.com/infobloxopen/infoblox-go-client", "github.com/kubernetes/repo-infra/verify/boilerplate/test", "github.com/linki/instrumented_http", + "github.com/linode/linodego", "github.com/nesv/go-dynect/dynect", "github.com/oracle/oci-go-sdk/common", "github.com/oracle/oci-go-sdk/dns", diff --git a/Gopkg.toml b/Gopkg.toml index 64265025b..4d76243e0 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -77,5 +77,5 @@ ignored = ["github.com/kubernetes/repo-infra/kazel"] version = "1.8.0" [[constraint]] - name = "github.com/chiefy/linodego" - version = "0.2.0" \ No newline at end of file + name = "github.com/linode/linodego" + version = "0.3.0" \ No newline at end of file diff --git a/main.go b/main.go index 28ed43a2d..1d04f831d 100644 --- a/main.go +++ b/main.go @@ -122,7 +122,7 @@ func main() { case "digitalocean": p, err = provider.NewDigitalOceanProvider(domainFilter, cfg.DryRun) case "linode": - p, err = provider.NewLinodeProvider(domainFilter, cfg.DryRun) + p, err = provider.NewLinodeProvider(domainFilter, cfg.DryRun, externaldns.Version) case "dnsimple": p, err = provider.NewDnsimpleProvider(domainFilter, zoneIDFilter, cfg.DryRun) case "infoblox": diff --git a/provider/linode.go b/provider/linode.go index 089be9010..7000f1961 100644 --- a/provider/linode.go +++ b/provider/linode.go @@ -23,7 +23,7 @@ import ( "os" "strconv" - "github.com/chiefy/linodego" + "github.com/linode/linodego" log "github.com/sirupsen/logrus" "golang.org/x/oauth2" @@ -76,7 +76,7 @@ type LinodeChangeDelete struct { } // NewLinodeProvider initializes a new Linode DNS based Provider. -func NewLinodeProvider(domainFilter DomainFilter, dryRun bool) (*LinodeProvider, error) { +func NewLinodeProvider(domainFilter DomainFilter, dryRun bool, appVersion string) (*LinodeProvider, error) { token, ok := os.LookupEnv("LINODE_TOKEN") if !ok { return nil, fmt.Errorf("no token found") @@ -91,6 +91,7 @@ func NewLinodeProvider(domainFilter DomainFilter, dryRun bool) (*LinodeProvider, } linodeClient := linodego.NewClient(oauth2Client) + linodeClient.SetUserAgent(fmt.Sprintf("ExternalDNS/%s linodego/%s", appVersion, linodego.Version)) provider := &LinodeProvider{ Client: &linodeClient, diff --git a/provider/linode_test.go b/provider/linode_test.go index e693939dc..974f8f770 100644 --- a/provider/linode_test.go +++ b/provider/linode_test.go @@ -21,9 +21,9 @@ import ( "os" "testing" - "github.com/chiefy/linodego" "github.com/kubernetes-incubator/external-dns/endpoint" "github.com/kubernetes-incubator/external-dns/plan" + "github.com/linode/linodego" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -137,11 +137,11 @@ func TestLinodeConvertRecordType(t *testing.T) { func TestNewLinodeProvider(t *testing.T) { _ = os.Setenv("LINODE_TOKEN", "xxxxxxxxxxxxxxxxx") - _, err := NewLinodeProvider(NewDomainFilter([]string{"ext-dns-test.zalando.to."}), true) + _, err := NewLinodeProvider(NewDomainFilter([]string{"ext-dns-test.zalando.to."}), true, "1.0") require.NoError(t, err) _ = os.Unsetenv("LINODE_TOKEN") - _, err = NewLinodeProvider(NewDomainFilter([]string{"ext-dns-test.zalando.to."}), true) + _, err = NewLinodeProvider(NewDomainFilter([]string{"ext-dns-test.zalando.to."}), true, "1.0") require.Error(t, err) } From e6cae22ec48610e979b3478b7516ea958e01eb02 Mon Sep 17 00:00:00 2001 From: Julian Vassev Date: Wed, 15 Aug 2018 12:29:19 -0700 Subject: [PATCH 2/2] Configure req timeout calling k8s APIs When running in a pod sometimes the request to get ingreses/services stalls indefinitely. A simple pod restart fixes this. Hard to reproduce but I got lucky and did thread dump which revealed a gorouting blocked on call to k8s. What's new is a `--request-timeout` flag that makes requests to k8s bounded in time. The default is 30s - this may cause some deployments with a slow api-server to timeout. --- main.go | 5 +++-- pkg/apis/externaldns/types.go | 3 +++ pkg/apis/externaldns/types_test.go | 4 ++++ provider/google.go | 8 ++++---- provider/google_test.go | 8 ++++---- source/store.go | 14 +++++++++----- 6 files changed, 27 insertions(+), 15 deletions(-) diff --git a/main.go b/main.go index 28ed43a2d..3c3c854bb 100644 --- a/main.go +++ b/main.go @@ -79,8 +79,9 @@ func main() { // Lookup all the selected sources by names and pass them the desired configuration. sources, err := source.ByNames(&source.SingletonClientGenerator{ - KubeConfig: cfg.KubeConfig, - KubeMaster: cfg.Master, + KubeConfig: cfg.KubeConfig, + KubeMaster: cfg.Master, + RequestTimeout: cfg.RequestTimeout, }, cfg.Sources, sourceCfg) if err != nil { log.Fatal(err) diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 115670624..de55a2af3 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -38,6 +38,7 @@ var ( type Config struct { Master string KubeConfig string + RequestTimeout time.Duration Sources []string Namespace string AnnotationFilter string @@ -95,6 +96,7 @@ type Config struct { var defaultConfig = &Config{ Master: "", KubeConfig: "", + RequestTimeout: time.Second * 30, Sources: nil, Namespace: "", AnnotationFilter: "", @@ -183,6 +185,7 @@ func (cfg *Config) ParseFlags(args []string) error { // Flags related to Kubernetes app.Flag("master", "The Kubernetes API server to connect to (default: auto-detect)").Default(defaultConfig.Master).StringVar(&cfg.Master) app.Flag("kubeconfig", "Retrieve target cluster configuration from a Kubernetes configuration file (default: auto-detect)").Default(defaultConfig.KubeConfig).StringVar(&cfg.KubeConfig) + app.Flag("request-timeout", "Request timeout when calling Kubernetes APIs. 0s means no timeout").Default(defaultConfig.RequestTimeout.String()).DurationVar(&cfg.RequestTimeout) // 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)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "fake", "connector") diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index 1edacff18..8824e2aa4 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -31,6 +31,7 @@ var ( minimalConfig = &Config{ Master: "", KubeConfig: "", + RequestTimeout: time.Second * 30, Sources: []string{"service"}, Namespace: "", FQDNTemplate: "", @@ -76,6 +77,7 @@ var ( overriddenConfig = &Config{ Master: "http://127.0.0.1:8080", KubeConfig: "/some/path", + RequestTimeout: time.Second * 77, Sources: []string{"service", "ingress", "connector"}, Namespace: "namespace", FQDNTemplate: "{{.Name}}.service.example.com", @@ -144,6 +146,7 @@ func TestParseFlags(t *testing.T) { args: []string{ "--master=http://127.0.0.1:8080", "--kubeconfig=/some/path", + "--request-timeout=77s", "--source=service", "--source=ingress", "--source=connector", @@ -203,6 +206,7 @@ func TestParseFlags(t *testing.T) { envVars: map[string]string{ "EXTERNAL_DNS_MASTER": "http://127.0.0.1:8080", "EXTERNAL_DNS_KUBECONFIG": "/some/path", + "EXTERNAL_DNS_REQUEST_TIMEOUT": "77s", "EXTERNAL_DNS_SOURCE": "service\ningress\nconnector", "EXTERNAL_DNS_NAMESPACE": "namespace", "EXTERNAL_DNS_FQDN_TEMPLATE": "{{.Name}}.service.example.com", diff --git a/provider/google.go b/provider/google.go index 47dab7334..017b22da4 100644 --- a/provider/google.go +++ b/provider/google.go @@ -143,10 +143,10 @@ func NewGoogleProvider(project string, domainFilter DomainFilter, zoneIDFilter Z } provider := &GoogleProvider{ - project: project, - domainFilter: domainFilter, - zoneIDFilter: zoneIDFilter, - dryRun: dryRun, + project: project, + domainFilter: domainFilter, + zoneIDFilter: zoneIDFilter, + dryRun: dryRun, resourceRecordSetsClient: resourceRecordSetsService{dnsClient.ResourceRecordSets}, managedZonesClient: managedZonesService{dnsClient.ManagedZones}, changesClient: changesService{dnsClient.Changes}, diff --git a/provider/google_test.go b/provider/google_test.go index 8791a1b9b..102debfbf 100644 --- a/provider/google_test.go +++ b/provider/google_test.go @@ -569,10 +569,10 @@ func validateChangeRecord(t *testing.T, record *dns.ResourceRecordSet, expected func newGoogleProvider(t *testing.T, domainFilter DomainFilter, zoneIDFilter ZoneIDFilter, dryRun bool, records []*endpoint.Endpoint) *GoogleProvider { provider := &GoogleProvider{ - project: "zalando-external-dns-test", - domainFilter: domainFilter, - zoneIDFilter: zoneIDFilter, - dryRun: false, + project: "zalando-external-dns-test", + domainFilter: domainFilter, + zoneIDFilter: zoneIDFilter, + dryRun: false, resourceRecordSetsClient: &mockResourceRecordSetsClient{}, managedZonesClient: &mockManagedZonesClient{}, changesClient: &mockChangesClient{}, diff --git a/source/store.go b/source/store.go index 5f4a23b85..d082a5b54 100644 --- a/source/store.go +++ b/source/store.go @@ -21,6 +21,7 @@ import ( "net/http" "os" "strings" + "time" "sync" @@ -53,9 +54,10 @@ type ClientGenerator interface { // SingletonClientGenerator stores provider clients and guarantees that only one instance of client // will be generated type SingletonClientGenerator struct { - KubeConfig string - KubeMaster string - client kubernetes.Interface + KubeConfig string + KubeMaster string + RequestTimeout time.Duration + client kubernetes.Interface sync.Once } @@ -63,7 +65,7 @@ type SingletonClientGenerator struct { func (p *SingletonClientGenerator) KubeClient() (kubernetes.Interface, error) { var err error p.Once.Do(func() { - p.client, err = NewKubeClient(p.KubeConfig, p.KubeMaster) + p.client, err = NewKubeClient(p.KubeConfig, p.KubeMaster, p.RequestTimeout) }) return p.client, err } @@ -108,7 +110,7 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err // NewKubeClient returns a new Kubernetes client object. It takes a Config and // uses KubeMaster and KubeConfig attributes to connect to the cluster. If // KubeConfig isn't provided it defaults to using the recommended default. -func NewKubeClient(kubeConfig, kubeMaster string) (*kubernetes.Clientset, error) { +func NewKubeClient(kubeConfig, kubeMaster string, requestTimeout time.Duration) (*kubernetes.Clientset, error) { if kubeConfig == "" { if _, err := os.Stat(clientcmd.RecommendedHomeFile); err == nil { kubeConfig = clientcmd.RecommendedHomeFile @@ -129,6 +131,8 @@ func NewKubeClient(kubeConfig, kubeMaster string) (*kubernetes.Clientset, error) }) } + config.Timeout = requestTimeout + client, err := kubernetes.NewForConfig(config) if err != nil { return nil, err