Skip to content

Commit cfcf17d

Browse files
authored
feat: configure the proxy with environment variables (#197)
This is a port of GoogleCloudPlatform/cloud-sql-proxy#1514.
1 parent f4dd91d commit cfcf17d

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
@@ -38,6 +38,8 @@ import (
3838
"github.com/GoogleCloudPlatform/alloydb-auth-proxy/internal/log"
3939
"github.com/GoogleCloudPlatform/alloydb-auth-proxy/internal/proxy"
4040
"github.com/spf13/cobra"
41+
"github.com/spf13/pflag"
42+
"github.com/spf13/viper"
4143
"go.opencensus.io/trace"
4244
)
4345

@@ -243,8 +245,66 @@ Service Account Impersonation
243245
In this example, the environment's IAM principal impersonates
244246
SERVICE_ACCOUNT_3 which impersonates SERVICE_ACCOUNT_2 which then
245247
impersonates the target SERVICE_ACCOUNT_1.
248+
249+
Configuration using environment variables
250+
251+
Instead of using CLI flags, the proxy may be configured using environment
252+
variables. Each environment variable uses "ALLOYDB_PROXY" as a prefix and
253+
is the uppercase version of the flag using underscores as word delimiters.
254+
For example, the --structured-logs flag may be set with the environment
255+
variable ALLOYDB_PROXY_STRUCTURED_LOGS. An invocation of the proxy using
256+
environment variables would look like the following:
257+
258+
ALLOYDB_PROXY_STRUCTURED_LOGS=true \
259+
./alloydb-auth-proxy \
260+
projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE
261+
262+
In addition to CLI flags, instance URIs may also be specified with
263+
environment variables. If invoking the proxy with only one instance URI,
264+
use ALLOYDB_PROXY_INSTANCE_URI. For example:
265+
266+
ALLOYDB_PROXY_INSTANCE_URI=projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE \
267+
./alloydb-auth-proxy
268+
269+
If multiple instance URIs are used, add the index of the instance URI as a
270+
suffix. For example:
271+
272+
ALLOYDB_PROXY_INSTANCE_URI_0=projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE1 \
273+
ALLOYDB_PROXY_INSTANCE_URI_1=projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE2 \
274+
./alloydb-auth-proxy
275+
246276
`
247277

278+
const envPrefix = "ALLOYDB_PROXY"
279+
280+
func instanceFromEnv(args []string) []string {
281+
// This supports naming the first instance first with:
282+
// INSTANCE_URI
283+
// or if that's not defined, with:
284+
// INSTANCE_URI_0
285+
inst := os.Getenv(fmt.Sprintf("%s_INSTANCE_URI", envPrefix))
286+
if inst == "" {
287+
inst = os.Getenv(fmt.Sprintf("%s_INSTANCE_URI_0", envPrefix))
288+
if inst == "" {
289+
return nil
290+
}
291+
}
292+
args = append(args, inst)
293+
294+
i := 1
295+
for {
296+
instN := os.Getenv(fmt.Sprintf("%s_INSTANCE_URI_%d", envPrefix, i))
297+
// if the next instance connection name is not defined, stop checking
298+
// environment variables.
299+
if instN == "" {
300+
break
301+
}
302+
args = append(args, instN)
303+
i++
304+
}
305+
return args
306+
}
307+
248308
// NewCommand returns a Command object representing an invocation of the proxy.
249309
func NewCommand(opts ...Option) *Command {
250310
cmd := &cobra.Command{
@@ -268,6 +328,10 @@ func NewCommand(opts ...Option) *Command {
268328
}
269329

270330
cmd.Args = func(cmd *cobra.Command, args []string) error {
331+
// If args is not already populated, try to read from the environment.
332+
if len(args) == 0 {
333+
args = instanceFromEnv(args)
334+
}
271335
// Handle logger separately from config
272336
if c.conf.StructuredLogs {
273337
c.logger, c.cleanup = log.NewStructuredLogger()
@@ -288,69 +352,87 @@ func NewCommand(opts ...Option) *Command {
288352

289353
cmd.RunE = func(*cobra.Command, []string) error { return runSignalWrapper(c) }
290354

355+
pflags := cmd.PersistentFlags()
356+
291357
// Global-only flags
292-
cmd.PersistentFlags().StringVarP(&c.conf.Token, "token", "t", "",
358+
pflags.StringVarP(&c.conf.Token, "token", "t", "",
293359
"Bearer token used for authorization.")
294-
cmd.PersistentFlags().StringVarP(&c.conf.CredentialsFile, "credentials-file", "c", "",
360+
pflags.StringVarP(&c.conf.CredentialsFile, "credentials-file", "c", "",
295361
"Path to a service account key to use for authentication.")
296-
cmd.PersistentFlags().StringVarP(&c.conf.CredentialsJSON, "json-credentials", "j", "",
362+
pflags.StringVarP(&c.conf.CredentialsJSON, "json-credentials", "j", "",
297363
"Use service account key JSON as a source of IAM credentials.")
298-
cmd.PersistentFlags().BoolVarP(&c.conf.GcloudAuth, "gcloud-auth", "g", false,
364+
pflags.BoolVarP(&c.conf.GcloudAuth, "gcloud-auth", "g", false,
299365
"Use gcloud's user configuration to retrieve a token for authentication.")
300-
cmd.PersistentFlags().BoolVarP(&c.conf.StructuredLogs, "structured-logs", "l", false,
366+
pflags.BoolVarP(&c.conf.StructuredLogs, "structured-logs", "l", false,
301367
"Enable structured logs using the LogEntry format")
302-
cmd.PersistentFlags().Uint64Var(&c.conf.MaxConnections, "max-connections", 0,
368+
pflags.Uint64Var(&c.conf.MaxConnections, "max-connections", 0,
303369
`Limits the number of connections by refusing any additional connections.
304370
When this flag is not set, there is no limit.`)
305-
cmd.PersistentFlags().DurationVar(&c.conf.WaitOnClose, "max-sigterm-delay", 0,
371+
pflags.DurationVar(&c.conf.WaitOnClose, "max-sigterm-delay", 0,
306372
`Maximum amount of time to wait after for any open connections
307373
to close after receiving a TERM signal. The proxy will shut
308374
down when the number of open connections reaches 0 or when
309375
the maximum time has passed. Defaults to 0s.`)
310-
cmd.PersistentFlags().StringVar(&c.conf.APIEndpointURL, "alloydbadmin-api-endpoint",
376+
pflags.StringVar(&c.conf.APIEndpointURL, "alloydbadmin-api-endpoint",
311377
"https://alloydb.googleapis.com/v1beta",
312378
"When set, the proxy uses this host as the base API path.")
313-
cmd.PersistentFlags().StringVar(&c.conf.FUSEDir, "fuse", "",
379+
pflags.StringVar(&c.conf.FUSEDir, "fuse", "",
314380
"Mount a directory at the path using FUSE to access AlloyDB instances.")
315-
cmd.PersistentFlags().StringVar(&c.conf.FUSETempDir, "fuse-tmp-dir",
381+
pflags.StringVar(&c.conf.FUSETempDir, "fuse-tmp-dir",
316382
filepath.Join(os.TempDir(), "csql-tmp"),
317383
"Temp dir for Unix sockets created with FUSE")
318-
cmd.PersistentFlags().StringVar(&c.impersonationChain, "impersonate-service-account", "",
384+
pflags.StringVar(&c.impersonationChain, "impersonate-service-account", "",
319385
`Comma separated list of service accounts to impersonate. Last value
320386
+is the target account.`)
321387
cmd.PersistentFlags().BoolVar(&c.quiet, "quiet", false, "Log error messages only")
322388

323-
cmd.PersistentFlags().StringVar(&c.telemetryProject, "telemetry-project", "",
389+
pflags.StringVar(&c.telemetryProject, "telemetry-project", "",
324390
"Enable Cloud Monitoring and Cloud Trace integration with the provided project ID.")
325-
cmd.PersistentFlags().BoolVar(&c.disableTraces, "disable-traces", false,
391+
pflags.BoolVar(&c.disableTraces, "disable-traces", false,
326392
"Disable Cloud Trace integration (used with telemetry-project)")
327-
cmd.PersistentFlags().IntVar(&c.telemetryTracingSampleRate, "telemetry-sample-rate", 10_000,
393+
pflags.IntVar(&c.telemetryTracingSampleRate, "telemetry-sample-rate", 10_000,
328394
"Configure the denominator of the probabilistic sample rate of traces sent to Cloud Trace\n(e.g., 10,000 traces 1/10,000 calls).")
329-
cmd.PersistentFlags().BoolVar(&c.disableMetrics, "disable-metrics", false,
395+
pflags.BoolVar(&c.disableMetrics, "disable-metrics", false,
330396
"Disable Cloud Monitoring integration (used with telemetry-project)")
331-
cmd.PersistentFlags().StringVar(&c.telemetryPrefix, "telemetry-prefix", "",
397+
pflags.StringVar(&c.telemetryPrefix, "telemetry-prefix", "",
332398
"Prefix to use for Cloud Monitoring metrics.")
333-
cmd.PersistentFlags().BoolVar(&c.prometheus, "prometheus", false,
399+
pflags.BoolVar(&c.prometheus, "prometheus", false,
334400
"Enable Prometheus HTTP endpoint /metrics")
335-
cmd.PersistentFlags().StringVar(&c.prometheusNamespace, "prometheus-namespace", "",
401+
pflags.StringVar(&c.prometheusNamespace, "prometheus-namespace", "",
336402
"Use the provided Prometheus namespace for metrics")
337-
cmd.PersistentFlags().StringVar(&c.httpAddress, "http-address", "localhost",
403+
pflags.StringVar(&c.httpAddress, "http-address", "localhost",
338404
"Address for Prometheus and health check server")
339-
cmd.PersistentFlags().StringVar(&c.httpPort, "http-port", "9090",
405+
pflags.StringVar(&c.httpPort, "http-port", "9090",
340406
"Port for the Prometheus server to use")
341-
cmd.PersistentFlags().BoolVar(&c.healthCheck, "health-check", false,
407+
pflags.BoolVar(&c.healthCheck, "health-check", false,
342408
`Enables HTTP endpoints /startup, /liveness, and /readiness
343409
that report on the proxy's health. Endpoints are available on localhost
344410
only. Uses the port specified by the http-port flag.`)
345411

346412
// Global and per instance flags
347-
cmd.PersistentFlags().StringVarP(&c.conf.Addr, "address", "a", "127.0.0.1",
413+
pflags.StringVarP(&c.conf.Addr, "address", "a", "127.0.0.1",
348414
"Address on which to bind AlloyDB instance listeners.")
349-
cmd.PersistentFlags().IntVarP(&c.conf.Port, "port", "p", 5432,
415+
pflags.IntVarP(&c.conf.Port, "port", "p", 5432,
350416
"Initial port to use for listeners. Subsequent listeners increment from this value.")
351-
cmd.PersistentFlags().StringVarP(&c.conf.UnixSocket, "unix-socket", "u", "",
417+
pflags.StringVarP(&c.conf.UnixSocket, "unix-socket", "u", "",
352418
`Enables Unix sockets for all listeners using the provided directory.`)
353419

420+
v := viper.NewWithOptions(viper.EnvKeyReplacer(strings.NewReplacer("-", "_")))
421+
v.SetEnvPrefix(envPrefix)
422+
v.AutomaticEnv()
423+
// Ignoring the error here since its only occurence is if one of the pflags
424+
// is nil which is never the case here.
425+
_ = v.BindPFlags(pflags)
426+
427+
pflags.VisitAll(func(f *pflag.Flag) {
428+
// Override any unset flags with Viper values to use the pflags
429+
// object as a single source of truth.
430+
if !f.Changed && v.IsSet(f.Name) {
431+
val := v.Get(f.Name)
432+
pflags.Set(f.Name, fmt.Sprintf("%v", val))
433+
}
434+
})
435+
354436
return c
355437
}
356438

0 commit comments

Comments
 (0)