diff --git a/cmd/options.go b/cmd/options.go index aceb01843..e96f6f9b3 100644 --- a/cmd/options.go +++ b/cmd/options.go @@ -14,7 +14,11 @@ package cmd -import "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/cloudsql" +import ( + "time" + + "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/cloudsql" +) // Option is a function that configures a Command. type Option func(*Command) @@ -98,6 +102,13 @@ func WithLazyRefresh() Option { } } +// WithFailoverPeriod configures the frequency to check for changes to DNS names. +func WithFailoverPeriod(d time.Duration) Option { + return func(c *Command) { + c.conf.FailoverPeriod = &d + } +} + // WithConnRefuseNotify configures the Proxy to call the provided function when // a connection is refused. The notification function is run in a goroutine. func WithConnRefuseNotify(n func()) Option { diff --git a/cmd/options_test.go b/cmd/options_test.go index c3dc718f6..1de2e5d00 100644 --- a/cmd/options_test.go +++ b/cmd/options_test.go @@ -20,6 +20,7 @@ import ( "io" "runtime" "testing" + "time" "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/cloudsql" "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/internal/log" @@ -146,6 +147,16 @@ func TestCommandOptions(t *testing.T) { }, option: WithLazyRefresh(), }, + { + desc: "with failover period", + isValid: func(c *Command) error { + if c.conf.FailoverPeriod == nil || *c.conf.FailoverPeriod != 10*time.Second { + return fmt.Errorf("want = %v, got = %v", 10*time.Second, c.conf.FailoverPeriod) + } + return nil + }, + option: WithFailoverPeriod(10 * time.Second), + }, } for _, tc := range tcs { diff --git a/cmd/root.go b/cmd/root.go index 76eeb8913..9f52bc743 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -94,6 +94,7 @@ type Command struct { dialer cloudsql.Dialer cleanup func() error connRefuseNotify func() + failoverPeriod time.Duration } var longHelp = ` @@ -596,6 +597,9 @@ CPU may be throttled and a background refresh cannot run reliably localFlags.DurationVar(&c.conf.ResourceExhaustedCooldownPeriod, "resource-exhausted-cooldown-delay", 0, `Cooldown period after a ResourceExhausted error.`, ) + localFlags.DurationVar(&c.failoverPeriod, "failover-period", 30*time.Second, + `Frequency to check for changes to DNS names. When set to 0, DNS polling is disabled.`, + ) localFlags.StringVar(&c.conf.SQLDataEndpoint, "sqldata-api-endpoint", "", "Override the SQL Data API endpoint", ) @@ -896,6 +900,10 @@ and re-try with just --auto-iam-authn`) conf.UserAgent = userAgent } + if userHasSetLocal(cmd, "failover-period") { + conf.FailoverPeriod = &cmd.failoverPeriod + } + if userHasSetLocal(cmd, "sqladmin-api-endpoint") && userHasSetLocal(cmd, "universe-domain") { return newBadCommandError("cannot specify --sqladmin-api-endpoint and --universe-domain at the same time") } diff --git a/cmd/root_test.go b/cmd/root_test.go index e7d870485..aed275070 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -506,6 +506,20 @@ func TestNewCommandArguments(t *testing.T) { ResourceExhaustedCooldownPeriod: 10 * time.Second, }), }, + { + desc: "using the failover-period flag", + args: []string{"--failover-period", "10s", "proj:region:inst"}, + want: withDefaults(&proxy.Config{ + FailoverPeriod: pointer(10 * time.Second), + }), + }, + { + desc: "using the failover-period flag set to 0", + args: []string{"--failover-period", "0s", "proj:region:inst"}, + want: withDefaults(&proxy.Config{ + FailoverPeriod: pointer(time.Duration(0)), + }), + }, } for _, tc := range tcs { @@ -885,6 +899,22 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { SQLDataEndpoint: "https://test.googleapis.com", }), }, + { + desc: "using the failover-period envvar", + envName: "CSQL_PROXY_FAILOVER_PERIOD", + envValue: "15s", + want: withDefaults(&proxy.Config{ + FailoverPeriod: pointer(15 * time.Second), + }), + }, + { + desc: "using the failover-period envvar set to 0", + envName: "CSQL_PROXY_FAILOVER_PERIOD", + envValue: "0s", + want: withDefaults(&proxy.Config{ + FailoverPeriod: pointer(time.Duration(0)), + }), + }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { diff --git a/docs/cmd/cloud-sql-proxy.md b/docs/cmd/cloud-sql-proxy.md index fc619b4d4..d50f9e1a8 100644 --- a/docs/cmd/cloud-sql-proxy.md +++ b/docs/cmd/cloud-sql-proxy.md @@ -243,6 +243,7 @@ cloud-sql-proxy INSTANCE_CONNECTION_NAME... [flags] --disable-metrics Disable Cloud Monitoring integration (used with --telemetry-project) --disable-traces Disable Cloud Trace integration (used with --telemetry-project) --exit-zero-on-sigterm Exit with 0 exit code when Sigterm received (default is 143) + --failover-period duration Frequency to check for changes to DNS names. When set to 0, DNS polling is disabled. (default 30s) --fuse string Mount a directory at the path using FUSE to access Cloud SQL instances. --fuse-tmp-dir string Temp dir for Unix sockets created with FUSE (default "/tmp/csql-tmp") -g, --gcloud-auth Use gclouds user credentials as a source of IAM credentials. diff --git a/internal/proxy/proxy.go b/internal/proxy/proxy.go index 5c8732614..a0608eb91 100644 --- a/internal/proxy/proxy.go +++ b/internal/proxy/proxy.go @@ -202,6 +202,10 @@ type Config struct { // of a request context, e.g., Cloud Run. LazyRefresh bool + // FailoverPeriod configures the frequency to check for changes to DNS names. + // When set to 0, DNS polling is disabled and checks only occur on new connections. + FailoverPeriod *time.Duration + // ResourceExhaustedCooldownPeriod sets the cooldown period after a ResourceExhausted error. ResourceExhaustedCooldownPeriod time.Duration @@ -482,6 +486,10 @@ func (c *Config) DialerOptions(l cloudsql.Logger) ([]cloudsqlconn.Option, error) opts = append(opts, cloudsqlconn.WithLazyRefresh()) } + if c.FailoverPeriod != nil { + opts = append(opts, cloudsqlconn.WithFailoverPeriod(*c.FailoverPeriod)) + } + if c.ResourceExhaustedCooldownPeriod != 0 { opts = append(opts, cloudsqlconn.WithResourceExhaustedCooldownPeriod(c.ResourceExhaustedCooldownPeriod)) } diff --git a/internal/proxy/proxy_test.go b/internal/proxy/proxy_test.go index c529d6a84..0a3365770 100644 --- a/internal/proxy/proxy_test.go +++ b/internal/proxy/proxy_test.go @@ -940,3 +940,17 @@ func TestProxyMultiInstances(t *testing.T) { }) } } + +func TestConfigDialerOptionsFailoverPeriod(t *testing.T) { + d := 15 * time.Second + c := &proxy.Config{ + FailoverPeriod: &d, + } + opts, err := c.DialerOptions(testLogger) + if err != nil { + t.Fatalf("want err = nil, got = %v", err) + } + if len(opts) == 0 { + t.Fatalf("expected options to be returned, got none") + } +}