Skip to content

Commit c9d152b

Browse files
committed
feat: configure the proxy with environment variables
This is a port of GoogleCloudPlatform/cloud-sql-proxy#1514.
1 parent 9d5db57 commit c9d152b

File tree

4 files changed

+492
-70
lines changed

4 files changed

+492
-70
lines changed

cmd/root.go

Lines changed: 106 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
247307
func 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.
299365
When 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
302368
to close after receiving a TERM signal. The proxy will shut
303369
down when the number of open connections reaches 0 or when
304370
the 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
337403
that report on the proxy's health. Endpoints are available on localhost
338404
only. 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

Comments
 (0)