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..09e933f0c 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" ) @@ -207,10 +209,66 @@ 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 "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 + CSQL_PROXY_AUTO_IAM_AUTHN. An invocation of the proxy using environment + variables would look like the following: + + 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 CSQL_PROXY_INSTANCE_CONNECTION_NAME. For example: + + 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: + + 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 ` +const envPrefix = "CSQL_PROXY" + +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)) + 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 +292,10 @@ func NewCommand(opts ...Option) *Command { } cmd.Args = func(cmd *cobra.Command, args []string) error { + // 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() @@ -254,74 +316,92 @@ 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() + // 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) { + // 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)) + } + }) + return c } diff --git a/cmd/root_test.go b/cmd/root_test.go index 66879144a..a3b8249c9 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -31,31 +31,57 @@ 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 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 +} + +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 args []string @@ -231,7 +257,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 +294,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 +306,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"}, @@ -296,17 +322,204 @@ 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, err := invokeProxyCommand(tc.args) + 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)) } - c.SetArgs(tc.args) + }) + } +} - err := c.Execute() +func TestNewCommandWithEnvironmentConfigPrivateFields(t *testing.T) { + tcs := []struct { + desc string + envName string + envValue string + isValid func(cmd *Command) bool + }{ + { + desc: "using the disable traces envvar", + envName: "CSQL_PROXY_DISABLE_TRACES", + envValue: "true", + isValid: func(cmd *Command) bool { + return cmd.disableTraces == true + }, + }, + { + desc: "using the telemetry sample rate envvar", + envName: "CSQL_PROXY_TELEMETRY_SAMPLE_RATE", + envValue: "500", + isValid: func(cmd *Command) bool { + return cmd.telemetryTracingSampleRate == 500 + }, + }, + { + desc: "using the disable metrics envvar", + envName: "CSQL_PROXY_DISABLE_METRICS", + envValue: "true", + isValid: func(cmd *Command) bool { + return cmd.disableMetrics == true + }, + }, + { + desc: "using the telemetry project envvar", + envName: "CSQL_PROXY_TELEMETRY_PROJECT", + envValue: "mycoolproject", + isValid: func(cmd *Command) bool { + return cmd.telemetryProject == "mycoolproject" + }, + }, + { + desc: "using the telemetry prefix envvar", + envName: "CSQL_PROXY_TELEMETRY_PREFIX", + envValue: "myprefix", + isValid: func(cmd *Command) bool { + return cmd.telemetryPrefix == "myprefix" + }, + }, + { + desc: "using the prometheus envvar", + envName: "CSQL_PROXY_PROMETHEUS", + envValue: "true", + isValid: func(cmd *Command) bool { + return cmd.prometheus == true + }, + }, + { + desc: "using the prometheus namespace envvar", + envName: "CSQL_PROXY_PROMETHEUS_NAMESPACE", + envValue: "myns", + isValid: func(cmd *Command) bool { + return cmd.prometheusNamespace == "myns" + }, + }, + { + desc: "using the health check envvar", + envName: "CSQL_PROXY_HEALTH_CHECK", + envValue: "true", + isValid: func(cmd *Command) bool { + return cmd.healthCheck == true + }, + }, + { + desc: "using the http address envvar", + envName: "CSQL_PROXY_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", + envName: "CSQL_PROXY_HTTP_PORT", + envValue: "5555", + isValid: func(cmd *Command) bool { + return cmd.httpPort == "5555" + }, + }, + } + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + os.Setenv(tc.envName, tc.envValue) + defer os.Unsetenv(tc.envName) + + c, err := invokeProxyCommand([]string{"proj:region:inst"}) + 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 + env map[string]string + args []string + want *proxy.Config + }{ + { + desc: "with one instance connection name", + env: map[string]string{ + "CSQL_PROXY_INSTANCE_CONNECTION_NAME": "proj:reg:inst", + }, + want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ + {Name: "proj:reg:inst"}, + }}), + }, + { + desc: "with multiple instance connection names", + env: map[string]string{ + "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"}, + {Name: "proj:reg:inst1"}, + }}), + }, + { + desc: "with query params", + + env: map[string]string{ + "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)}, + }}), + }, + { + desc: "when the index skips a number", + env: map[string]string{ + "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"}, + }}), + }, + { + desc: "when there are CLI args provided", + env: map[string]string{ + "CSQL_PROXY_INSTANCE_CONNECTION_NAME": "proj:reg:inst0", + }, + 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", + env: map[string]string{ + "CSQL_PROXY_INSTANCE_CONNECTION_NAME_0": "proj:reg:inst0", + }, + want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ + {Name: "proj:reg:inst0"}, + }}), + }, + } + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + 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 { t.Fatalf("want error = nil, got = %v", err) } @@ -318,11 +531,156 @@ 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 TestNewCommandWithEnvironmentConfig(t *testing.T) { + tcs := []struct { + desc string + envName string + envValue string + want *proxy.Config + }{ + { + desc: "using the address envvar", + envName: "CSQL_PROXY_ADDRESS", + envValue: "0.0.0.0", + want: withDefaults(&proxy.Config{ + Addr: "0.0.0.0", + }), + }, + { + desc: "using the port envvar", + envName: "CSQL_PROXY_PORT", + envValue: "6000", + want: withDefaults(&proxy.Config{ + Port: 6000, + }), + }, + { + desc: "using the token envvar", + envName: "CSQL_PROXY_TOKEN", + envValue: "MYCOOLTOKEN", + want: withDefaults(&proxy.Config{ + Token: "MYCOOLTOKEN", + }), + }, + { + desc: "using the credentiale file envvar", + envName: "CSQL_PROXY_CREDENTIALS_FILE", + envValue: "/path/to/file", + want: withDefaults(&proxy.Config{ + CredentialsFile: "/path/to/file", + }), + }, + { + desc: "using the JSON credentials", + envName: "CSQL_PROXY_JSON_CREDENTIALS", + envValue: `{"json":"goes-here"}`, + want: withDefaults(&proxy.Config{ + CredentialsJSON: `{"json":"goes-here"}`, + }), + }, + { + desc: "using the gcloud auth envvar", + envName: "CSQL_PROXY_GCLOUD_AUTH", + envValue: "true", + want: withDefaults(&proxy.Config{ + GcloudAuth: true, + }), + }, + { + desc: "using the api-endpoint envvar", + envName: "CSQL_PROXY_SQLADMIN_API_ENDPOINT", + envValue: "https://test.googleapis.com/", + want: withDefaults(&proxy.Config{ + APIEndpointURL: "https://test.googleapis.com/", + }), + }, + { + desc: "using the unix socket envvar", + envName: "CSQL_PROXY_UNIX_SOCKET", + envValue: "/path/to/dir/", + want: withDefaults(&proxy.Config{ + UnixSocket: "/path/to/dir/", + }), + }, + { + desc: "using the iam authn login envvar", + envName: "CSQL_PROXY_AUTO_IAM_AUTHN", + envValue: "true", + want: withDefaults(&proxy.Config{ + IAMAuthN: true, + }), + }, + { + desc: "enabling structured logging", + envName: "CSQL_PROXY_STRUCTURED_LOGS", + envValue: "true", + want: withDefaults(&proxy.Config{ + StructuredLogs: true, + }), + }, + { + desc: "using the max connections envvar", + envName: "CSQL_PROXY_MAX_CONNECTIONS", + envValue: "1", + want: withDefaults(&proxy.Config{ + MaxConnections: 1, + }), + }, + { + desc: "using wait after signterm envvar", + envName: "CSQL_PROXY_MAX_SIGTERM_DELAY", + envValue: "10s", + want: withDefaults(&proxy.Config{ + WaitOnClose: 10 * time.Second, + }), + }, + { + desc: "using the private-ip envvar", + envName: "CSQL_PROXY_PRIVATE_IP", + envValue: "true", + want: withDefaults(&proxy.Config{ + PrivateIP: true, + }), + }, + { + desc: "using the quota project envvar", + envName: "CSQL_PROXY_QUOTA_PROJECT", + envValue: "proj", + want: withDefaults(&proxy.Config{ + QuotaProject: "proj", + }), + }, + { + desc: "using the imopersonate service accounn envvar", + 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", + ImpersonateDelegates: []string{ + "sv3@developer.gserviceaccount.com", + "sv2@developer.gserviceaccount.com", + }, + }), + }, + } + for _, tc := range tcs { + t.Run(tc.desc, func(t *testing.T) { + os.Setenv(tc.envName, tc.envValue) + defer os.Unsetenv(tc.envName) + + c, err := invokeProxyCommand([]string{"proj:region:inst"}) + 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,35 +694,27 @@ 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 { 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) } @@ -379,10 +729,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,60 +742,52 @@ 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 { 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) } @@ -580,17 +918,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") } diff --git a/go.mod b/go.mod index 94faaeaa0..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 @@ -28,6 +30,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 +40,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 +51,21 @@ 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/pflag v1.0.5 // 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/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..472a3f090 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= @@ -614,9 +617,12 @@ 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= +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 +819,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 +851,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 +904,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= @@ -1010,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= @@ -1033,6 +1042,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 +1098,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 +1190,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 +1203,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= @@ -1253,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= @@ -1295,8 +1314,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 +1327,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 +1337,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 +1360,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 +1511,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 +1770,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 +1922,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 +2047,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 +2194,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=