From 496974a31555d64f144d24247507c0ec457d7edd Mon Sep 17 00:00:00 2001 From: Jack Wotherspoon Date: Tue, 11 Jul 2023 13:24:49 -0400 Subject: [PATCH] feat: add PSC support to proxy (#1863) --- cmd/root.go | 16 ++++++ cmd/root_test.go | 108 ++++++++++++++++++++++++++++++++++++++++ internal/proxy/proxy.go | 12 +++++ 3 files changed, 136 insertions(+) diff --git a/cmd/root.go b/cmd/root.go index b831b4f0b..4fa36bd66 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -450,6 +450,8 @@ status code.`) "(*) Enables Automatic IAM Authentication for all instances") pflags.BoolVar(&c.conf.PrivateIP, "private-ip", false, "(*) Connect to the private ip address for all instances") + pflags.BoolVar(&c.conf.PSC, "psc", false, + "(*) Connect to the PSC endpoint for all instances") v := viper.NewWithOptions(viper.EnvKeyReplacer(strings.NewReplacer("-", "_"))) v.SetEnvPrefix(envPrefix) @@ -509,6 +511,11 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { return newBadCommandError("cannot specify --private-ip and --auto-ip together") } + // If more than one IP type is set, error. + if conf.PrivateIP && conf.PSC { + return newBadCommandError("cannot specify --private-ip and --psc flags at the same time") + } + // If more than one auth method is set, error. if conf.Token != "" && conf.CredentialsFile != "" { return newBadCommandError("cannot specify --token and --credentials-file flags at the same time") @@ -666,6 +673,15 @@ and re-try with just --auto-iam-authn`) return newBadCommandError("cannot use --auto-ip with private-ip") } + ic.PSC, err = parseBoolOpt(q, "psc") + if err != nil { + return err + } + + if ic.PrivateIP != nil && ic.PSC != nil { + return newBadCommandError("cannot specify both private-ip and psc query params") + } + } ics = append(ics, ic) } diff --git a/cmd/root_test.go b/cmd/root_test.go index 6283e56fd..a64683c68 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -356,6 +356,22 @@ func TestNewCommandArguments(t *testing.T) { }}, }), }, + { + desc: "using the psc flag", + args: []string{"--psc", "proj:region:inst"}, + want: withDefaults(&proxy.Config{ + PSC: true, + }), + }, + { + desc: "using the psc flag query param", + args: []string{"proj:region:inst?psc=true"}, + want: withDefaults(&proxy.Config{ + Instances: []proxy.InstanceConnConfig{{ + PSC: pointer(true), + }}, + }), + }, { desc: "using the quota project flag", args: []string{"--quota-project", "proj", "proj:region:inst"}, @@ -638,6 +654,14 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { PrivateIP: true, }), }, + { + desc: "using the psc envvar", + envName: "CSQL_PROXY_PSC", + envValue: "true", + want: withDefaults(&proxy.Config{ + PSC: true, + }), + }, { desc: "using the quota project envvar", envName: "CSQL_PROXY_QUOTA_PROJECT", @@ -905,6 +929,79 @@ func TestPrivateIPQueryParams(t *testing.T) { } } +func TestPSCQueryParams(t *testing.T) { + tcs := []struct { + desc string + args []string + want *bool + }{ + { + desc: "when the query string is absent", + args: []string{"proj:region:inst"}, + want: nil, + }, + { + desc: "when the query string has no value", + args: []string{"proj:region:inst?psc"}, + want: pointer(true), + }, + { + desc: "when the query string is true", + args: []string{"proj:region:inst?psc=true"}, + want: pointer(true), + }, + { + desc: "when the query string is True", + args: []string{"proj:region:inst?psc=True"}, + want: pointer(true), + }, + { + desc: "when the query string is (short) T", + args: []string{"proj:region:inst?psc=T"}, + want: pointer(true), + }, + { + desc: "when the query string is (short) t", + args: []string{"proj:region:inst?psc=t"}, + want: pointer(true), + }, + { + desc: "when the query string is false", + args: []string{"proj:region:inst?psc=false"}, + want: pointer(false), + }, + { + desc: "when the query string is (short) f", + args: []string{"proj:region:inst?psc=f"}, + want: pointer(false), + }, + { + desc: "when the query string is False", + args: []string{"proj:region:inst?psc=False"}, + want: pointer(false), + }, + { + desc: "when the query string is (short) F", + args: []string{"proj:region:inst?psc=F"}, + want: pointer(false), + }, + } + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + c, err := invokeProxyCommand(tc.args) + if err != nil { + t.Fatalf("command.Execute: %v", err) + } + if tc.want == nil && c.conf.Instances[0].PSC == nil { + return + } + if got := c.conf.Instances[0].PSC; *got != *tc.want { + t.Errorf("args = %v, want = %v, got = %v", tc.args, *tc.want, *got) + } + }) + } +} + func TestNewCommandWithErrors(t *testing.T) { tcs := []struct { desc string @@ -1064,6 +1161,17 @@ func TestNewCommandWithErrors(t *testing.T) { "p:r:i?private-ip=true", }, }, + { + desc: "using private IP and psc query params", + args: []string{"p:r:i?private-ip=true&psc=true"}, + }, + { + desc: "using --private-ip with --psc", + args: []string{ + "--private-ip", "--psc", + "p:r:i", + }, + }, { desc: "run-connection-test with fuse", args: []string{ diff --git a/internal/proxy/proxy.go b/internal/proxy/proxy.go index 461db9597..5e1970b81 100644 --- a/internal/proxy/proxy.go +++ b/internal/proxy/proxy.go @@ -102,6 +102,10 @@ type InstanceConnConfig struct { // PrivateIP tells the proxy to attempt to connect to the db instance's // private IP address instead of the public IP address PrivateIP *bool + + // PSC tells the proxy to attempt to connect to the db instance's + // private service connect endpoint + PSC *bool } // Config contains all the configuration provided by the caller. @@ -168,6 +172,10 @@ type Config struct { // for all instances. PrivateIP bool + // PSC enables connections via the database server's private service connect + // endpoint for all instances + PSC bool + // AutoIP supports a legacy behavior where the Proxy will connect to // the first IP address returned from the SQL ADmin API response. This // setting should be avoided and used only to support legacy Proxy @@ -253,6 +261,10 @@ func dialOptions(c Config, i InstanceConnConfig) []cloudsqlconn.DialOption { // add the option. case i.PrivateIP != nil && *i.PrivateIP || i.PrivateIP == nil && c.PrivateIP: opts = append(opts, cloudsqlconn.WithPrivateIP()) + // If PSC is enabled at the instance level, or PSC is enabled globally + // add the option. + case i.PSC != nil && *i.PSC || i.PSC == nil && c.PSC: + opts = append(opts, cloudsqlconn.WithPSC()) case c.AutoIP: opts = append(opts, cloudsqlconn.WithAutoIP()) default: