From 007f267bc23c433485b7b6884f0bc645ac78abe2 Mon Sep 17 00:00:00 2001 From: Eno Compton Date: Wed, 26 Oct 2022 13:30:31 -0600 Subject: [PATCH 1/9] feat: configure the proxy with environment variables Fixes #225. --- README.md | 1 + cmd/root.go | 139 +++++++++--- cmd/root_test.go | 553 +++++++++++++++++++++++++++++++++++++++++++---- go.mod | 13 ++ go.sum | 35 +++ 5 files changed, 666 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index a55eee456..5093faa72 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ The Cloud SQL Auth proxy has support for: - [HTTP Healthchecks][health-check-example] - Service account impersonation - Separate Dialer functionality released as the [Cloud SQL Go Connector][go connector] +- Configuration with environment variables - Fully POSIX-compliant flags If you're using Go, Java, or Python, consider using the corresponding Cloud SQL diff --git a/cmd/root.go b/cmd/root.go index 7b0884eb3..fd6272521 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -38,6 +38,8 @@ import ( "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/internal/log" "github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/internal/proxy" "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/spf13/viper" "go.opencensus.io/trace" ) @@ -71,6 +73,7 @@ type Command struct { conf *proxy.Config logger cloudsql.Logger dialer cloudsql.Dialer + v *viper.Viper cleanup func() error disableTraces bool @@ -207,10 +210,67 @@ Service Account Impersonation SERVICE_ACCOUNT_3 which impersonates SERVICE_ACCOUNT_2 which then impersonates the target SERVICE_ACCOUNT_1. +Configuration using environment variables + + Instead of using CLI flags, the proxy may be configured using environment + variables. Each environment variable uses "CLOUD_SQL" as a prefix and is the + uppercase version of the flag using underscores as word delimiters. For + example, the --auto-iam-authn flag may be set with the environment variable + CLOUD_SQL_AUTO_IAM_AUTHN. An invocation of the proxy using environment + variables would look like the following: + + CLOUD_SQL_AUTO_IAM_AUTHN=true \ + ./cloud-sql-proxy my-project:us-central1:my-db-server + + In addition to CLI flags, instance connection names may also be specified + with environment variables. If invoking the proxy with only one instance + connection name, use CLOUD_SQL_INSTANCE_CONNECTION_NAME. For example: + + CLOUD_SQL_INSTANCE_CONNECTION_NAME=my-project:us-central1:my-db-server \ + ./cloud-sql-proxy + + If multiple instance connection names are used, add the index of the + instance connection name as a suffix. For example: + + CLOUD_SQL_INSTANCE_CONNECTION_NAME_0=my-project:us-central1:my-db-server \ + CLOUD_SQL_INSTANCE_CONNECTION_NAME_1=my-other-project:us-central1:my-other-server \ + ./cloud-sql-proxy + (*) indicates a flag that may be used as a query parameter ` +const envPrefix = "CLOUD_SQL" + +func instanceConnectionNames(args []string) []string { + // If args is already populated from a CLI invocation, ignore any instance + // connection name env vars and return the CLI args. + if len(args) > 0 { + return args + } + inst := os.Getenv(fmt.Sprintf("%s_INSTANCE_CONNECTION_NAME", envPrefix)) + if inst == "" { + inst = os.Getenv(fmt.Sprintf("%s_INSTANCE_CONNECTION_NAME_0", envPrefix)) + if inst == "" { + return nil + } + } + args = append(args, inst) + + i := 1 + for { + instN := os.Getenv(fmt.Sprintf("%s_INSTANCE_CONNECTION_NAME_%d", envPrefix, i)) + // if the next instance connection name is not defined, stop checking + // environment variables. + if instN == "" { + break + } + args = append(args, instN) + i++ + } + return args +} + // NewCommand returns a Command object representing an invocation of the proxy. func NewCommand(opts ...Option) *Command { cmd := &cobra.Command{ @@ -234,6 +294,7 @@ func NewCommand(opts ...Option) *Command { } cmd.Args = func(cmd *cobra.Command, args []string) error { + args = instanceConnectionNames(args) // Handle logger separately from config if c.conf.StructuredLogs { c.logger, c.cleanup = log.NewStructuredLogger() @@ -254,74 +315,93 @@ func NewCommand(opts ...Option) *Command { cmd.RunE = func(*cobra.Command, []string) error { return runSignalWrapper(c) } + pflags := cmd.PersistentFlags() + // Override Cobra's default messages. - cmd.PersistentFlags().BoolP("help", "h", false, "Display help information for cloud-sql-proxy") - cmd.PersistentFlags().BoolP("version", "v", false, "Print the cloud-sql-proxy version") + pflags.BoolP("help", "h", false, "Display help information for cloud-sql-proxy") + pflags.BoolP("version", "v", false, "Print the cloud-sql-proxy version") // Global-only flags - cmd.PersistentFlags().StringVarP(&c.conf.Token, "token", "t", "", + pflags.StringVarP(&c.conf.Token, "token", "t", "", "Use bearer token as a source of IAM credentials.") - cmd.PersistentFlags().StringVarP(&c.conf.CredentialsFile, "credentials-file", "c", "", + pflags.StringVarP(&c.conf.CredentialsFile, "credentials-file", "c", "", "Use service account key file as a source of IAM credentials.") - cmd.PersistentFlags().StringVarP(&c.conf.CredentialsJSON, "json-credentials", "j", "", + pflags.StringVarP(&c.conf.CredentialsJSON, "json-credentials", "j", "", "Use service account key JSON as a source of IAM credentials.") - cmd.PersistentFlags().BoolVarP(&c.conf.GcloudAuth, "gcloud-auth", "g", false, + pflags.BoolVarP(&c.conf.GcloudAuth, "gcloud-auth", "g", false, "Use gcloud's user credentials as a source of IAM credentials.") - cmd.PersistentFlags().BoolVarP(&c.conf.StructuredLogs, "structured-logs", "l", false, + pflags.BoolVarP(&c.conf.StructuredLogs, "structured-logs", "l", false, "Enable structured logging with LogEntry format") - cmd.PersistentFlags().Uint64Var(&c.conf.MaxConnections, "max-connections", 0, + pflags.Uint64Var(&c.conf.MaxConnections, "max-connections", 0, "Limit the number of connections. Default is no limit.") - cmd.PersistentFlags().DurationVar(&c.conf.WaitOnClose, "max-sigterm-delay", 0, + pflags.DurationVar(&c.conf.WaitOnClose, "max-sigterm-delay", 0, "Maximum number of seconds to wait for connections to close after receiving a TERM signal.") - cmd.PersistentFlags().StringVar(&c.telemetryProject, "telemetry-project", "", + pflags.StringVar(&c.telemetryProject, "telemetry-project", "", "Enable Cloud Monitoring and Cloud Trace with the provided project ID.") - cmd.PersistentFlags().BoolVar(&c.disableTraces, "disable-traces", false, + pflags.BoolVar(&c.disableTraces, "disable-traces", false, "Disable Cloud Trace integration (used with --telemetry-project)") - cmd.PersistentFlags().IntVar(&c.telemetryTracingSampleRate, "telemetry-sample-rate", 10_000, + pflags.IntVar(&c.telemetryTracingSampleRate, "telemetry-sample-rate", 10_000, "Set the Cloud Trace sample rate. A smaller number means more traces.") - cmd.PersistentFlags().BoolVar(&c.disableMetrics, "disable-metrics", false, + pflags.BoolVar(&c.disableMetrics, "disable-metrics", false, "Disable Cloud Monitoring integration (used with --telemetry-project)") - cmd.PersistentFlags().StringVar(&c.telemetryPrefix, "telemetry-prefix", "", + pflags.StringVar(&c.telemetryPrefix, "telemetry-prefix", "", "Prefix for Cloud Monitoring metrics.") - cmd.PersistentFlags().BoolVar(&c.prometheus, "prometheus", false, + pflags.BoolVar(&c.prometheus, "prometheus", false, "Enable Prometheus HTTP endpoint /metrics on localhost") - cmd.PersistentFlags().StringVar(&c.prometheusNamespace, "prometheus-namespace", "", + pflags.StringVar(&c.prometheusNamespace, "prometheus-namespace", "", "Use the provided Prometheus namespace for metrics") - cmd.PersistentFlags().StringVar(&c.httpAddress, "http-address", "localhost", + pflags.StringVar(&c.httpAddress, "http-address", "localhost", "Address for Prometheus and health check server") - cmd.PersistentFlags().StringVar(&c.httpPort, "http-port", "9090", + pflags.StringVar(&c.httpPort, "http-port", "9090", "Port for Prometheus and health check server") - cmd.PersistentFlags().BoolVar(&c.healthCheck, "health-check", false, + pflags.BoolVar(&c.healthCheck, "health-check", false, "Enables health check endpoints /startup, /liveness, and /readiness on localhost.") - cmd.PersistentFlags().StringVar(&c.conf.APIEndpointURL, "sqladmin-api-endpoint", "", + pflags.StringVar(&c.conf.APIEndpointURL, "sqladmin-api-endpoint", "", "API endpoint for all Cloud SQL Admin API requests. (default: https://sqladmin.googleapis.com)") - cmd.PersistentFlags().StringVar(&c.conf.QuotaProject, "quota-project", "", + pflags.StringVar(&c.conf.QuotaProject, "quota-project", "", `Specifies the project to use for Cloud SQL Admin API quota tracking. The IAM principal must have the "serviceusage.services.use" permission for the given project. See https://cloud.google.com/service-usage/docs/overview and https://cloud.google.com/storage/docs/requester-pays`) - cmd.PersistentFlags().StringVar(&c.conf.FUSEDir, "fuse", "", + pflags.StringVar(&c.conf.FUSEDir, "fuse", "", "Mount a directory at the path using FUSE to access Cloud SQL instances.") - cmd.PersistentFlags().StringVar(&c.conf.FUSETempDir, "fuse-tmp-dir", + pflags.StringVar(&c.conf.FUSETempDir, "fuse-tmp-dir", filepath.Join(os.TempDir(), "csql-tmp"), "Temp dir for Unix sockets created with FUSE") - cmd.PersistentFlags().StringVar(&c.impersonationChain, "impersonate-service-account", "", + pflags.StringVar(&c.impersonationChain, "impersonate-service-account", "", `Comma separated list of service accounts to impersonate. Last value is the target account.`) cmd.PersistentFlags().BoolVar(&c.quiet, "quiet", false, "Log error messages only") // Global and per instance flags - cmd.PersistentFlags().StringVarP(&c.conf.Addr, "address", "a", "127.0.0.1", + pflags.StringVarP(&c.conf.Addr, "address", "a", "127.0.0.1", "(*) Address to bind Cloud SQL instance listeners.") - cmd.PersistentFlags().IntVarP(&c.conf.Port, "port", "p", 0, + pflags.IntVarP(&c.conf.Port, "port", "p", 0, "(*) Initial port for listeners. Subsequent listeners increment from this value.") - cmd.PersistentFlags().StringVarP(&c.conf.UnixSocket, "unix-socket", "u", "", + pflags.StringVarP(&c.conf.UnixSocket, "unix-socket", "u", "", `(*) Enables Unix sockets for all listeners with the provided directory.`) - cmd.PersistentFlags().BoolVarP(&c.conf.IAMAuthN, "auto-iam-authn", "i", false, + pflags.BoolVarP(&c.conf.IAMAuthN, "auto-iam-authn", "i", false, "(*) Enables Automatic IAM Authentication for all instances") - cmd.PersistentFlags().BoolVar(&c.conf.PrivateIP, "private-ip", false, + pflags.BoolVar(&c.conf.PrivateIP, "private-ip", false, "(*) Connect to the private ip address for all instances") + v := viper.NewWithOptions(viper.EnvKeyReplacer(strings.NewReplacer("-", "_"))) + v.SetEnvPrefix(envPrefix) + v.AutomaticEnv() + if err := v.BindPFlags(pflags); err != nil { + panic(err) + } + + pflags.VisitAll(func(f *pflag.Flag) { + // When the flag has not been set, but there is a Viper value, set the + // flag to the Viper value. This removes the need to manually assign + // Viper values into the proxy.Config for each value. + if !f.Changed && v.IsSet(f.Name) { + val := v.Get(f.Name) + pflags.Set(f.Name, fmt.Sprintf("%v", val)) + } + }) + return c } @@ -493,6 +573,7 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { } conf.Instances = ics + cmd.conf = conf return nil } diff --git a/cmd/root_test.go b/cmd/root_test.go index 66879144a..2ad7e4caf 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -31,31 +31,33 @@ import ( "github.com/spf13/cobra" ) -func TestNewCommandArguments(t *testing.T) { - - // saving true in a variable so we can take its address. - trueValue := true - - withDefaults := func(c *proxy.Config) *proxy.Config { - if c.UserAgent == "" { - c.UserAgent = userAgent - } - if c.Addr == "" { - c.Addr = "127.0.0.1" - } - if c.FUSEDir == "" { - if c.Instances == nil { - c.Instances = []proxy.InstanceConnConfig{{}} - } - if i := &c.Instances[0]; i.Name == "" { - i.Name = "proj:region:inst" - } +func withDefaults(c *proxy.Config) *proxy.Config { + if c.UserAgent == "" { + c.UserAgent = userAgent + } + if c.Addr == "" { + c.Addr = "127.0.0.1" + } + if c.FUSEDir == "" { + if c.Instances == nil { + c.Instances = []proxy.InstanceConnConfig{{}} } - if c.FUSETempDir == "" { - c.FUSETempDir = filepath.Join(os.TempDir(), "csql-tmp") + if i := &c.Instances[0]; i.Name == "" { + i.Name = "proj:region:inst" } - return c } + if c.FUSETempDir == "" { + c.FUSETempDir = filepath.Join(os.TempDir(), "csql-tmp") + } + return c +} + +// pointer returns a pointer to v +func pointer[T any](v T) *T { + return &v +} + +func TestNewCommandArguments(t *testing.T) { tcs := []struct { desc string args []string @@ -231,7 +233,7 @@ func TestNewCommandArguments(t *testing.T) { args: []string{"proj:region:inst?auto-iam-authn=true"}, want: withDefaults(&proxy.Config{ Instances: []proxy.InstanceConnConfig{{ - IAMAuthN: &trueValue, + IAMAuthN: pointer(true), }}, }), }, @@ -268,7 +270,7 @@ func TestNewCommandArguments(t *testing.T) { args: []string{"proj:region:inst?private-ip=true"}, want: withDefaults(&proxy.Config{ Instances: []proxy.InstanceConnConfig{{ - PrivateIP: &trueValue, + PrivateIP: pointer(true), }}, }), }, @@ -280,7 +282,7 @@ func TestNewCommandArguments(t *testing.T) { }), }, { - desc: "", + desc: "using the impersonate service account flag", args: []string{"--impersonate-service-account", "sv1@developer.gserviceaccount.com,sv2@developer.gserviceaccount.com,sv3@developer.gserviceaccount.com", "proj:region:inst"}, @@ -318,11 +320,474 @@ func TestNewCommandArguments(t *testing.T) { } } -func TestAutoIAMAuthNQueryParams(t *testing.T) { - // saving true and false in a variable so we can take its address - trueValue := true - falseValue := false +func TestNewCommandWithEnvironmentConfigPrivateFields(t *testing.T) { + tcs := []struct { + desc string + // sets an env var before the test, and returns a cleanup function + setEnv func() func() + isValid func(cmd *Command) bool + }{ + { + desc: "using the disable traces envvar", + setEnv: func() func() { + key := "CLOUD_SQL_DISABLE_TRACES" + os.Setenv(key, "true") + return func() { os.Unsetenv(key) } + }, + isValid: func(cmd *Command) bool { + return cmd.disableTraces == true + }, + }, + { + desc: "using the telemetry sample rate envvar", + setEnv: func() func() { + key := "CLOUD_SQL_TELEMETRY_SAMPLE_RATE" + os.Setenv(key, "500") + return func() { os.Unsetenv(key) } + }, + isValid: func(cmd *Command) bool { + return cmd.telemetryTracingSampleRate == 500 + }, + }, + { + desc: "using the disable metrics envvar", + setEnv: func() func() { + key := "CLOUD_SQL_DISABLE_METRICS" + os.Setenv(key, "true") + return func() { os.Unsetenv(key) } + }, + isValid: func(cmd *Command) bool { + return cmd.disableMetrics == true + }, + }, + { + desc: "using the telemetry project envvar", + setEnv: func() func() { + key := "CLOUD_SQL_TELEMETRY_PROJECT" + os.Setenv(key, "mycoolproject") + return func() { os.Unsetenv(key) } + }, + isValid: func(cmd *Command) bool { + return cmd.telemetryProject == "mycoolproject" + }, + }, + { + desc: "using the telemetry prefix envvar", + setEnv: func() func() { + key := "CLOUD_SQL_TELEMETRY_PREFIX" + os.Setenv(key, "myprefix") + return func() { os.Unsetenv(key) } + }, + isValid: func(cmd *Command) bool { + return cmd.telemetryPrefix == "myprefix" + }, + }, + { + desc: "using the prometheus envvar", + setEnv: func() func() { + key := "CLOUD_SQL_PROMETHEUS" + os.Setenv(key, "true") + return func() { os.Unsetenv(key) } + }, + isValid: func(cmd *Command) bool { + return cmd.prometheus == true + }, + }, + { + desc: "using the prometheus namespace envvar", + setEnv: func() func() { + key := "CLOUD_SQL_PROMETHEUS_NAMESPACE" + os.Setenv(key, "myns") + return func() { os.Unsetenv(key) } + }, + isValid: func(cmd *Command) bool { + return cmd.prometheusNamespace == "myns" + }, + }, + { + desc: "using the health check envvar", + setEnv: func() func() { + key := "CLOUD_SQL_HEALTH_CHECK" + os.Setenv(key, "true") + return func() { os.Unsetenv(key) } + }, + isValid: func(cmd *Command) bool { + return cmd.healthCheck == true + }, + }, + { + desc: "using the http address envvar", + setEnv: func() func() { + key := "CLOUD_SQL_HTTP_ADDRESS" + os.Setenv(key, "0.0.0.0") + return func() { os.Unsetenv(key) } + }, + isValid: func(cmd *Command) bool { + return cmd.httpAddress == "0.0.0.0" + }, + }, + { + desc: "using the http port envvar", + setEnv: func() func() { + key := "CLOUD_SQL_HTTP_PORT" + os.Setenv(key, "5555") + return func() { os.Unsetenv(key) } + }, + isValid: func(cmd *Command) bool { + return cmd.httpPort == "5555" + }, + }, + } + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + cleanup := tc.setEnv() + defer cleanup() + + c := NewCommand() + // Keep the test output quiet + c.SilenceUsage = true + c.SilenceErrors = true + // Disable execute behavior + c.RunE = func(*cobra.Command, []string) error { + return nil + } + c.SetArgs([]string{"proj:region:inst"}) + + err := c.Execute() + if err != nil { + t.Fatalf("want error = nil, got = %v", err) + } + + if !tc.isValid(c) { + t.Fatal("want valid, got invalid") + } + }) + } +} + +func TestNewCommandWithEnvironmentConfigInstanceConnectionName(t *testing.T) { + tcs := []struct { + desc string + // sets an env var before the test, and returns a cleanup function + setEnv func() func() + args []string + want *proxy.Config + }{ + { + desc: "with one instance connection name", + setEnv: func() func() { + key := "CLOUD_SQL_INSTANCE_CONNECTION_NAME" + os.Setenv(key, "proj:reg:inst") + return func() { os.Unsetenv(key) } + }, + want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ + {Name: "proj:reg:inst"}, + }}), + }, + { + desc: "with multiple instance connection names", + setEnv: func() func() { + key0 := "CLOUD_SQL_INSTANCE_CONNECTION_NAME_0" + key1 := "CLOUD_SQL_INSTANCE_CONNECTION_NAME_1" + os.Setenv(key0, "proj:reg:inst0") + os.Setenv(key1, "proj:reg:inst1") + return func() { + os.Unsetenv(key0) + os.Unsetenv(key1) + } + }, + want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ + {Name: "proj:reg:inst0"}, + {Name: "proj:reg:inst1"}, + }}), + }, + { + desc: "with query params", + setEnv: func() func() { + key := "CLOUD_SQL_INSTANCE_CONNECTION_NAME_0" + os.Setenv(key, "proj:reg:inst0?auto-iam-authn=true") + return func() { + os.Unsetenv(key) + } + }, + want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ + {Name: "proj:reg:inst0", IAMAuthN: pointer(true)}, + }}), + }, + { + desc: "when the index skips a number", + setEnv: func() func() { + key0 := "CLOUD_SQL_INSTANCE_CONNECTION_NAME_0" + key1 := "CLOUD_SQL_INSTANCE_CONNECTION_NAME_2" + os.Setenv(key0, "proj:reg:inst0") + os.Setenv(key1, "proj:reg:inst1") + return func() { + os.Unsetenv(key0) + os.Unsetenv(key1) + } + }, + want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ + {Name: "proj:reg:inst0"}, + }}), + }, + { + desc: "when there are CLI args provided", + setEnv: func() func() { + key := "CLOUD_SQL_INSTANCE_CONNECTION_NAME" + os.Setenv(key, "proj:reg:inst0") + return func() { os.Unsetenv(key) } + }, + args: []string{"myotherproj:myreg:myinst"}, + want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ + {Name: "myotherproj:myreg:myinst"}, + }}), + }, + { + desc: "when only an index instance connection name is defined", + setEnv: func() func() { + key := "CLOUD_SQL_INSTANCE_CONNECTION_NAME_0" + os.Setenv(key, "proj:reg:inst0") + return func() { os.Unsetenv(key) } + }, + want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ + {Name: "proj:reg:inst0"}, + }}), + }, + } + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + cleanup := tc.setEnv() + defer cleanup() + + c := NewCommand() + // Keep the test output quiet + c.SilenceUsage = true + c.SilenceErrors = true + // Disable execute behavior + c.RunE = func(*cobra.Command, []string) error { + return nil + } + c.SetArgs(tc.args) + + err := c.Execute() + if err != nil { + t.Fatalf("want error = nil, got = %v", err) + } + + if got := c.conf; !cmp.Equal(tc.want, got) { + t.Fatalf("want = %#v\ngot = %#v\ndiff = %v", tc.want, got, cmp.Diff(tc.want, got)) + } + }) + } +} + +func TestNewCommandWithEnvironmentConfig(t *testing.T) { + tcs := []struct { + desc string + // sets an env var before the test, and returns a cleanup function + setEnv func() func() + want *proxy.Config + }{ + { + desc: "using the address envvar", + setEnv: func() func() { + key := "CLOUD_SQL_ADDRESS" + os.Setenv(key, "0.0.0.0") + return func() { os.Unsetenv(key) } + }, + want: withDefaults(&proxy.Config{ + Addr: "0.0.0.0", + }), + }, + { + desc: "using the port envvar", + setEnv: func() func() { + key := "CLOUD_SQL_PORT" + os.Setenv(key, "6000") + return func() { os.Unsetenv(key) } + }, + want: withDefaults(&proxy.Config{ + Port: 6000, + }), + }, + { + desc: "using the token envvar", + setEnv: func() func() { + key := "CLOUD_SQL_TOKEN" + os.Setenv(key, "MYCOOLTOKEN") + return func() { os.Unsetenv(key) } + }, + want: withDefaults(&proxy.Config{ + Token: "MYCOOLTOKEN", + }), + }, + { + desc: "using the credentiale file envvar", + setEnv: func() func() { + key := "CLOUD_SQL_CREDENTIALS_FILE" + os.Setenv(key, "/path/to/file") + return func() { os.Unsetenv(key) } + }, + want: withDefaults(&proxy.Config{ + CredentialsFile: "/path/to/file", + }), + }, + { + desc: "using the JSON credentials", + setEnv: func() func() { + key := "CLOUD_SQL_JSON_CREDENTIALS" + os.Setenv(key, `{"json":"goes-here"}`) + return func() { os.Unsetenv(key) } + }, + want: withDefaults(&proxy.Config{ + CredentialsJSON: `{"json":"goes-here"}`, + }), + }, + { + desc: "using the gcloud auth envvar", + setEnv: func() func() { + key := "CLOUD_SQL_GCLOUD_AUTH" + os.Setenv(key, "true") + return func() { os.Unsetenv(key) } + }, + want: withDefaults(&proxy.Config{ + GcloudAuth: true, + }), + }, + { + desc: "using the api-endpoint envvar", + setEnv: func() func() { + key := "CLOUD_SQL_SQLADMIN_API_ENDPOINT" + os.Setenv(key, "https://test.googleapis.com/") + return func() { os.Unsetenv(key) } + }, + want: withDefaults(&proxy.Config{ + APIEndpointURL: "https://test.googleapis.com/", + }), + }, + { + desc: "using the unix socket envvar", + setEnv: func() func() { + key := "CLOUD_SQL_UNIX_SOCKET" + os.Setenv(key, "/path/to/dir/") + return func() { os.Unsetenv(key) } + }, + want: withDefaults(&proxy.Config{ + UnixSocket: "/path/to/dir/", + }), + }, + { + desc: "using the iam authn login envvar", + setEnv: func() func() { + key := "CLOUD_SQL_AUTO_IAM_AUTHN" + os.Setenv(key, "true") + return func() { os.Unsetenv(key) } + }, + want: withDefaults(&proxy.Config{ + IAMAuthN: true, + }), + }, + { + desc: "enabling structured logging", + setEnv: func() func() { + key := "CLOUD_SQL_STRUCTURED_LOGS" + os.Setenv(key, "true") + return func() { os.Unsetenv(key) } + }, + want: withDefaults(&proxy.Config{ + StructuredLogs: true, + }), + }, + { + desc: "using the max connections envvar", + setEnv: func() func() { + key := "CLOUD_SQL_MAX_CONNECTIONS" + os.Setenv(key, "1") + return func() { os.Unsetenv(key) } + }, + want: withDefaults(&proxy.Config{ + MaxConnections: 1, + }), + }, + { + desc: "using wait after signterm envvar", + setEnv: func() func() { + key := "CLOUD_SQL_MAX_SIGTERM_DELAY" + os.Setenv(key, "10s") + return func() { os.Unsetenv(key) } + }, + want: withDefaults(&proxy.Config{ + WaitOnClose: 10 * time.Second, + }), + }, + { + desc: "using the private-ip envvar", + setEnv: func() func() { + key := "CLOUD_SQL_PRIVATE_IP" + os.Setenv(key, "true") + return func() { os.Unsetenv(key) } + }, + want: withDefaults(&proxy.Config{ + PrivateIP: true, + }), + }, + { + desc: "using the quota project envvar", + setEnv: func() func() { + key := "CLOUD_SQL_QUOTA_PROJECT" + os.Setenv(key, "proj") + return func() { os.Unsetenv(key) } + }, + want: withDefaults(&proxy.Config{ + QuotaProject: "proj", + }), + }, + { + desc: "using the imopersonate service accounn envvar", + setEnv: func() func() { + key := "CLOUD_SQL_IMPERSONATE_SERVICE_ACCOUNT" + os.Setenv(key, + "sv1@developer.gserviceaccount.com,sv2@developer.gserviceaccount.com,sv3@developer.gserviceaccount.com", + ) + return func() { os.Unsetenv(key) } + }, + want: withDefaults(&proxy.Config{ + ImpersonateTarget: "sv1@developer.gserviceaccount.com", + ImpersonateDelegates: []string{ + "sv3@developer.gserviceaccount.com", + "sv2@developer.gserviceaccount.com", + }, + }), + }, + } + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + cleanup := tc.setEnv() + defer cleanup() + c := NewCommand() + // Keep the test output quiet + c.SilenceUsage = true + c.SilenceErrors = true + // Disable execute behavior + c.RunE = func(*cobra.Command, []string) error { + return nil + } + c.SetArgs([]string{"proj:region:inst"}) + + err := c.Execute() + if err != nil { + t.Fatalf("want error = nil, got = %v", err) + } + + if got := c.conf; !cmp.Equal(tc.want, got) { + t.Fatalf("want = %#v\ngot = %#v\ndiff = %v", tc.want, got, cmp.Diff(tc.want, got)) + } + }) + } +} + +func TestAutoIAMAuthNQueryParams(t *testing.T) { tcs := []struct { desc string args []string @@ -336,22 +801,22 @@ func TestAutoIAMAuthNQueryParams(t *testing.T) { { desc: "when the query string is true", args: []string{"proj:region:inst?auto-iam-authn=true"}, - want: &trueValue, + want: pointer(true), }, { desc: "when the query string is (short) t", args: []string{"proj:region:inst?auto-iam-authn=t"}, - want: &trueValue, + want: pointer(true), }, { desc: "when the query string is false", args: []string{"proj:region:inst?auto-iam-authn=false"}, - want: &falseValue, + want: pointer(false), }, { desc: "when the query string is (short) f", args: []string{"proj:region:inst?auto-iam-authn=f"}, - want: &falseValue, + want: pointer(false), }, } for _, tc := range tcs { @@ -379,10 +844,6 @@ func TestAutoIAMAuthNQueryParams(t *testing.T) { } func TestPrivateIPQueryParams(t *testing.T) { - // saving true and false in a variable so we can take its address - trueValue := true - falseValue := false - tcs := []struct { desc string args []string @@ -396,47 +857,47 @@ func TestPrivateIPQueryParams(t *testing.T) { { desc: "when the query string has no value", args: []string{"proj:region:inst?private-ip"}, - want: &trueValue, + want: pointer(true), }, { desc: "when the query string is true", args: []string{"proj:region:inst?private-ip=true"}, - want: &trueValue, + want: pointer(true), }, { desc: "when the query string is True", args: []string{"proj:region:inst?private-ip=True"}, - want: &trueValue, + want: pointer(true), }, { desc: "when the query string is (short) T", args: []string{"proj:region:inst?private-ip=T"}, - want: &trueValue, + want: pointer(true), }, { desc: "when the query string is (short) t", args: []string{"proj:region:inst?private-ip=t"}, - want: &trueValue, + want: pointer(true), }, { desc: "when the query string is false", args: []string{"proj:region:inst?private-ip=false"}, - want: &falseValue, + want: pointer(false), }, { desc: "when the query string is (short) f", args: []string{"proj:region:inst?private-ip=f"}, - want: &falseValue, + want: pointer(false), }, { desc: "when the query string is False", args: []string{"proj:region:inst?private-ip=False"}, - want: &falseValue, + want: pointer(false), }, { desc: "when the query string is (short) F", args: []string{"proj:region:inst?private-ip=F"}, - want: &falseValue, + want: pointer(false), }, } for _, tc := range tcs { diff --git a/go.mod b/go.mod index 94faaeaa0..19bc6b512 100644 --- a/go.mod +++ b/go.mod @@ -28,6 +28,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect @@ -37,6 +38,7 @@ require ( github.com/google/uuid v1.3.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect github.com/googleapis/gax-go/v2 v2.6.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgconn v1.13.0 // indirect @@ -47,14 +49,23 @@ require ( github.com/jackc/pgtype v1.12.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/lib/pq v1.10.6 // indirect + github.com/magiconair/properties v1.8.6 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/prometheus/client_golang v1.13.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect github.com/prometheus/prometheus v0.35.0 // indirect github.com/prometheus/statsd_exporter v0.22.7 // indirect + github.com/spf13/afero v1.8.2 // indirect + github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.13.0 // indirect + github.com/subosito/gotenv v1.4.1 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be // indirect @@ -66,5 +77,7 @@ require ( google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect google.golang.org/grpc v1.50.1 // indirect google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 8f28e82e7..cf2f9fb76 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -17,6 +18,7 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= @@ -248,6 +250,7 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= @@ -617,6 +620,8 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= @@ -813,6 +818,7 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -844,6 +850,7 @@ github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3i github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gophercloud/gophercloud v0.24.0/go.mod h1:Q8fZtyi5zZxPS/j9aj3sSxtvj41AdQMDwyo1myduD5c= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= @@ -896,6 +903,7 @@ github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -1033,6 +1041,8 @@ github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -1087,6 +1097,8 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= @@ -1177,6 +1189,10 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= +github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= @@ -1186,6 +1202,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -1295,8 +1312,12 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= +github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= @@ -1304,6 +1325,8 @@ github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSW github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -1312,6 +1335,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU= +github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -1333,6 +1358,8 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= +github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -1482,10 +1509,12 @@ golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -1739,6 +1768,7 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1890,6 +1920,7 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -2014,7 +2045,9 @@ google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -2159,6 +2192,8 @@ gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKW gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= From 16b5a9fbfaa3331c1fbbdd4a8b284fc89ac99828 Mon Sep 17 00:00:00 2001 From: Eno Compton Date: Wed, 26 Oct 2022 13:39:16 -0600 Subject: [PATCH 2/9] Consolidate proxy invocation --- cmd/root_test.go | 96 ++++++++++++------------------------------------ 1 file changed, 23 insertions(+), 73 deletions(-) diff --git a/cmd/root_test.go b/cmd/root_test.go index 2ad7e4caf..dd869c34d 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -57,6 +57,22 @@ func pointer[T any](v T) *T { return &v } +func invokeProxyCommand(args []string) (*Command, error) { + c := NewCommand() + // Keep the test output quiet + c.SilenceUsage = true + c.SilenceErrors = true + // Disable execute behavior + c.RunE = func(*cobra.Command, []string) error { + return nil + } + c.SetArgs(args) + + err := c.Execute() + + return c, err +} + func TestNewCommandArguments(t *testing.T) { tcs := []struct { desc string @@ -298,17 +314,7 @@ func TestNewCommandArguments(t *testing.T) { for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { - c := NewCommand() - // Keep the test output quiet - c.SilenceUsage = true - c.SilenceErrors = true - // Disable execute behavior - c.RunE = func(*cobra.Command, []string) error { - return nil - } - c.SetArgs(tc.args) - - err := c.Execute() + c, err := invokeProxyCommand(tc.args) if err != nil { t.Fatalf("want error = nil, got = %v", err) } @@ -443,17 +449,7 @@ func TestNewCommandWithEnvironmentConfigPrivateFields(t *testing.T) { cleanup := tc.setEnv() defer cleanup() - c := NewCommand() - // Keep the test output quiet - c.SilenceUsage = true - c.SilenceErrors = true - // Disable execute behavior - c.RunE = func(*cobra.Command, []string) error { - return nil - } - c.SetArgs([]string{"proj:region:inst"}) - - err := c.Execute() + c, err := invokeProxyCommand([]string{"proj:region:inst"}) if err != nil { t.Fatalf("want error = nil, got = %v", err) } @@ -559,17 +555,7 @@ func TestNewCommandWithEnvironmentConfigInstanceConnectionName(t *testing.T) { cleanup := tc.setEnv() defer cleanup() - c := NewCommand() - // Keep the test output quiet - c.SilenceUsage = true - c.SilenceErrors = true - // Disable execute behavior - c.RunE = func(*cobra.Command, []string) error { - return nil - } - c.SetArgs(tc.args) - - err := c.Execute() + c, err := invokeProxyCommand(tc.args) if err != nil { t.Fatalf("want error = nil, got = %v", err) } @@ -765,17 +751,7 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { cleanup := tc.setEnv() defer cleanup() - c := NewCommand() - // Keep the test output quiet - c.SilenceUsage = true - c.SilenceErrors = true - // Disable execute behavior - c.RunE = func(*cobra.Command, []string) error { - return nil - } - c.SetArgs([]string{"proj:region:inst"}) - - err := c.Execute() + c, err := invokeProxyCommand([]string{"proj:region:inst"}) if err != nil { t.Fatalf("want error = nil, got = %v", err) } @@ -821,15 +797,7 @@ func TestAutoIAMAuthNQueryParams(t *testing.T) { } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { - c := NewCommand() - // Keep the test output quiet - c.SilenceUsage = true - c.SilenceErrors = true - // Disable execute behavior - c.RunE = func(*cobra.Command, []string) error { return nil } - c.SetArgs(tc.args) - - err := c.Execute() + c, err := invokeProxyCommand(tc.args) if err != nil { t.Fatalf("command.Execute: %v", err) } @@ -902,15 +870,7 @@ func TestPrivateIPQueryParams(t *testing.T) { } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { - c := NewCommand() - // Keep the test output quiet - c.SilenceUsage = true - c.SilenceErrors = true - // Disable execute behavior - c.RunE = func(*cobra.Command, []string) error { return nil } - c.SetArgs(tc.args) - - err := c.Execute() + c, err := invokeProxyCommand(tc.args) if err != nil { t.Fatalf("command.Execute: %v", err) } @@ -1041,17 +1001,7 @@ func TestNewCommandWithErrors(t *testing.T) { for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { - c := NewCommand() - // Keep the test output quiet - c.SilenceUsage = true - c.SilenceErrors = true - // Disable execute behavior - c.RunE = func(*cobra.Command, []string) error { - return nil - } - c.SetArgs(tc.args) - - err := c.Execute() + _, err := invokeProxyCommand(tc.args) if err == nil { t.Fatal("want error != nil, got = nil") } From 174eadf88b2ccf12e43cf71bc28c274f44042183 Mon Sep 17 00:00:00 2001 From: Eno Compton Date: Wed, 26 Oct 2022 13:44:27 -0600 Subject: [PATCH 3/9] Remove panic with note about error --- cmd/root.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index fd6272521..d73e2ad34 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -388,9 +388,9 @@ is the target account.`) v := viper.NewWithOptions(viper.EnvKeyReplacer(strings.NewReplacer("-", "_"))) v.SetEnvPrefix(envPrefix) v.AutomaticEnv() - if err := v.BindPFlags(pflags); err != nil { - panic(err) - } + // Ignoring the error here since its only occurence is if one of the pflags + // is nil which is never the case here. + _ = v.BindPFlags(pflags) pflags.VisitAll(func(f *pflag.Flag) { // When the flag has not been set, but there is a Viper value, set the From 6960a220fc2a1ed927624ae44bf00a7343703347 Mon Sep 17 00:00:00 2001 From: Eno Compton Date: Wed, 26 Oct 2022 13:46:30 -0600 Subject: [PATCH 4/9] go mod tidy --- go.mod | 4 ++-- go.sum | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 19bc6b512..3b79072ff 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,8 @@ require ( github.com/jackc/pgx/v4 v4.17.2 github.com/microsoft/go-mssqldb v0.17.0 github.com/spf13/cobra v1.6.1 + github.com/spf13/pflag v1.0.5 + github.com/spf13/viper v1.13.0 go.opencensus.io v0.24.0 go.uber.org/zap v1.23.0 golang.org/x/oauth2 v0.1.0 @@ -63,8 +65,6 @@ require ( github.com/spf13/afero v1.8.2 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.13.0 // indirect github.com/subosito/gotenv v1.4.1 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect diff --git a/go.sum b/go.sum index cf2f9fb76..472a3f090 100644 --- a/go.sum +++ b/go.sum @@ -617,6 +617,7 @@ github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= @@ -1018,8 +1019,8 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= @@ -1270,6 +1271,7 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= From b65f923397ec513a6d359af21de7cd3aacb80971 Mon Sep 17 00:00:00 2001 From: Eno Compton Date: Wed, 26 Oct 2022 13:55:36 -0600 Subject: [PATCH 5/9] Remove unused field --- cmd/root.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/root.go b/cmd/root.go index d73e2ad34..c49a7f236 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -73,7 +73,6 @@ type Command struct { conf *proxy.Config logger cloudsql.Logger dialer cloudsql.Dialer - v *viper.Viper cleanup func() error disableTraces bool From 84b8ddfd3e92accf8016eb0d108572cf54bdf915 Mon Sep 17 00:00:00 2001 From: Eno Compton Date: Wed, 26 Oct 2022 13:58:13 -0600 Subject: [PATCH 6/9] Remove unused assignment --- cmd/root.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/root.go b/cmd/root.go index c49a7f236..1cb9f7343 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -572,7 +572,6 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { } conf.Instances = ics - cmd.conf = conf return nil } From 74a2c1dfe74af7106232b7418b53fc53ebf97865 Mon Sep 17 00:00:00 2001 From: Eno Compton Date: Mon, 31 Oct 2022 09:50:02 -0600 Subject: [PATCH 7/9] Tighten up test cases --- cmd/root_test.go | 321 +++++++++++++++++------------------------------ 1 file changed, 115 insertions(+), 206 deletions(-) diff --git a/cmd/root_test.go b/cmd/root_test.go index dd869c34d..06bab821a 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -328,117 +328,87 @@ func TestNewCommandArguments(t *testing.T) { func TestNewCommandWithEnvironmentConfigPrivateFields(t *testing.T) { tcs := []struct { - desc string - // sets an env var before the test, and returns a cleanup function - setEnv func() func() - isValid func(cmd *Command) bool + desc string + envName string + envValue string + isValid func(cmd *Command) bool }{ { - desc: "using the disable traces envvar", - setEnv: func() func() { - key := "CLOUD_SQL_DISABLE_TRACES" - os.Setenv(key, "true") - return func() { os.Unsetenv(key) } - }, + desc: "using the disable traces envvar", + envName: "CLOUD_SQL_DISABLE_TRACES", + envValue: "true", isValid: func(cmd *Command) bool { return cmd.disableTraces == true }, }, { - desc: "using the telemetry sample rate envvar", - setEnv: func() func() { - key := "CLOUD_SQL_TELEMETRY_SAMPLE_RATE" - os.Setenv(key, "500") - return func() { os.Unsetenv(key) } - }, + desc: "using the telemetry sample rate envvar", + envName: "CLOUD_SQL_TELEMETRY_SAMPLE_RATE", + envValue: "500", isValid: func(cmd *Command) bool { return cmd.telemetryTracingSampleRate == 500 }, }, { - desc: "using the disable metrics envvar", - setEnv: func() func() { - key := "CLOUD_SQL_DISABLE_METRICS" - os.Setenv(key, "true") - return func() { os.Unsetenv(key) } - }, + desc: "using the disable metrics envvar", + envName: "CLOUD_SQL_DISABLE_METRICS", + envValue: "true", isValid: func(cmd *Command) bool { return cmd.disableMetrics == true }, }, { - desc: "using the telemetry project envvar", - setEnv: func() func() { - key := "CLOUD_SQL_TELEMETRY_PROJECT" - os.Setenv(key, "mycoolproject") - return func() { os.Unsetenv(key) } - }, + desc: "using the telemetry project envvar", + envName: "CLOUD_SQL_TELEMETRY_PROJECT", + envValue: "mycoolproject", isValid: func(cmd *Command) bool { return cmd.telemetryProject == "mycoolproject" }, }, { - desc: "using the telemetry prefix envvar", - setEnv: func() func() { - key := "CLOUD_SQL_TELEMETRY_PREFIX" - os.Setenv(key, "myprefix") - return func() { os.Unsetenv(key) } - }, + desc: "using the telemetry prefix envvar", + envName: "CLOUD_SQL_TELEMETRY_PREFIX", + envValue: "myprefix", isValid: func(cmd *Command) bool { return cmd.telemetryPrefix == "myprefix" }, }, { - desc: "using the prometheus envvar", - setEnv: func() func() { - key := "CLOUD_SQL_PROMETHEUS" - os.Setenv(key, "true") - return func() { os.Unsetenv(key) } - }, + desc: "using the prometheus envvar", + envName: "CLOUD_SQL_PROMETHEUS", + envValue: "true", isValid: func(cmd *Command) bool { return cmd.prometheus == true }, }, { - desc: "using the prometheus namespace envvar", - setEnv: func() func() { - key := "CLOUD_SQL_PROMETHEUS_NAMESPACE" - os.Setenv(key, "myns") - return func() { os.Unsetenv(key) } - }, + desc: "using the prometheus namespace envvar", + envName: "CLOUD_SQL_PROMETHEUS_NAMESPACE", + envValue: "myns", isValid: func(cmd *Command) bool { return cmd.prometheusNamespace == "myns" }, }, { - desc: "using the health check envvar", - setEnv: func() func() { - key := "CLOUD_SQL_HEALTH_CHECK" - os.Setenv(key, "true") - return func() { os.Unsetenv(key) } - }, + desc: "using the health check envvar", + envName: "CLOUD_SQL_HEALTH_CHECK", + envValue: "true", isValid: func(cmd *Command) bool { return cmd.healthCheck == true }, }, { - desc: "using the http address envvar", - setEnv: func() func() { - key := "CLOUD_SQL_HTTP_ADDRESS" - os.Setenv(key, "0.0.0.0") - return func() { os.Unsetenv(key) } - }, + desc: "using the http address envvar", + envName: "CLOUD_SQL_HTTP_ADDRESS", + envValue: "0.0.0.0", isValid: func(cmd *Command) bool { return cmd.httpAddress == "0.0.0.0" }, }, { - desc: "using the http port envvar", - setEnv: func() func() { - key := "CLOUD_SQL_HTTP_PORT" - os.Setenv(key, "5555") - return func() { os.Unsetenv(key) } - }, + desc: "using the http port envvar", + envName: "CLOUD_SQL_HTTP_PORT", + envValue: "5555", isValid: func(cmd *Command) bool { return cmd.httpPort == "5555" }, @@ -446,8 +416,8 @@ func TestNewCommandWithEnvironmentConfigPrivateFields(t *testing.T) { } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { - cleanup := tc.setEnv() - defer cleanup() + os.Setenv(tc.envName, tc.envValue) + defer os.Unsetenv(tc.envName) c, err := invokeProxyCommand([]string{"proj:region:inst"}) if err != nil { @@ -464,17 +434,14 @@ func TestNewCommandWithEnvironmentConfigPrivateFields(t *testing.T) { func TestNewCommandWithEnvironmentConfigInstanceConnectionName(t *testing.T) { tcs := []struct { desc string - // sets an env var before the test, and returns a cleanup function - setEnv func() func() - args []string - want *proxy.Config + env map[string]string + args []string + want *proxy.Config }{ { desc: "with one instance connection name", - setEnv: func() func() { - key := "CLOUD_SQL_INSTANCE_CONNECTION_NAME" - os.Setenv(key, "proj:reg:inst") - return func() { os.Unsetenv(key) } + env: map[string]string{ + "CLOUD_SQL_INSTANCE_CONNECTION_NAME": "proj:reg:inst", }, want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ {Name: "proj:reg:inst"}, @@ -482,15 +449,9 @@ func TestNewCommandWithEnvironmentConfigInstanceConnectionName(t *testing.T) { }, { desc: "with multiple instance connection names", - setEnv: func() func() { - key0 := "CLOUD_SQL_INSTANCE_CONNECTION_NAME_0" - key1 := "CLOUD_SQL_INSTANCE_CONNECTION_NAME_1" - os.Setenv(key0, "proj:reg:inst0") - os.Setenv(key1, "proj:reg:inst1") - return func() { - os.Unsetenv(key0) - os.Unsetenv(key1) - } + env: map[string]string{ + "CLOUD_SQL_INSTANCE_CONNECTION_NAME_0": "proj:reg:inst0", + "CLOUD_SQL_INSTANCE_CONNECTION_NAME_1": "proj:reg:inst1", }, want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ {Name: "proj:reg:inst0"}, @@ -499,12 +460,9 @@ func TestNewCommandWithEnvironmentConfigInstanceConnectionName(t *testing.T) { }, { desc: "with query params", - setEnv: func() func() { - key := "CLOUD_SQL_INSTANCE_CONNECTION_NAME_0" - os.Setenv(key, "proj:reg:inst0?auto-iam-authn=true") - return func() { - os.Unsetenv(key) - } + + env: map[string]string{ + "CLOUD_SQL_INSTANCE_CONNECTION_NAME_0": "proj:reg:inst0?auto-iam-authn=true", }, want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ {Name: "proj:reg:inst0", IAMAuthN: pointer(true)}, @@ -512,15 +470,9 @@ func TestNewCommandWithEnvironmentConfigInstanceConnectionName(t *testing.T) { }, { desc: "when the index skips a number", - setEnv: func() func() { - key0 := "CLOUD_SQL_INSTANCE_CONNECTION_NAME_0" - key1 := "CLOUD_SQL_INSTANCE_CONNECTION_NAME_2" - os.Setenv(key0, "proj:reg:inst0") - os.Setenv(key1, "proj:reg:inst1") - return func() { - os.Unsetenv(key0) - os.Unsetenv(key1) - } + env: map[string]string{ + "CLOUD_SQL_INSTANCE_CONNECTION_NAME_0": "proj:reg:inst0", + "CLOUD_SQL_INSTANCE_CONNECTION_NAME_2": "proj:reg:inst1", }, want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ {Name: "proj:reg:inst0"}, @@ -528,10 +480,8 @@ func TestNewCommandWithEnvironmentConfigInstanceConnectionName(t *testing.T) { }, { desc: "when there are CLI args provided", - setEnv: func() func() { - key := "CLOUD_SQL_INSTANCE_CONNECTION_NAME" - os.Setenv(key, "proj:reg:inst0") - return func() { os.Unsetenv(key) } + env: map[string]string{ + "CLOUD_SQL_INSTANCE_CONNECTION_NAME": "proj:reg:inst0", }, args: []string{"myotherproj:myreg:myinst"}, want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ @@ -540,10 +490,8 @@ func TestNewCommandWithEnvironmentConfigInstanceConnectionName(t *testing.T) { }, { desc: "when only an index instance connection name is defined", - setEnv: func() func() { - key := "CLOUD_SQL_INSTANCE_CONNECTION_NAME_0" - os.Setenv(key, "proj:reg:inst0") - return func() { os.Unsetenv(key) } + env: map[string]string{ + "CLOUD_SQL_INSTANCE_CONNECTION_NAME_0": "proj:reg:inst0", }, want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ {Name: "proj:reg:inst0"}, @@ -552,8 +500,16 @@ func TestNewCommandWithEnvironmentConfigInstanceConnectionName(t *testing.T) { } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { - cleanup := tc.setEnv() - defer cleanup() + var cleanup []string + for k, v := range tc.env { + os.Setenv(k, v) + cleanup = append(cleanup, k) + } + defer func() { + for _, k := range cleanup { + os.Unsetenv(k) + } + }() c, err := invokeProxyCommand(tc.args) if err != nil { @@ -569,174 +525,127 @@ func TestNewCommandWithEnvironmentConfigInstanceConnectionName(t *testing.T) { func TestNewCommandWithEnvironmentConfig(t *testing.T) { tcs := []struct { - desc string - // sets an env var before the test, and returns a cleanup function - setEnv func() func() - want *proxy.Config + desc string + envName string + envValue string + want *proxy.Config }{ { - desc: "using the address envvar", - setEnv: func() func() { - key := "CLOUD_SQL_ADDRESS" - os.Setenv(key, "0.0.0.0") - return func() { os.Unsetenv(key) } - }, + desc: "using the address envvar", + envName: "CLOUD_SQL_ADDRESS", + envValue: "0.0.0.0", want: withDefaults(&proxy.Config{ Addr: "0.0.0.0", }), }, { - desc: "using the port envvar", - setEnv: func() func() { - key := "CLOUD_SQL_PORT" - os.Setenv(key, "6000") - return func() { os.Unsetenv(key) } - }, + desc: "using the port envvar", + envName: "CLOUD_SQL_PORT", + envValue: "6000", want: withDefaults(&proxy.Config{ Port: 6000, }), }, { - desc: "using the token envvar", - setEnv: func() func() { - key := "CLOUD_SQL_TOKEN" - os.Setenv(key, "MYCOOLTOKEN") - return func() { os.Unsetenv(key) } - }, + desc: "using the token envvar", + envName: "CLOUD_SQL_TOKEN", + envValue: "MYCOOLTOKEN", want: withDefaults(&proxy.Config{ Token: "MYCOOLTOKEN", }), }, { - desc: "using the credentiale file envvar", - setEnv: func() func() { - key := "CLOUD_SQL_CREDENTIALS_FILE" - os.Setenv(key, "/path/to/file") - return func() { os.Unsetenv(key) } - }, + desc: "using the credentiale file envvar", + envName: "CLOUD_SQL_CREDENTIALS_FILE", + envValue: "/path/to/file", want: withDefaults(&proxy.Config{ CredentialsFile: "/path/to/file", }), }, { - desc: "using the JSON credentials", - setEnv: func() func() { - key := "CLOUD_SQL_JSON_CREDENTIALS" - os.Setenv(key, `{"json":"goes-here"}`) - return func() { os.Unsetenv(key) } - }, + desc: "using the JSON credentials", + envName: "CLOUD_SQL_JSON_CREDENTIALS", + envValue: `{"json":"goes-here"}`, want: withDefaults(&proxy.Config{ CredentialsJSON: `{"json":"goes-here"}`, }), }, { - desc: "using the gcloud auth envvar", - setEnv: func() func() { - key := "CLOUD_SQL_GCLOUD_AUTH" - os.Setenv(key, "true") - return func() { os.Unsetenv(key) } - }, + desc: "using the gcloud auth envvar", + envName: "CLOUD_SQL_GCLOUD_AUTH", + envValue: "true", want: withDefaults(&proxy.Config{ GcloudAuth: true, }), }, { - desc: "using the api-endpoint envvar", - setEnv: func() func() { - key := "CLOUD_SQL_SQLADMIN_API_ENDPOINT" - os.Setenv(key, "https://test.googleapis.com/") - return func() { os.Unsetenv(key) } - }, + desc: "using the api-endpoint envvar", + envName: "CLOUD_SQL_SQLADMIN_API_ENDPOINT", + envValue: "https://test.googleapis.com/", want: withDefaults(&proxy.Config{ APIEndpointURL: "https://test.googleapis.com/", }), }, { - desc: "using the unix socket envvar", - setEnv: func() func() { - key := "CLOUD_SQL_UNIX_SOCKET" - os.Setenv(key, "/path/to/dir/") - return func() { os.Unsetenv(key) } - }, + desc: "using the unix socket envvar", + envName: "CLOUD_SQL_UNIX_SOCKET", + envValue: "/path/to/dir/", want: withDefaults(&proxy.Config{ UnixSocket: "/path/to/dir/", }), }, { - desc: "using the iam authn login envvar", - setEnv: func() func() { - key := "CLOUD_SQL_AUTO_IAM_AUTHN" - os.Setenv(key, "true") - return func() { os.Unsetenv(key) } - }, + desc: "using the iam authn login envvar", + envName: "CLOUD_SQL_AUTO_IAM_AUTHN", + envValue: "true", want: withDefaults(&proxy.Config{ IAMAuthN: true, }), }, { - desc: "enabling structured logging", - setEnv: func() func() { - key := "CLOUD_SQL_STRUCTURED_LOGS" - os.Setenv(key, "true") - return func() { os.Unsetenv(key) } - }, + desc: "enabling structured logging", + envName: "CLOUD_SQL_STRUCTURED_LOGS", + envValue: "true", want: withDefaults(&proxy.Config{ StructuredLogs: true, }), }, { - desc: "using the max connections envvar", - setEnv: func() func() { - key := "CLOUD_SQL_MAX_CONNECTIONS" - os.Setenv(key, "1") - return func() { os.Unsetenv(key) } - }, + desc: "using the max connections envvar", + envName: "CLOUD_SQL_MAX_CONNECTIONS", + envValue: "1", want: withDefaults(&proxy.Config{ MaxConnections: 1, }), }, { - desc: "using wait after signterm envvar", - setEnv: func() func() { - key := "CLOUD_SQL_MAX_SIGTERM_DELAY" - os.Setenv(key, "10s") - return func() { os.Unsetenv(key) } - }, + desc: "using wait after signterm envvar", + envName: "CLOUD_SQL_MAX_SIGTERM_DELAY", + envValue: "10s", want: withDefaults(&proxy.Config{ WaitOnClose: 10 * time.Second, }), }, { - desc: "using the private-ip envvar", - setEnv: func() func() { - key := "CLOUD_SQL_PRIVATE_IP" - os.Setenv(key, "true") - return func() { os.Unsetenv(key) } - }, + desc: "using the private-ip envvar", + envName: "CLOUD_SQL_PRIVATE_IP", + envValue: "true", want: withDefaults(&proxy.Config{ PrivateIP: true, }), }, { - desc: "using the quota project envvar", - setEnv: func() func() { - key := "CLOUD_SQL_QUOTA_PROJECT" - os.Setenv(key, "proj") - return func() { os.Unsetenv(key) } - }, + desc: "using the quota project envvar", + envName: "CLOUD_SQL_QUOTA_PROJECT", + envValue: "proj", want: withDefaults(&proxy.Config{ QuotaProject: "proj", }), }, { - desc: "using the imopersonate service accounn envvar", - setEnv: func() func() { - key := "CLOUD_SQL_IMPERSONATE_SERVICE_ACCOUNT" - os.Setenv(key, - "sv1@developer.gserviceaccount.com,sv2@developer.gserviceaccount.com,sv3@developer.gserviceaccount.com", - ) - return func() { os.Unsetenv(key) } - }, + desc: "using the imopersonate service accounn envvar", + envName: "CLOUD_SQL_IMPERSONATE_SERVICE_ACCOUNT", + envValue: "sv1@developer.gserviceaccount.com,sv2@developer.gserviceaccount.com,sv3@developer.gserviceaccount.com", want: withDefaults(&proxy.Config{ ImpersonateTarget: "sv1@developer.gserviceaccount.com", ImpersonateDelegates: []string{ @@ -748,8 +657,8 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { - cleanup := tc.setEnv() - defer cleanup() + os.Setenv(tc.envName, tc.envValue) + defer os.Unsetenv(tc.envName) c, err := invokeProxyCommand([]string{"proj:region:inst"}) if err != nil { From 7054b95674f1ce8507ed83cb6fcb218381156414 Mon Sep 17 00:00:00 2001 From: Eno Compton Date: Mon, 31 Oct 2022 14:38:16 -0600 Subject: [PATCH 8/9] PR comments and better prefix --- cmd/root.go | 23 ++++++++------- cmd/root_test.go | 76 ++++++++++++++++++++++++++---------------------- 2 files changed, 54 insertions(+), 45 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 1cb9f7343..a4e50ef18 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -239,14 +239,13 @@ Configuration using environment variables ` -const envPrefix = "CLOUD_SQL" +const envPrefix = "CSQL_PROXY" -func instanceConnectionNames(args []string) []string { - // If args is already populated from a CLI invocation, ignore any instance - // connection name env vars and return the CLI args. - if len(args) > 0 { - return args - } +func instanceFromEnv(args []string) []string { + // This supports naming the first instance first with: + // INSTANCE_CONNECTION_NAME + // or if that's not defined, with: + // INSTANCE_CONNECTION_NAME_0 inst := os.Getenv(fmt.Sprintf("%s_INSTANCE_CONNECTION_NAME", envPrefix)) if inst == "" { inst = os.Getenv(fmt.Sprintf("%s_INSTANCE_CONNECTION_NAME_0", envPrefix)) @@ -293,7 +292,10 @@ func NewCommand(opts ...Option) *Command { } cmd.Args = func(cmd *cobra.Command, args []string) error { - args = instanceConnectionNames(args) + // If args is not already populated, try to read from the environment. + if len(args) == 0 { + args = instanceFromEnv(args) + } // Handle logger separately from config if c.conf.StructuredLogs { c.logger, c.cleanup = log.NewStructuredLogger() @@ -392,9 +394,8 @@ is the target account.`) _ = v.BindPFlags(pflags) pflags.VisitAll(func(f *pflag.Flag) { - // When the flag has not been set, but there is a Viper value, set the - // flag to the Viper value. This removes the need to manually assign - // Viper values into the proxy.Config for each value. + // Override any unset flags with Viper values to use the pflags + // object as a single source of truth. if !f.Changed && v.IsSet(f.Name) { val := v.Get(f.Name) pflags.Set(f.Name, fmt.Sprintf("%v", val)) diff --git a/cmd/root_test.go b/cmd/root_test.go index 06bab821a..a3b8249c9 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -52,7 +52,15 @@ func withDefaults(c *proxy.Config) *proxy.Config { return c } -// pointer returns a pointer to v +// pointer returns the address of v and makes it easy to take the address of a +// predeclared identifier. Compare: +// +// t := true +// pt := &t +// +// vs +// +// pt := pointer(true) func pointer[T any](v T) *T { return &v } @@ -335,7 +343,7 @@ func TestNewCommandWithEnvironmentConfigPrivateFields(t *testing.T) { }{ { desc: "using the disable traces envvar", - envName: "CLOUD_SQL_DISABLE_TRACES", + envName: "CSQL_PROXY_DISABLE_TRACES", envValue: "true", isValid: func(cmd *Command) bool { return cmd.disableTraces == true @@ -343,7 +351,7 @@ func TestNewCommandWithEnvironmentConfigPrivateFields(t *testing.T) { }, { desc: "using the telemetry sample rate envvar", - envName: "CLOUD_SQL_TELEMETRY_SAMPLE_RATE", + envName: "CSQL_PROXY_TELEMETRY_SAMPLE_RATE", envValue: "500", isValid: func(cmd *Command) bool { return cmd.telemetryTracingSampleRate == 500 @@ -351,7 +359,7 @@ func TestNewCommandWithEnvironmentConfigPrivateFields(t *testing.T) { }, { desc: "using the disable metrics envvar", - envName: "CLOUD_SQL_DISABLE_METRICS", + envName: "CSQL_PROXY_DISABLE_METRICS", envValue: "true", isValid: func(cmd *Command) bool { return cmd.disableMetrics == true @@ -359,7 +367,7 @@ func TestNewCommandWithEnvironmentConfigPrivateFields(t *testing.T) { }, { desc: "using the telemetry project envvar", - envName: "CLOUD_SQL_TELEMETRY_PROJECT", + envName: "CSQL_PROXY_TELEMETRY_PROJECT", envValue: "mycoolproject", isValid: func(cmd *Command) bool { return cmd.telemetryProject == "mycoolproject" @@ -367,7 +375,7 @@ func TestNewCommandWithEnvironmentConfigPrivateFields(t *testing.T) { }, { desc: "using the telemetry prefix envvar", - envName: "CLOUD_SQL_TELEMETRY_PREFIX", + envName: "CSQL_PROXY_TELEMETRY_PREFIX", envValue: "myprefix", isValid: func(cmd *Command) bool { return cmd.telemetryPrefix == "myprefix" @@ -375,7 +383,7 @@ func TestNewCommandWithEnvironmentConfigPrivateFields(t *testing.T) { }, { desc: "using the prometheus envvar", - envName: "CLOUD_SQL_PROMETHEUS", + envName: "CSQL_PROXY_PROMETHEUS", envValue: "true", isValid: func(cmd *Command) bool { return cmd.prometheus == true @@ -383,7 +391,7 @@ func TestNewCommandWithEnvironmentConfigPrivateFields(t *testing.T) { }, { desc: "using the prometheus namespace envvar", - envName: "CLOUD_SQL_PROMETHEUS_NAMESPACE", + envName: "CSQL_PROXY_PROMETHEUS_NAMESPACE", envValue: "myns", isValid: func(cmd *Command) bool { return cmd.prometheusNamespace == "myns" @@ -391,7 +399,7 @@ func TestNewCommandWithEnvironmentConfigPrivateFields(t *testing.T) { }, { desc: "using the health check envvar", - envName: "CLOUD_SQL_HEALTH_CHECK", + envName: "CSQL_PROXY_HEALTH_CHECK", envValue: "true", isValid: func(cmd *Command) bool { return cmd.healthCheck == true @@ -399,7 +407,7 @@ func TestNewCommandWithEnvironmentConfigPrivateFields(t *testing.T) { }, { desc: "using the http address envvar", - envName: "CLOUD_SQL_HTTP_ADDRESS", + envName: "CSQL_PROXY_HTTP_ADDRESS", envValue: "0.0.0.0", isValid: func(cmd *Command) bool { return cmd.httpAddress == "0.0.0.0" @@ -407,7 +415,7 @@ func TestNewCommandWithEnvironmentConfigPrivateFields(t *testing.T) { }, { desc: "using the http port envvar", - envName: "CLOUD_SQL_HTTP_PORT", + envName: "CSQL_PROXY_HTTP_PORT", envValue: "5555", isValid: func(cmd *Command) bool { return cmd.httpPort == "5555" @@ -441,7 +449,7 @@ func TestNewCommandWithEnvironmentConfigInstanceConnectionName(t *testing.T) { { desc: "with one instance connection name", env: map[string]string{ - "CLOUD_SQL_INSTANCE_CONNECTION_NAME": "proj:reg:inst", + "CSQL_PROXY_INSTANCE_CONNECTION_NAME": "proj:reg:inst", }, want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ {Name: "proj:reg:inst"}, @@ -450,8 +458,8 @@ func TestNewCommandWithEnvironmentConfigInstanceConnectionName(t *testing.T) { { desc: "with multiple instance connection names", env: map[string]string{ - "CLOUD_SQL_INSTANCE_CONNECTION_NAME_0": "proj:reg:inst0", - "CLOUD_SQL_INSTANCE_CONNECTION_NAME_1": "proj:reg:inst1", + "CSQL_PROXY_INSTANCE_CONNECTION_NAME_0": "proj:reg:inst0", + "CSQL_PROXY_INSTANCE_CONNECTION_NAME_1": "proj:reg:inst1", }, want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ {Name: "proj:reg:inst0"}, @@ -462,7 +470,7 @@ func TestNewCommandWithEnvironmentConfigInstanceConnectionName(t *testing.T) { desc: "with query params", env: map[string]string{ - "CLOUD_SQL_INSTANCE_CONNECTION_NAME_0": "proj:reg:inst0?auto-iam-authn=true", + "CSQL_PROXY_INSTANCE_CONNECTION_NAME_0": "proj:reg:inst0?auto-iam-authn=true", }, want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ {Name: "proj:reg:inst0", IAMAuthN: pointer(true)}, @@ -471,8 +479,8 @@ func TestNewCommandWithEnvironmentConfigInstanceConnectionName(t *testing.T) { { desc: "when the index skips a number", env: map[string]string{ - "CLOUD_SQL_INSTANCE_CONNECTION_NAME_0": "proj:reg:inst0", - "CLOUD_SQL_INSTANCE_CONNECTION_NAME_2": "proj:reg:inst1", + "CSQL_PROXY_INSTANCE_CONNECTION_NAME_0": "proj:reg:inst0", + "CSQL_PROXY_INSTANCE_CONNECTION_NAME_2": "proj:reg:inst1", }, want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ {Name: "proj:reg:inst0"}, @@ -481,7 +489,7 @@ func TestNewCommandWithEnvironmentConfigInstanceConnectionName(t *testing.T) { { desc: "when there are CLI args provided", env: map[string]string{ - "CLOUD_SQL_INSTANCE_CONNECTION_NAME": "proj:reg:inst0", + "CSQL_PROXY_INSTANCE_CONNECTION_NAME": "proj:reg:inst0", }, args: []string{"myotherproj:myreg:myinst"}, want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ @@ -491,7 +499,7 @@ func TestNewCommandWithEnvironmentConfigInstanceConnectionName(t *testing.T) { { desc: "when only an index instance connection name is defined", env: map[string]string{ - "CLOUD_SQL_INSTANCE_CONNECTION_NAME_0": "proj:reg:inst0", + "CSQL_PROXY_INSTANCE_CONNECTION_NAME_0": "proj:reg:inst0", }, want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ {Name: "proj:reg:inst0"}, @@ -532,7 +540,7 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { }{ { desc: "using the address envvar", - envName: "CLOUD_SQL_ADDRESS", + envName: "CSQL_PROXY_ADDRESS", envValue: "0.0.0.0", want: withDefaults(&proxy.Config{ Addr: "0.0.0.0", @@ -540,7 +548,7 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { }, { desc: "using the port envvar", - envName: "CLOUD_SQL_PORT", + envName: "CSQL_PROXY_PORT", envValue: "6000", want: withDefaults(&proxy.Config{ Port: 6000, @@ -548,7 +556,7 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { }, { desc: "using the token envvar", - envName: "CLOUD_SQL_TOKEN", + envName: "CSQL_PROXY_TOKEN", envValue: "MYCOOLTOKEN", want: withDefaults(&proxy.Config{ Token: "MYCOOLTOKEN", @@ -556,7 +564,7 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { }, { desc: "using the credentiale file envvar", - envName: "CLOUD_SQL_CREDENTIALS_FILE", + envName: "CSQL_PROXY_CREDENTIALS_FILE", envValue: "/path/to/file", want: withDefaults(&proxy.Config{ CredentialsFile: "/path/to/file", @@ -564,7 +572,7 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { }, { desc: "using the JSON credentials", - envName: "CLOUD_SQL_JSON_CREDENTIALS", + envName: "CSQL_PROXY_JSON_CREDENTIALS", envValue: `{"json":"goes-here"}`, want: withDefaults(&proxy.Config{ CredentialsJSON: `{"json":"goes-here"}`, @@ -572,7 +580,7 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { }, { desc: "using the gcloud auth envvar", - envName: "CLOUD_SQL_GCLOUD_AUTH", + envName: "CSQL_PROXY_GCLOUD_AUTH", envValue: "true", want: withDefaults(&proxy.Config{ GcloudAuth: true, @@ -580,7 +588,7 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { }, { desc: "using the api-endpoint envvar", - envName: "CLOUD_SQL_SQLADMIN_API_ENDPOINT", + envName: "CSQL_PROXY_SQLADMIN_API_ENDPOINT", envValue: "https://test.googleapis.com/", want: withDefaults(&proxy.Config{ APIEndpointURL: "https://test.googleapis.com/", @@ -588,7 +596,7 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { }, { desc: "using the unix socket envvar", - envName: "CLOUD_SQL_UNIX_SOCKET", + envName: "CSQL_PROXY_UNIX_SOCKET", envValue: "/path/to/dir/", want: withDefaults(&proxy.Config{ UnixSocket: "/path/to/dir/", @@ -596,7 +604,7 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { }, { desc: "using the iam authn login envvar", - envName: "CLOUD_SQL_AUTO_IAM_AUTHN", + envName: "CSQL_PROXY_AUTO_IAM_AUTHN", envValue: "true", want: withDefaults(&proxy.Config{ IAMAuthN: true, @@ -604,7 +612,7 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { }, { desc: "enabling structured logging", - envName: "CLOUD_SQL_STRUCTURED_LOGS", + envName: "CSQL_PROXY_STRUCTURED_LOGS", envValue: "true", want: withDefaults(&proxy.Config{ StructuredLogs: true, @@ -612,7 +620,7 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { }, { desc: "using the max connections envvar", - envName: "CLOUD_SQL_MAX_CONNECTIONS", + envName: "CSQL_PROXY_MAX_CONNECTIONS", envValue: "1", want: withDefaults(&proxy.Config{ MaxConnections: 1, @@ -620,7 +628,7 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { }, { desc: "using wait after signterm envvar", - envName: "CLOUD_SQL_MAX_SIGTERM_DELAY", + envName: "CSQL_PROXY_MAX_SIGTERM_DELAY", envValue: "10s", want: withDefaults(&proxy.Config{ WaitOnClose: 10 * time.Second, @@ -628,7 +636,7 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { }, { desc: "using the private-ip envvar", - envName: "CLOUD_SQL_PRIVATE_IP", + envName: "CSQL_PROXY_PRIVATE_IP", envValue: "true", want: withDefaults(&proxy.Config{ PrivateIP: true, @@ -636,7 +644,7 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { }, { desc: "using the quota project envvar", - envName: "CLOUD_SQL_QUOTA_PROJECT", + envName: "CSQL_PROXY_QUOTA_PROJECT", envValue: "proj", want: withDefaults(&proxy.Config{ QuotaProject: "proj", @@ -644,7 +652,7 @@ func TestNewCommandWithEnvironmentConfig(t *testing.T) { }, { desc: "using the imopersonate service accounn envvar", - envName: "CLOUD_SQL_IMPERSONATE_SERVICE_ACCOUNT", + envName: "CSQL_PROXY_IMPERSONATE_SERVICE_ACCOUNT", envValue: "sv1@developer.gserviceaccount.com,sv2@developer.gserviceaccount.com,sv3@developer.gserviceaccount.com", want: withDefaults(&proxy.Config{ ImpersonateTarget: "sv1@developer.gserviceaccount.com", From 2eba4617e3c5110bf059e9d0d856c5068c394b9a Mon Sep 17 00:00:00 2001 From: Eno Compton Date: Mon, 31 Oct 2022 14:40:54 -0600 Subject: [PATCH 9/9] Update help string --- cmd/root.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index a4e50ef18..09e933f0c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -212,27 +212,27 @@ Service Account Impersonation Configuration using environment variables Instead of using CLI flags, the proxy may be configured using environment - variables. Each environment variable uses "CLOUD_SQL" as a prefix and is the - uppercase version of the flag using underscores as word delimiters. For + variables. Each environment variable uses "CSQL_PROXY" as a prefix and is + the uppercase version of the flag using underscores as word delimiters. For example, the --auto-iam-authn flag may be set with the environment variable - CLOUD_SQL_AUTO_IAM_AUTHN. An invocation of the proxy using environment + CSQL_PROXY_AUTO_IAM_AUTHN. An invocation of the proxy using environment variables would look like the following: - CLOUD_SQL_AUTO_IAM_AUTHN=true \ + CSQL_PROXY_AUTO_IAM_AUTHN=true \ ./cloud-sql-proxy my-project:us-central1:my-db-server In addition to CLI flags, instance connection names may also be specified with environment variables. If invoking the proxy with only one instance - connection name, use CLOUD_SQL_INSTANCE_CONNECTION_NAME. For example: + connection name, use CSQL_PROXY_INSTANCE_CONNECTION_NAME. For example: - CLOUD_SQL_INSTANCE_CONNECTION_NAME=my-project:us-central1:my-db-server \ + CSQL_PROXY_INSTANCE_CONNECTION_NAME=my-project:us-central1:my-db-server \ ./cloud-sql-proxy If multiple instance connection names are used, add the index of the instance connection name as a suffix. For example: - CLOUD_SQL_INSTANCE_CONNECTION_NAME_0=my-project:us-central1:my-db-server \ - CLOUD_SQL_INSTANCE_CONNECTION_NAME_1=my-other-project:us-central1:my-other-server \ + CSQL_PROXY_INSTANCE_CONNECTION_NAME_0=my-project:us-central1:my-db-server \ + CSQL_PROXY_INSTANCE_CONNECTION_NAME_1=my-other-project:us-central1:my-other-server \ ./cloud-sql-proxy (*) indicates a flag that may be used as a query parameter