From d232d6c1dcc623a0106537ec12d11318ec82bc80 Mon Sep 17 00:00:00 2001 From: Eno Compton Date: Wed, 24 Jan 2024 11:58:11 -0700 Subject: [PATCH] docs: improve documentation for subcommand (#568) This is a port of https://github.com/GoogleCloudPlatform/cloud-sql-proxy/pull/2105 --- cmd/root.go | 451 +++++++++++++++++++++++++++------------------------- 1 file changed, 236 insertions(+), 215 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 421ab6e0..e86b64cc 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -112,250 +112,252 @@ func WithDialer(d alloydb.Dialer) Option { var longHelp = ` Overview - The AlloyDB Auth proxy is a utility for ensuring secure connections - to your AlloyDB instances. It provides IAM authorization, allowing you - to control who can connect to your instances through IAM permissions, and TLS - 1.3 encryption, without having to manage certificates. + The AlloyDB Auth proxy is a utility for ensuring secure connections + to your AlloyDB instances. It provides IAM authorization, allowing you + to control who can connect to your instances through IAM permissions, and TLS + 1.3 encryption, without having to manage certificates. - NOTE: The proxy does not configure the network. You MUST ensure the proxy - can reach your AlloyDB instance, either by deploying it in a VPC that has - access to your instance, or by ensuring a network path to the instance. + NOTE: The proxy does not configure the network. You MUST ensure the proxy + can reach your AlloyDB instance, either by deploying it in a VPC that has + access to your instance, or by ensuring a network path to the instance. - For every provided instance connection name, the proxy creates: + For every provided instance connection name, the proxy creates: - - a socket that mimics a database running locally, and - - an encrypted connection using TLS 1.3 back to your AlloyDB instance. + - a socket that mimics a database running locally, and + - an encrypted connection using TLS 1.3 back to your AlloyDB instance. - The proxy uses an ephemeral certificate to establish a secure connection to - your AlloyDB instance. The proxy will refresh those certificates on an - hourly basis. Existing client connections are unaffected by the refresh - cycle. + The proxy uses an ephemeral certificate to establish a secure connection to + your AlloyDB instance. The proxy will refresh those certificates on an + hourly basis. Existing client connections are unaffected by the refresh + cycle. Authentication - The Proxy uses Application Default Credentials by default. Enable these - credentials with gcloud: + The Proxy uses Application Default Credentials by default. Enable these + credentials with gcloud: - gcloud auth application-default login + gcloud auth application-default login - In Google-run environments, Application Default Credentials are already - available and do not need to be retrieved. + In Google-run environments, Application Default Credentials are already + available and do not need to be retrieved. - The Proxy will use the environment's IAM principal when authenticating to - the backend. To use a specific set of credentials, use the - --credentials-file flag, e.g., + The Proxy will use the environment's IAM principal when authenticating to + the backend. To use a specific set of credentials, use the + --credentials-file flag, e.g., - ./alloydb-auth-proxy --credentials-file /path/to/key.json \ - projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE + ./alloydb-auth-proxy --credentials-file /path/to/key.json \ + projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE - See the individual flags below, for more options. + See the individual flags below, for more options. Starting the Proxy - To start the proxy, you will need your instance URI, which may be found in - the AlloyDB instance overview page or by using gcloud with the following - command: + To start the proxy, you will need your instance URI, which may be found in + the AlloyDB instance overview page or by using gcloud with the following + command: - gcloud alpha alloydb instances describe INSTANCE_NAME \ - --region=REGION --cluster CLUSTER_NAME --format='value(name)' + gcloud alpha alloydb instances describe INSTANCE_NAME \ + --region=REGION --cluster CLUSTER_NAME --format='value(name)' - For example, if your instance URI is: + For example, if your instance URI is: - projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE + projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE - Starting the proxy will look like: + Starting the proxy will look like: - ./alloydb-auth-proxy \ - projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE + ./alloydb-auth-proxy \ + projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE - By default, the proxy will start a TCP listener on Postgres' default port - 5432. If multiple instances are specified which all use the same database - engine, the first will be started on the default port and subsequent - instances will be incremented from there (e.g., 5432, 5433, 5434, etc.) To - disable this behavior, use the --port flag. All subsequent listeners will - increment from the provided value. + By default, the proxy will start a TCP listener on Postgres' default port + 5432. If multiple instances are specified which all use the same database + engine, the first will be started on the default port and subsequent + instances will be incremented from there (e.g., 5432, 5433, 5434, etc.) To + disable this behavior, use the --port flag. All subsequent listeners will + increment from the provided value. - All socket listeners use the localhost network interface. To override this - behavior, use the --address flag. + All socket listeners use the localhost network interface. To override this + behavior, use the --address flag. Instance Level Configuration - The proxy supports overriding configuration on an instance-level with an - optional query string syntax using the corresponding full flag name. The - query string takes the form of a URL query string and should be appended to - the instance URI, e.g., + The proxy supports overriding configuration on an instance-level with an + optional query string syntax using the corresponding full flag name. The + query string takes the form of a URL query string and should be appended to + the instance URI, e.g., - 'projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE?key1=value1' + 'projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE?key1=value1' - When using the optional query string syntax, quotes must wrap the instance - connection name and query string to prevent conflicts with the shell. For - example, to override the address and port for one instance but otherwise use - the default behavior, use: + When using the optional query string syntax, quotes must wrap the instance + connection name and query string to prevent conflicts with the shell. For + example, to override the address and port for one instance but otherwise use + the default behavior, use: - ./alloydb-auth-proxy \ - 'projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE1' \ - 'projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE2?address=0.0.0.0&port=7000' + ./alloydb-auth-proxy \ + 'projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE1' \ + 'projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE2?address=0.0.0.0&port=7000' - When necessary, you may specify the full path to a Unix socket. Set the - unix-socket-path query parameter to the absolute path of the Unix socket for - the database instance. The parent directory of the unix-socket-path must - exist when the proxy starts or else socket creation will fail. For Postgres - instances, the proxy will ensure that the last path element is - '.s.PGSQL.5432' appending it if necessary. For example, + When necessary, you may specify the full path to a Unix socket. Set the + unix-socket-path query parameter to the absolute path of the Unix socket for + the database instance. The parent directory of the unix-socket-path must + exist when the proxy starts or else socket creation will fail. For Postgres + instances, the proxy will ensure that the last path element is + '.s.PGSQL.5432' appending it if necessary. For example, - ./alloydb-auth-proxy \ - 'projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE1?unix-socket-path=/path/to/socket' - - (*) indicates a flag that may be used as a query parameter + ./alloydb-auth-proxy \ + 'projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE1?unix-socket-path=/path/to/socket' Automatic IAM Authentication - The Auth Proxy support Automatic IAM Authentication where the Proxy - retrieves the environment's IAM principal's OAuth2 token and supplies it to - the backend. When a client connects to the Proxy, there is no need to supply - a database user password. + The Auth Proxy support Automatic IAM Authentication where the Proxy + retrieves the environment's IAM principal's OAuth2 token and supplies it to + the backend. When a client connects to the Proxy, there is no need to supply + a database user password. - To enable the feature, run: + To enable the feature, run: - ./alloydb-auth-proxy \ - --auto-iam-authn \ - 'projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE' + ./alloydb-auth-proxy \ + --auto-iam-authn \ + 'projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE' - In addition, Auto IAM AuthN may be enabled on a per-instance basis with the - query string syntax described above. + In addition, Auto IAM AuthN may be enabled on a per-instance basis with the + query string syntax described above. - ./alloydb-auth-proxy \ - 'projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE?auto-iam-authn=true' + ./alloydb-auth-proxy \ + 'projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE?auto-iam-authn=true' Health checks - When enabling the --health-check flag, the proxy will start an HTTP server - on localhost with three endpoints: + When enabling the --health-check flag, the proxy will start an HTTP server + on localhost with three endpoints: - - /startup: Returns 200 status when the proxy has finished starting up. - Otherwise returns 503 status. + - /startup: Returns 200 status when the proxy has finished starting up. + Otherwise returns 503 status. - - /readiness: Returns 200 status when the proxy has started, has available - connections if max connections have been set with the --max-connections - flag, and when the proxy can connect to all registered instances. Otherwise, - returns a 503 status. Optionally supports a min-ready query param (e.g., - /readiness?min-ready=3) where the proxy will return a 200 status if the - proxy can connect successfully to at least min-ready number of instances. If - min-ready exceeds the number of registered instances, returns a 400. + - /readiness: Returns 200 status when the proxy has started, has available + connections if max connections have been set with the --max-connections + flag, and when the proxy can connect to all registered instances. Otherwise, + returns a 503 status. Optionally supports a min-ready query param (e.g., + /readiness?min-ready=3) where the proxy will return a 200 status if the + proxy can connect successfully to at least min-ready number of instances. If + min-ready exceeds the number of registered instances, returns a 400. - - /liveness: Always returns 200 status. If this endpoint is not responding, - the proxy is in a bad state and should be restarted. + - /liveness: Always returns 200 status. If this endpoint is not responding, + the proxy is in a bad state and should be restarted. - To configure the address, use --http-address. To configure the port, use - --http-port. + To configure the address, use --http-address. To configure the port, use + --http-port. Service Account Impersonation - The proxy supports service account impersonation with the - --impersonate-service-account flag and matches gcloud's flag. When enabled, - all API requests are made impersonating the supplied service account. The - IAM principal must have the iam.serviceAccounts.getAccessToken permission or - the role roles/iam.serviceAccounts.serviceAccountTokenCreator. + The proxy supports service account impersonation with the + --impersonate-service-account flag and matches gcloud's flag. When enabled, + all API requests are made impersonating the supplied service account. The + IAM principal must have the iam.serviceAccounts.getAccessToken permission or + the role roles/iam.serviceAccounts.serviceAccountTokenCreator. - For example: + For example: - ./alloydb-auth-proxy \ - --impersonate-service-account=impersonated@my-project.iam.gserviceaccount.com - projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE + ./alloydb-auth-proxy \ + --impersonate-service-account=impersonated@my-project.iam.gserviceaccount.com + projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE - In addition, the flag supports an impersonation delegation chain where the - value is a comma-separated list of service accounts. The first service - account in the list is the impersonation target. Each subsequent service - account is a delegate to the previous service account. When delegation is - used, each delegate must have the permissions named above on the service - account it is delegating to. + In addition, the flag supports an impersonation delegation chain where the + value is a comma-separated list of service accounts. The first service + account in the list is the impersonation target. Each subsequent service + account is a delegate to the previous service account. When delegation is + used, each delegate must have the permissions named above on the service + account it is delegating to. - For example: + For example: - ./alloydb-auth-proxy \ - --impersonate-service-account=SERVICE_ACCOUNT_1,SERVICE_ACCOUNT_2,SERVICE_ACCOUNT_3 - projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE + ./alloydb-auth-proxy \ + --impersonate-service-account=SERVICE_ACCOUNT_1,SERVICE_ACCOUNT_2,SERVICE_ACCOUNT_3 + projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE - 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. + 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: + 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 + 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: + 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 + 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: + 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 + 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 Localhost Admin Server - The Proxy includes support for an admin server on localhost. By default, - the admin server is not enabled. To enable the server, pass the --debug or - --quitquitquit flag. This will start the server on localhost at port 9091. - To change the port, use the --admin-port flag. + The Proxy includes support for an admin server on localhost. By default, + the admin server is not enabled. To enable the server, pass the --debug or + --quitquitquit flag. This will start the server on localhost at port 9091. + To change the port, use the --admin-port flag. - When --debug is set, the admin server enables Go's profiler available at - /debug/pprof/. + When --debug is set, the admin server enables Go's profiler available at + /debug/pprof/. - See the documentation on pprof for details on how to use the - profiler at https://pkg.go.dev/net/http/pprof. + See the documentation on pprof for details on how to use the + profiler at https://pkg.go.dev/net/http/pprof. - When --quitquitquit is set, the admin server adds an endpoint at - /quitquitquit. The admin server exits gracefully when it receives a POST - request at /quitquitquit. + When --quitquitquit is set, the admin server adds an endpoint at + /quitquitquit. The admin server exits gracefully when it receives a POST + request at /quitquitquit. Waiting for Startup - Sometimes it is necessary to wait for the Proxy to start. + See the wait subcommand's help for details. - To help ensure the Proxy is up and ready, the Proxy includes a wait - subcommand with an optional --max flag to set the maximum time to wait. +(*) indicates a flag that may be used as a query parameter +` - Invoke the wait command, like this: +var waitHelp = ` + Sometimes it is necessary to wait for the Proxy to start. - ./alloydb-auth-proxy wait + To help ensure the Proxy is up and ready, the Proxy includes a wait + subcommand with an optional --max flag to set the maximum time to wait. - By default, the Proxy will wait up to the maximum time for the startup - endpoint to respond. The wait command requires that the Proxy be started in - another process with the HTTP health check enabled. If an alternate health - check port or address is used, as in: + Invoke the wait command, like this: - ./alloydb-auth-proxy \ - --http-address 0.0.0.0 \ - --http-port 9191 + ./alloydb-auth-proxy wait - Then the wait command must also be told to use the same custom values: + By default, the Proxy will wait up to the maximum time for the startup + endpoint to respond. The wait command requires that the Proxy be started in + another process with the HTTP health check enabled. If an alternate health + check port or address is used, as in: - ./alloydb-auth-proxy wait \ - --http-address 0.0.0.0 \ - --http-port 9191 + ./alloydb-auth-proxy \ + --http-address 0.0.0.0 \ + --http-port 9191 - By default the wait command will wait 30 seconds. To alter this value, - use: + Then the wait command must also be told to use the same custom values: - ./alloydb-auth-proxy wait --max 10s + ./alloydb-auth-proxy wait \ + --http-address 0.0.0.0 \ + --http-port 9191 -(*) indicates a flag that may be used as a query parameter + By default the wait command will wait 30 seconds. To alter this value, + use: + + ./alloydb-auth-proxy wait --max 10s ` const envPrefix = "ALLOYDB_PROXY" @@ -446,10 +448,12 @@ func NewCommand(opts ...Option) *Command { } var waitCmd = &cobra.Command{ - Use: "wait", - RunE: runWaitCmd, + Use: "wait", + Short: "Wait for another Proxy process to start", + Long: waitHelp, + RunE: runWaitCmd, } - waitFlags := waitCmd.PersistentFlags() + waitFlags := waitCmd.Flags() waitFlags.DurationP( waitMaxFlag, "m", 30*time.Second, @@ -481,86 +485,91 @@ func NewCommand(opts ...Option) *Command { rootCmd.RunE = func(*cobra.Command, []string) error { return runSignalWrapper(c) } - pflags := rootCmd.PersistentFlags() + // Flags that apply only to the root command + localFlags := rootCmd.Flags() + // Flags that apply to all sub-commands + globalFlags := rootCmd.PersistentFlags() + + localFlags.BoolP("help", "h", false, "Display help information for cloud-sql-proxy") + localFlags.BoolP("version", "v", false, "Print the cloud-sql-proxy version") - // Global-only flags - pflags.StringVar(&c.conf.OtherUserAgents, "user-agent", "", + localFlags.StringVar(&c.conf.OtherUserAgents, "user-agent", "", "Space separated list of additional user agents, e.g. custom-agent/0.0.1") - pflags.StringVarP(&c.conf.Token, "token", "t", "", + localFlags.StringVarP(&c.conf.Token, "token", "t", "", "Bearer token used for authorization.") - pflags.StringVarP(&c.conf.CredentialsFile, "credentials-file", "c", "", + localFlags.StringVarP(&c.conf.CredentialsFile, "credentials-file", "c", "", "Path to a service account key to use for authentication.") - pflags.StringVarP(&c.conf.CredentialsJSON, "json-credentials", "j", "", + localFlags.StringVarP(&c.conf.CredentialsJSON, "json-credentials", "j", "", "Use service account key JSON as a source of IAM credentials.") - pflags.BoolVarP(&c.conf.GcloudAuth, "gcloud-auth", "g", false, + localFlags.BoolVarP(&c.conf.GcloudAuth, "gcloud-auth", "g", false, `Use gcloud's user credentials as a source of IAM credentials. NOTE: this flag is a legacy feature and generally should not be used. Instead prefer Application Default Credentials (enabled with: gcloud auth application-default login) which the Proxy will then pick-up automatically.`) - pflags.BoolVarP(&c.conf.StructuredLogs, "structured-logs", "l", false, + localFlags.BoolVarP(&c.conf.StructuredLogs, "structured-logs", "l", false, "Enable structured logs using the LogEntry format") - pflags.Uint64Var(&c.conf.MaxConnections, "max-connections", 0, + localFlags.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.`) - pflags.DurationVar(&c.conf.WaitOnClose, "max-sigterm-delay", 0, + localFlags.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.`) - pflags.StringVar(&c.conf.APIEndpointURL, "alloydbadmin-api-endpoint", + localFlags.StringVar(&c.conf.APIEndpointURL, "alloydbadmin-api-endpoint", "https://alloydb.googleapis.com", "When set, the proxy uses this host as the base API path.") - pflags.StringVar(&c.conf.FUSEDir, "fuse", "", + localFlags.StringVar(&c.conf.FUSEDir, "fuse", "", "Mount a directory at the path using FUSE to access AlloyDB instances.") - pflags.StringVar(&c.conf.FUSETempDir, "fuse-tmp-dir", + localFlags.StringVar(&c.conf.FUSETempDir, "fuse-tmp-dir", filepath.Join(os.TempDir(), "alloydb-tmp"), "Temp dir for Unix sockets created with FUSE") - pflags.StringVar(&c.conf.ImpersonationChain, "impersonate-service-account", "", + localFlags.StringVar(&c.conf.ImpersonationChain, "impersonate-service-account", "", `Comma separated list of service accounts to impersonate. Last value +is the target account.`) rootCmd.PersistentFlags().BoolVar(&c.conf.Quiet, "quiet", false, "Log error messages only") - pflags.StringVar(&c.conf.TelemetryProject, "telemetry-project", "", + localFlags.StringVar(&c.conf.TelemetryProject, "telemetry-project", "", "Enable Cloud Monitoring and Cloud Trace integration with the provided project ID.") - pflags.BoolVar(&c.conf.DisableTraces, "disable-traces", false, + localFlags.BoolVar(&c.conf.DisableTraces, "disable-traces", false, "Disable Cloud Trace integration (used with telemetry-project)") - pflags.IntVar(&c.conf.TelemetryTracingSampleRate, "telemetry-sample-rate", 10_000, + localFlags.IntVar(&c.conf.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).") - pflags.BoolVar(&c.conf.DisableMetrics, "disable-metrics", false, + localFlags.BoolVar(&c.conf.DisableMetrics, "disable-metrics", false, "Disable Cloud Monitoring integration (used with telemetry-project)") - pflags.StringVar(&c.conf.TelemetryPrefix, "telemetry-prefix", "", + localFlags.StringVar(&c.conf.TelemetryPrefix, "telemetry-prefix", "", "Prefix to use for Cloud Monitoring metrics.") - pflags.BoolVar(&c.conf.Prometheus, "prometheus", false, + localFlags.BoolVar(&c.conf.Prometheus, "prometheus", false, "Enable Prometheus HTTP endpoint /metrics") - pflags.StringVar(&c.conf.PrometheusNamespace, "prometheus-namespace", "", + localFlags.StringVar(&c.conf.PrometheusNamespace, "prometheus-namespace", "", "Use the provided Prometheus namespace for metrics") - pflags.StringVar(&c.conf.HTTPAddress, "http-address", "localhost", + globalFlags.StringVar(&c.conf.HTTPAddress, "http-address", "localhost", "Address for Prometheus and health check server") - pflags.StringVar(&c.conf.HTTPPort, "http-port", "9090", + globalFlags.StringVar(&c.conf.HTTPPort, "http-port", "9090", "Port for the Prometheus server to use") - pflags.BoolVar(&c.conf.Debug, "debug", false, + localFlags.BoolVar(&c.conf.Debug, "debug", false, "Enable pprof on the localhost admin server") - pflags.BoolVar(&c.conf.QuitQuitQuit, "quitquitquit", false, + localFlags.BoolVar(&c.conf.QuitQuitQuit, "quitquitquit", false, "Enable quitquitquit endpoint on the localhost admin server") - pflags.StringVar(&c.conf.AdminPort, "admin-port", "9091", + localFlags.StringVar(&c.conf.AdminPort, "admin-port", "9091", "Port for localhost-only admin server") - pflags.BoolVar(&c.conf.HealthCheck, "health-check", false, + localFlags.BoolVar(&c.conf.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.`) - pflags.BoolVar(&c.conf.RunConnectionTest, "run-connection-test", false, `Runs a connection test + localFlags.BoolVar(&c.conf.RunConnectionTest, "run-connection-test", false, `Runs a connection test against all specified instances. If an instance is unreachable, the Proxy exits with a failure status code.`) // Global and per instance flags - pflags.StringVarP(&c.conf.Addr, "address", "a", "127.0.0.1", + localFlags.StringVarP(&c.conf.Addr, "address", "a", "127.0.0.1", "(*) Address on which to bind AlloyDB instance listeners.") - pflags.IntVarP(&c.conf.Port, "port", "p", 5432, + localFlags.IntVarP(&c.conf.Port, "port", "p", 5432, "(*) Initial port to use for listeners. Subsequent listeners increment from this value.") - pflags.StringVarP(&c.conf.UnixSocket, "unix-socket", "u", "", + localFlags.StringVarP(&c.conf.UnixSocket, "unix-socket", "u", "", `(*) Enables Unix sockets for all listeners using the provided directory.`) - pflags.BoolVarP(&c.conf.AutoIAMAuthN, "auto-iam-authn", "i", false, + localFlags.BoolVarP(&c.conf.AutoIAMAuthN, "auto-iam-authn", "i", false, "(*) Enables Automatic IAM Authentication for all instances") v := viper.NewWithOptions(viper.EnvKeyReplacer(strings.NewReplacer("-", "_"))) @@ -568,20 +577,35 @@ status code.`) 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) + _ = v.BindPFlags(globalFlags) + _ = v.BindPFlags(localFlags) - pflags.VisitAll(func(f *pflag.Flag) { - // Override any unset flags with Viper values to use the pflags - // object as a single source of truth. + // Override any unset flags with Viper values to use the pflags + // object as a single source of truth. + localFlags.VisitAll(func(f *pflag.Flag) { if !f.Changed && v.IsSet(f.Name) { val := v.Get(f.Name) - pflags.Set(f.Name, fmt.Sprintf("%v", val)) + _ = localFlags.Set(f.Name, fmt.Sprintf("%v", val)) + } + }) + globalFlags.VisitAll(func(f *pflag.Flag) { + if !f.Changed && v.IsSet(f.Name) { + val := v.Get(f.Name) + _ = globalFlags.Set(f.Name, fmt.Sprintf("%v", val)) } }) return c } +func userHasSetLocal(cmd *Command, f string) bool { + return cmd.LocalFlags().Lookup(f).Changed +} + +func userHasSetGlobal(cmd *Command, f string) bool { + return cmd.PersistentFlags().Lookup(f).Changed +} + func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { // If no instance connection names were provided AND FUSE isn't enabled, // error. @@ -605,13 +629,10 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { return newBadCommandError("cannot specify --fuse-tmp-dir without --fuse") } - userHasSet := func(f string) bool { - return cmd.PersistentFlags().Lookup(f).Changed - } - if userHasSet("address") && userHasSet("unix-socket") { + if userHasSetLocal(cmd, "address") && userHasSetLocal(cmd, "unix-socket") { return newBadCommandError("cannot specify --unix-socket and --address together") } - if userHasSet("port") && userHasSet("unix-socket") { + if userHasSetLocal(cmd, "port") && userHasSetLocal(cmd, "unix-socket") { return newBadCommandError("cannot specify --unix-socket and --port together") } // First, validate global config. @@ -639,7 +660,7 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { return newBadCommandError("cannot specify --json-credentials and --gcloud-auth flags at the same time") } - if userHasSet("alloydbadmin-api-endpoint") { + if userHasSetLocal(cmd, "alloydbadmin-api-endpoint") { _, err := url.Parse(conf.APIEndpointURL) if err != nil { return newBadCommandError(fmt.Sprintf( @@ -653,21 +674,21 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { cmd.logger.Infof("Using API Endpoint %v", conf.APIEndpointURL) } - if userHasSet("http-port") && !userHasSet("prometheus") && !userHasSet("health-check") { + if userHasSetGlobal(cmd, "http-port") && !userHasSetLocal(cmd, "prometheus") && !userHasSetLocal(cmd, "health-check") { cmd.logger.Infof("Ignoring --http-port because --prometheus or --health-check was not set") } - if !userHasSet("telemetry-project") && userHasSet("telemetry-prefix") { + if !userHasSetLocal(cmd, "telemetry-project") && userHasSetLocal(cmd, "telemetry-prefix") { cmd.logger.Infof("Ignoring --telementry-prefix as --telemetry-project was not set") } - if !userHasSet("telemetry-project") && userHasSet("disable-metrics") { + if !userHasSetLocal(cmd, "telemetry-project") && userHasSetLocal(cmd, "disable-metrics") { cmd.logger.Infof("Ignoring --disable-metrics as --telemetry-project was not set") } - if !userHasSet("telemetry-project") && userHasSet("disable-traces") { + if !userHasSetLocal(cmd, "telemetry-project") && userHasSetLocal(cmd, "disable-traces") { cmd.logger.Infof("Ignoring --disable-traces as --telemetry-project was not set") } - if userHasSet("user-agent") { + if userHasSetLocal(cmd, "user-agent") { defaultUserAgent += " " + cmd.conf.OtherUserAgents conf.UserAgent = defaultUserAgent }