@@ -37,6 +37,8 @@ import (
3737 "github.com/GoogleCloudPlatform/alloydb-auth-proxy/internal/log"
3838 "github.com/GoogleCloudPlatform/alloydb-auth-proxy/internal/proxy"
3939 "github.com/spf13/cobra"
40+ "github.com/spf13/pflag"
41+ "github.com/spf13/viper"
4042 "go.opencensus.io/trace"
4143)
4244
@@ -241,8 +243,66 @@ Service Account Impersonation
241243 In this example, the environment's IAM principal impersonates
242244 SERVICE_ACCOUNT_3 which impersonates SERVICE_ACCOUNT_2 which then
243245 impersonates the target SERVICE_ACCOUNT_1.
246+
247+ Configuration using environment variables
248+
249+ Instead of using CLI flags, the proxy may be configured using environment
250+ variables. Each environment variable uses "ALLOYDB_PROXY" as a prefix and
251+ is the uppercase version of the flag using underscores as word delimiters.
252+ For example, the --structured-logs flag may be set with the environment
253+ variable ALLOYDB_STRUCTURED_LOGS. An invocation of the proxy using
254+ environment variables would look like the following:
255+
256+ ALLOYDB_STRUCTURED_LOGS=true \
257+ ./alloydb-auth-proxy \
258+ projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE
259+
260+ In addition to CLI flags, instance URIs may also be specified with
261+ environment variables. If invoking the proxy with only one instance URI,
262+ use ALLOYDB_INSTANCE_URI. For example:
263+
264+ ALLOYDB_INSTANCE_URI=projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE \
265+ ./alloydb-auth-proxy
266+
267+ If multiple instance URIs are used, add the index of the instance URI as a
268+ suffix. For example:
269+
270+ ALLOYDB_INSTANCE_URI_0=projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE1 \
271+ ALLOYDB_INSTANCE_URI_1=projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE2 \
272+ ./alloydb-auth-proxy
273+
244274`
245275
276+ const envPrefix = "ALLOYDB"
277+
278+ func instanceFromEnv (args []string ) []string {
279+ // This supports naming the first instance first with:
280+ // INSTANCE_URI
281+ // or if that's not defined, with:
282+ // INSTANCE_URI_0
283+ inst := os .Getenv (fmt .Sprintf ("%s_INSTANCE_URI" , envPrefix ))
284+ if inst == "" {
285+ inst = os .Getenv (fmt .Sprintf ("%s_INSTANCE_URI_0" , envPrefix ))
286+ if inst == "" {
287+ return nil
288+ }
289+ }
290+ args = append (args , inst )
291+
292+ i := 1
293+ for {
294+ instN := os .Getenv (fmt .Sprintf ("%s_INSTANCE_URI_%d" , envPrefix , i ))
295+ // if the next instance connection name is not defined, stop checking
296+ // environment variables.
297+ if instN == "" {
298+ break
299+ }
300+ args = append (args , instN )
301+ i ++
302+ }
303+ return args
304+ }
305+
246306// NewCommand returns a Command object representing an invocation of the proxy.
247307func NewCommand (opts ... Option ) * Command {
248308 cmd := & cobra.Command {
@@ -266,6 +326,10 @@ func NewCommand(opts ...Option) *Command {
266326 }
267327
268328 cmd .Args = func (cmd * cobra.Command , args []string ) error {
329+ // If args is not already populated, try to read from the environment.
330+ if len (args ) == 0 {
331+ args = instanceFromEnv (args )
332+ }
269333 // Handle logger separately from config
270334 if c .conf .StructuredLogs {
271335 c .logger , c .cleanup = log .NewStructuredLogger ()
@@ -283,68 +347,86 @@ func NewCommand(opts ...Option) *Command {
283347
284348 cmd .RunE = func (* cobra.Command , []string ) error { return runSignalWrapper (c ) }
285349
350+ pflags := cmd .PersistentFlags ()
351+
286352 // Global-only flags
287- cmd . PersistentFlags () .StringVarP (& c .conf .Token , "token" , "t" , "" ,
353+ pflags .StringVarP (& c .conf .Token , "token" , "t" , "" ,
288354 "Bearer token used for authorization." )
289- cmd . PersistentFlags () .StringVarP (& c .conf .CredentialsFile , "credentials-file" , "c" , "" ,
355+ pflags .StringVarP (& c .conf .CredentialsFile , "credentials-file" , "c" , "" ,
290356 "Path to a service account key to use for authentication." )
291- cmd . PersistentFlags () .StringVarP (& c .conf .CredentialsJSON , "json-credentials" , "j" , "" ,
357+ pflags .StringVarP (& c .conf .CredentialsJSON , "json-credentials" , "j" , "" ,
292358 "Use service account key JSON as a source of IAM credentials." )
293- cmd . PersistentFlags () .BoolVarP (& c .conf .GcloudAuth , "gcloud-auth" , "g" , false ,
359+ pflags .BoolVarP (& c .conf .GcloudAuth , "gcloud-auth" , "g" , false ,
294360 "Use gcloud's user configuration to retrieve a token for authentication." )
295- cmd . PersistentFlags () .BoolVarP (& c .conf .StructuredLogs , "structured-logs" , "l" , false ,
361+ pflags .BoolVarP (& c .conf .StructuredLogs , "structured-logs" , "l" , false ,
296362 "Enable structured logs using the LogEntry format" )
297- cmd . PersistentFlags () .Uint64Var (& c .conf .MaxConnections , "max-connections" , 0 ,
363+ pflags .Uint64Var (& c .conf .MaxConnections , "max-connections" , 0 ,
298364 `Limits the number of connections by refusing any additional connections.
299365When this flag is not set, there is no limit.` )
300- cmd . PersistentFlags () .DurationVar (& c .conf .WaitOnClose , "max-sigterm-delay" , 0 ,
366+ pflags .DurationVar (& c .conf .WaitOnClose , "max-sigterm-delay" , 0 ,
301367 `Maximum amount of time to wait after for any open connections
302368to close after receiving a TERM signal. The proxy will shut
303369down when the number of open connections reaches 0 or when
304370the maximum time has passed. Defaults to 0s.` )
305- cmd . PersistentFlags () .StringVar (& c .conf .APIEndpointURL , "alloydbadmin-api-endpoint" ,
371+ pflags .StringVar (& c .conf .APIEndpointURL , "alloydbadmin-api-endpoint" ,
306372 "https://alloydb.googleapis.com/v1beta" ,
307373 "When set, the proxy uses this host as the base API path." )
308- cmd . PersistentFlags () .StringVar (& c .conf .FUSEDir , "fuse" , "" ,
374+ pflags .StringVar (& c .conf .FUSEDir , "fuse" , "" ,
309375 "Mount a directory at the path using FUSE to access AlloyDB instances." )
310- cmd . PersistentFlags () .StringVar (& c .conf .FUSETempDir , "fuse-tmp-dir" ,
376+ pflags .StringVar (& c .conf .FUSETempDir , "fuse-tmp-dir" ,
311377 filepath .Join (os .TempDir (), "csql-tmp" ),
312378 "Temp dir for Unix sockets created with FUSE" )
313- cmd . PersistentFlags () .StringVar (& c .impersonationChain , "impersonate-service-account" , "" ,
379+ pflags .StringVar (& c .impersonationChain , "impersonate-service-account" , "" ,
314380 `Comma separated list of service accounts to impersonate. Last value
315381+is the target account.` )
316382
317- cmd . PersistentFlags () .StringVar (& c .telemetryProject , "telemetry-project" , "" ,
383+ pflags .StringVar (& c .telemetryProject , "telemetry-project" , "" ,
318384 "Enable Cloud Monitoring and Cloud Trace integration with the provided project ID." )
319- cmd . PersistentFlags () .BoolVar (& c .disableTraces , "disable-traces" , false ,
385+ pflags .BoolVar (& c .disableTraces , "disable-traces" , false ,
320386 "Disable Cloud Trace integration (used with telemetry-project)" )
321- cmd . PersistentFlags () .IntVar (& c .telemetryTracingSampleRate , "telemetry-sample-rate" , 10_000 ,
387+ pflags .IntVar (& c .telemetryTracingSampleRate , "telemetry-sample-rate" , 10_000 ,
322388 "Configure the denominator of the probabilistic sample rate of traces sent to Cloud Trace\n (e.g., 10,000 traces 1/10,000 calls)." )
323- cmd . PersistentFlags () .BoolVar (& c .disableMetrics , "disable-metrics" , false ,
389+ pflags .BoolVar (& c .disableMetrics , "disable-metrics" , false ,
324390 "Disable Cloud Monitoring integration (used with telemetry-project)" )
325- cmd . PersistentFlags () .StringVar (& c .telemetryPrefix , "telemetry-prefix" , "" ,
391+ pflags .StringVar (& c .telemetryPrefix , "telemetry-prefix" , "" ,
326392 "Prefix to use for Cloud Monitoring metrics." )
327- cmd . PersistentFlags () .BoolVar (& c .prometheus , "prometheus" , false ,
393+ pflags .BoolVar (& c .prometheus , "prometheus" , false ,
328394 "Enable Prometheus HTTP endpoint /metrics" )
329- cmd . PersistentFlags () .StringVar (& c .prometheusNamespace , "prometheus-namespace" , "" ,
395+ pflags .StringVar (& c .prometheusNamespace , "prometheus-namespace" , "" ,
330396 "Use the provided Prometheus namespace for metrics" )
331- cmd . PersistentFlags () .StringVar (& c .httpAddress , "http-address" , "localhost" ,
397+ pflags .StringVar (& c .httpAddress , "http-address" , "localhost" ,
332398 "Address for Prometheus and health check server" )
333- cmd . PersistentFlags () .StringVar (& c .httpPort , "http-port" , "9090" ,
399+ pflags .StringVar (& c .httpPort , "http-port" , "9090" ,
334400 "Port for the Prometheus server to use" )
335- cmd . PersistentFlags () .BoolVar (& c .healthCheck , "health-check" , false ,
401+ pflags .BoolVar (& c .healthCheck , "health-check" , false ,
336402 `Enables HTTP endpoints /startup, /liveness, and /readiness
337403that report on the proxy's health. Endpoints are available on localhost
338404only. Uses the port specified by the http-port flag.` )
339405
340406 // Global and per instance flags
341- cmd . PersistentFlags () .StringVarP (& c .conf .Addr , "address" , "a" , "127.0.0.1" ,
407+ pflags .StringVarP (& c .conf .Addr , "address" , "a" , "127.0.0.1" ,
342408 "Address on which to bind AlloyDB instance listeners." )
343- cmd . PersistentFlags () .IntVarP (& c .conf .Port , "port" , "p" , 5432 ,
409+ pflags .IntVarP (& c .conf .Port , "port" , "p" , 5432 ,
344410 "Initial port to use for listeners. Subsequent listeners increment from this value." )
345- cmd . PersistentFlags () .StringVarP (& c .conf .UnixSocket , "unix-socket" , "u" , "" ,
411+ pflags .StringVarP (& c .conf .UnixSocket , "unix-socket" , "u" , "" ,
346412 `Enables Unix sockets for all listeners using the provided directory.` )
347413
414+ v := viper .NewWithOptions (viper .EnvKeyReplacer (strings .NewReplacer ("-" , "_" )))
415+ v .SetEnvPrefix (envPrefix )
416+ v .AutomaticEnv ()
417+ // Ignoring the error here since its only occurence is if one of the pflags
418+ // is nil which is never the case here.
419+ _ = v .BindPFlags (pflags )
420+
421+ pflags .VisitAll (func (f * pflag.Flag ) {
422+ // Override any unset flags with Viper values to use the pflags
423+ // object as a single source of truth.
424+ if ! f .Changed && v .IsSet (f .Name ) {
425+ val := v .Get (f .Name )
426+ pflags .Set (f .Name , fmt .Sprintf ("%v" , val ))
427+ }
428+ })
429+
348430 return c
349431}
350432
0 commit comments