diff --git a/cmd/root.go b/cmd/root.go index 671883ccc0..01a8e1dc55 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -38,6 +38,8 @@ import ( "github.com/GoogleCloudPlatform/alloydb-auth-proxy/internal/log" "github.com/GoogleCloudPlatform/alloydb-auth-proxy/internal/proxy" "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/spf13/viper" "go.opencensus.io/trace" ) @@ -243,8 +245,66 @@ Service Account Impersonation In this example, the environment's IAM principal impersonates 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 "ALLOYDB_PROXY" as a prefix and + is the uppercase version of the flag using underscores as word delimiters. + For example, the --structured-logs flag may be set with the environment + variable ALLOYDB_PROXY_STRUCTURED_LOGS. An invocation of the proxy using + environment variables would look like the following: + + ALLOYDB_PROXY_STRUCTURED_LOGS=true \ + ./alloydb-auth-proxy \ + projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE + + In addition to CLI flags, instance URIs may also be specified with + environment variables. If invoking the proxy with only one instance URI, + use ALLOYDB_PROXY_INSTANCE_URI. For example: + + ALLOYDB_PROXY_INSTANCE_URI=projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE \ + ./alloydb-auth-proxy + + If multiple instance URIs are used, add the index of the instance URI as a + suffix. For example: + + ALLOYDB_PROXY_INSTANCE_URI_0=projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE1 \ + ALLOYDB_PROXY_INSTANCE_URI_1=projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE2 \ + ./alloydb-auth-proxy + ` +const envPrefix = "ALLOYDB_PROXY" + +func instanceFromEnv(args []string) []string { + // This supports naming the first instance first with: + // INSTANCE_URI + // or if that's not defined, with: + // INSTANCE_URI_0 + inst := os.Getenv(fmt.Sprintf("%s_INSTANCE_URI", envPrefix)) + if inst == "" { + inst = os.Getenv(fmt.Sprintf("%s_INSTANCE_URI_0", envPrefix)) + if inst == "" { + return nil + } + } + args = append(args, inst) + + i := 1 + for { + instN := os.Getenv(fmt.Sprintf("%s_INSTANCE_URI_%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{ @@ -268,6 +328,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() @@ -288,69 +352,87 @@ func NewCommand(opts ...Option) *Command { cmd.RunE = func(*cobra.Command, []string) error { return runSignalWrapper(c) } + pflags := cmd.PersistentFlags() + // Global-only flags - cmd.PersistentFlags().StringVarP(&c.conf.Token, "token", "t", "", + pflags.StringVarP(&c.conf.Token, "token", "t", "", "Bearer token used for authorization.") - cmd.PersistentFlags().StringVarP(&c.conf.CredentialsFile, "credentials-file", "c", "", + pflags.StringVarP(&c.conf.CredentialsFile, "credentials-file", "c", "", "Path to a service account key to use for authentication.") - 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 configuration to retrieve a token for authentication.") - cmd.PersistentFlags().BoolVarP(&c.conf.StructuredLogs, "structured-logs", "l", false, + pflags.BoolVarP(&c.conf.StructuredLogs, "structured-logs", "l", false, "Enable structured logs using the LogEntry format") - cmd.PersistentFlags().Uint64Var(&c.conf.MaxConnections, "max-connections", 0, + pflags.Uint64Var(&c.conf.MaxConnections, "max-connections", 0, `Limits the number of connections by refusing any additional connections. When this flag is not set, there is no limit.`) - cmd.PersistentFlags().DurationVar(&c.conf.WaitOnClose, "max-sigterm-delay", 0, + pflags.DurationVar(&c.conf.WaitOnClose, "max-sigterm-delay", 0, `Maximum amount of time to wait after for any open connections to close after receiving a TERM signal. The proxy will shut down when the number of open connections reaches 0 or when the maximum time has passed. Defaults to 0s.`) - cmd.PersistentFlags().StringVar(&c.conf.APIEndpointURL, "alloydbadmin-api-endpoint", + pflags.StringVar(&c.conf.APIEndpointURL, "alloydbadmin-api-endpoint", "https://alloydb.googleapis.com/v1beta", "When set, the proxy uses this host as the base API path.") - cmd.PersistentFlags().StringVar(&c.conf.FUSEDir, "fuse", "", + pflags.StringVar(&c.conf.FUSEDir, "fuse", "", "Mount a directory at the path using FUSE to access AlloyDB 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") - cmd.PersistentFlags().StringVar(&c.telemetryProject, "telemetry-project", "", + pflags.StringVar(&c.telemetryProject, "telemetry-project", "", "Enable Cloud Monitoring and Cloud Trace integration 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, "Configure the denominator of the probabilistic sample rate of traces sent to Cloud Trace\n(e.g., 10,000 traces 1/10,000 calls).") - 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 to use for Cloud Monitoring metrics.") - cmd.PersistentFlags().BoolVar(&c.prometheus, "prometheus", false, + pflags.BoolVar(&c.prometheus, "prometheus", false, "Enable Prometheus HTTP endpoint /metrics") - 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 the Prometheus server to use") - cmd.PersistentFlags().BoolVar(&c.healthCheck, "health-check", false, + pflags.BoolVar(&c.healthCheck, "health-check", false, `Enables HTTP endpoints /startup, /liveness, and /readiness that report on the proxy's health. Endpoints are available on localhost only. Uses the port specified by the http-port flag.`) // 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 on which to bind AlloyDB instance listeners.") - cmd.PersistentFlags().IntVarP(&c.conf.Port, "port", "p", 5432, + pflags.IntVarP(&c.conf.Port, "port", "p", 5432, "Initial port to use 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 using the provided directory.`) + 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 865305d9f2..905028d5fe 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -32,33 +32,52 @@ import ( "github.com/spf13/cobra" ) -func TestNewCommandArguments(t *testing.T) { - withDefaults := func(c *proxy.Config) *proxy.Config { - if c.UserAgent == "" { - c.UserAgent = userAgent - } - if c.Addr == "" { - c.Addr = "127.0.0.1" - } - if c.Port == 0 { - c.Port = 5432 - } - if c.FUSEDir == "" { - if c.Instances == nil { - c.Instances = []proxy.InstanceConnConfig{{}} - } - if i := &c.Instances[0]; i.Name == "" { - i.Name = "projects/proj/locations/region/clusters/clust/instances/inst" - } - } - if c.FUSETempDir == "" { - c.FUSETempDir = filepath.Join(os.TempDir(), "csql-tmp") +const sampleURI = "projects/proj/locations/region/clusters/clust/instances/inst" + +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 withDefaults(c *proxy.Config) *proxy.Config { + if c.UserAgent == "" { + c.UserAgent = userAgent + } + if c.Addr == "" { + c.Addr = "127.0.0.1" + } + if c.Port == 0 { + c.Port = 5432 + } + if c.FUSEDir == "" { + if c.Instances == nil { + c.Instances = []proxy.InstanceConnConfig{{}} } - if c.APIEndpointURL == "" { - c.APIEndpointURL = "https://alloydb.googleapis.com/v1beta" + if i := &c.Instances[0]; i.Name == "" { + i.Name = sampleURI } - return c } + if c.FUSETempDir == "" { + c.FUSETempDir = filepath.Join(os.TempDir(), "csql-tmp") + } + if c.APIEndpointURL == "" { + c.APIEndpointURL = "https://alloydb.googleapis.com/v1beta" + } + return c +} + +func TestNewCommandArguments(t *testing.T) { tcs := []struct { desc string args []string @@ -222,7 +241,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", "projects/proj/locations/region/clusters/clust/instances/inst"}, @@ -238,17 +257,322 @@ 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) } - c.SetArgs(tc.args) - err := c.Execute() + 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 TestNewCommandWithEnvironmentConfigPrivateFields(t *testing.T) { + tcs := []struct { + desc string + envName string + envValue string + isValid func(cmd *Command) bool + }{ + { + desc: "using the disable traces envvar", + envName: "ALLOYDB_PROXY_DISABLE_TRACES", + envValue: "true", + isValid: func(cmd *Command) bool { + return cmd.disableTraces == true + }, + }, + { + desc: "using the telemetry sample rate envvar", + envName: "ALLOYDB_PROXY_TELEMETRY_SAMPLE_RATE", + envValue: "500", + isValid: func(cmd *Command) bool { + return cmd.telemetryTracingSampleRate == 500 + }, + }, + { + desc: "using the disable metrics envvar", + envName: "ALLOYDB_PROXY_DISABLE_METRICS", + envValue: "true", + isValid: func(cmd *Command) bool { + return cmd.disableMetrics == true + }, + }, + { + desc: "using the telemetry project envvar", + envName: "ALLOYDB_PROXY_TELEMETRY_PROJECT", + envValue: "mycoolproject", + isValid: func(cmd *Command) bool { + return cmd.telemetryProject == "mycoolproject" + }, + }, + { + desc: "using the telemetry prefix envvar", + envName: "ALLOYDB_PROXY_TELEMETRY_PREFIX", + envValue: "myprefix", + isValid: func(cmd *Command) bool { + return cmd.telemetryPrefix == "myprefix" + }, + }, + { + desc: "using the prometheus envvar", + envName: "ALLOYDB_PROXY_PROMETHEUS", + envValue: "true", + isValid: func(cmd *Command) bool { + return cmd.prometheus == true + }, + }, + { + desc: "using the prometheus namespace envvar", + envName: "ALLOYDB_PROXY_PROMETHEUS_NAMESPACE", + envValue: "myns", + isValid: func(cmd *Command) bool { + return cmd.prometheusNamespace == "myns" + }, + }, + { + desc: "using the health check envvar", + envName: "ALLOYDB_PROXY_HEALTH_CHECK", + envValue: "true", + isValid: func(cmd *Command) bool { + return cmd.healthCheck == true + }, + }, + { + desc: "using the http address envvar", + envName: "ALLOYDB_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: "ALLOYDB_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{ + "projects/proj/locations/region/clusters/clust/instances/inst", + }) + if err != nil { + t.Fatalf("want error = nil, got = %v", err) + } + + if !tc.isValid(c) { + t.Fatal("want valid, got invalid") + } + }) + } +} + +func TestNewCommandWithEnvironmentConfig(t *testing.T) { + tcs := []struct { + desc string + envName string + envValue string + want *proxy.Config + }{ + { + desc: "using the address envvar", + envName: "ALLOYDB_PROXY_ADDRESS", + envValue: "0.0.0.0", + want: withDefaults(&proxy.Config{ + Addr: "0.0.0.0", + }), + }, + { + desc: "using the port envvar", + envName: "ALLOYDB_PROXY_PORT", + envValue: "6000", + want: withDefaults(&proxy.Config{ + Port: 6000, + }), + }, + { + desc: "using the token envvar", + envName: "ALLOYDB_PROXY_TOKEN", + envValue: "MYCOOLTOKEN", + want: withDefaults(&proxy.Config{ + Token: "MYCOOLTOKEN", + }), + }, + { + desc: "using the credentiale file envvar", + envName: "ALLOYDB_PROXY_CREDENTIALS_FILE", + envValue: "/path/to/file", + want: withDefaults(&proxy.Config{ + CredentialsFile: "/path/to/file", + }), + }, + { + desc: "using the JSON credentials", + envName: "ALLOYDB_PROXY_JSON_CREDENTIALS", + envValue: `{"json":"goes-here"}`, + want: withDefaults(&proxy.Config{ + CredentialsJSON: `{"json":"goes-here"}`, + }), + }, + { + desc: "using the gcloud auth envvar", + envName: "ALLOYDB_PROXY_GCLOUD_AUTH", + envValue: "true", + want: withDefaults(&proxy.Config{ + GcloudAuth: true, + }), + }, + { + desc: "using the api-endpoint envvar", + envName: "ALLOYDB_PROXY_ALLOYDBADMIN_API_ENDPOINT", + envValue: "https://test.googleapis.com/", + want: withDefaults(&proxy.Config{ + APIEndpointURL: "https://test.googleapis.com", + }), + }, + { + desc: "using the unix socket envvar", + envName: "ALLOYDB_PROXY_UNIX_SOCKET", + envValue: "/path/to/dir/", + want: withDefaults(&proxy.Config{ + UnixSocket: "/path/to/dir/", + }), + }, + { + desc: "enabling structured logging", + envName: "ALLOYDB_PROXY_STRUCTURED_LOGS", + envValue: "true", + want: withDefaults(&proxy.Config{ + StructuredLogs: true, + }), + }, + { + desc: "using the max connections envvar", + envName: "ALLOYDB_PROXY_MAX_CONNECTIONS", + envValue: "1", + want: withDefaults(&proxy.Config{ + MaxConnections: 1, + }), + }, + { + desc: "using wait after signterm envvar", + envName: "ALLOYDB_PROXY_MAX_SIGTERM_DELAY", + envValue: "10s", + want: withDefaults(&proxy.Config{ + WaitOnClose: 10 * time.Second, + }), + }, + { + desc: "using the imopersonate service accounn envvar", + envName: "ALLOYDB_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{sampleURI}) + 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 TestNewCommandWithEnvironmentConfigInstanceConnectionName(t *testing.T) { + u := "projects/proj/locations/region/clusters/clust/instances/inst" + tcs := []struct { + desc string + env map[string]string + args []string + want *proxy.Config + }{ + { + desc: "with one instance connection name", + env: map[string]string{ + "ALLOYDB_PROXY_INSTANCE_URI": u, + }, + want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ + {Name: u}, + }}), + }, + { + desc: "with multiple instance connection names", + env: map[string]string{ + "ALLOYDB_PROXY_INSTANCE_URI_0": u + "0", + "ALLOYDB_PROXY_INSTANCE_URI_1": u + "1", + }, + want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ + {Name: u + "0"}, + {Name: u + "1"}, + }}), + }, + { + desc: "when the index skips a number", + env: map[string]string{ + "ALLOYDB_PROXY_INSTANCE_URI_0": u + "0", + "ALLOYDB_PROXY_INSTANCE_URI_2": u + "2", + }, + want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ + {Name: u + "0"}, + }}), + }, + { + desc: "when there are CLI args provided", + env: map[string]string{ + "ALLOYDB_PROXY_INSTANCE_URI": u, + }, + args: []string{u + "1"}, + want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ + {Name: u + "1"}, + }}), + }, + { + desc: "when only an index instance connection name is defined", + env: map[string]string{ + "ALLOYDB_PROXY_INSTANCE_URI_0": u, + }, + want: withDefaults(&proxy.Config{Instances: []proxy.InstanceConnConfig{ + {Name: u}, + }}), + }, + } + 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) } @@ -419,17 +743,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 59002ac9e6..00d3f126a2 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,8 @@ require ( github.com/google/go-cmp v0.5.9 github.com/hanwen/go-fuse/v2 v2.1.0 github.com/spf13/cobra v1.6.1 + github.com/spf13/pflag v1.0.5 + github.com/spf13/viper v1.7.0 go.opencensus.io v0.24.0 go.uber.org/zap v1.24.0 golang.org/x/oauth2 v0.3.0 @@ -25,6 +27,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.1 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -32,6 +35,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.7.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 @@ -43,14 +47,20 @@ require ( github.com/jackc/pgx/v4 v4.17.2 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/lib/pq v1.10.5 // indirect + github.com/magiconair/properties v1.8.1 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/mitchellh/mapstructure v1.4.3 // indirect + github.com/pelletier/go-toml v1.9.3 // 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.6.0 // indirect + github.com/spf13/cast v1.3.1 // indirect + github.com/spf13/jwalterweatherman v1.0.0 // indirect + github.com/subosito/gotenv v1.2.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect @@ -62,5 +72,6 @@ require ( google.golang.org/genproto v0.0.0-20221206210731-b1a01be3a5f6 // indirect google.golang.org/grpc v1.51.0 // indirect google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/ini.v1 v1.51.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 9e34f682c1..d70b654ce7 100644 --- a/go.sum +++ b/go.sum @@ -706,6 +706,7 @@ github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= 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 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= 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= @@ -927,6 +928,7 @@ github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2c 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/gophercloud/gophercloud v0.24.0/go.mod h1:Q8fZtyi5zZxPS/j9aj3sSxtvj41AdQMDwyo1myduD5c= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 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= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -978,6 +980,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= @@ -1072,6 +1075,7 @@ github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= @@ -1114,6 +1118,7 @@ github.com/linode/linodego v1.4.0/go.mod h1:PVsRxSlOiJyvG4/scTszpmZDTdgS+to3X6eS github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= 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 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 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= @@ -1166,6 +1171,7 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 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 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= github.com/mitchellh/mapstructure v1.4.3/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= @@ -1255,6 +1261,7 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 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 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -1362,8 +1369,10 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= @@ -1371,8 +1380,10 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 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 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 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= @@ -1380,6 +1391,7 @@ github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHN github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= 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 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 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= @@ -1388,6 +1400,7 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn 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 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= 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= @@ -1409,6 +1422,7 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 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 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 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= @@ -2234,6 +2248,7 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= 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 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.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=