jeudi 17 août 2023

What is a Common Pattern for CLI Args vs Environment Vars vs defaults in go?

I would like to define a tool which provides a CLI Interface to set parameters when run locally but can also just use environment variables to set the needed parameters. Lastly there should be sane defaults set for most (not all) parameters.

The precedence should be:

  1. CLI Args
  2. Env Variables
  3. Defaults

Currently I am using cobra to parse the cli args and to define the defaults e.g.:

cmd.PersistentFlags().StringVar(&s3bucketName, "s3-bucket", "my-default-bucket", "S3 Bucket to store results")

In order load the env variables I am then checking every parameter for it's nil value and default and then overwriting it with an environment variable if present.

E.g.

envS3bucket, exists := os.LookupEnv("S3_BUCKET")
if exists && (s3bucketName == "" || s3bucketname == "my-default-bucket") {
  s3bucketName = envS3bucket
}

This is a bit tedious if you do this for a lot of variables. Additionaly I need to define a defaultS3BucketName variable or duplicate the actual default value as seen above.

I also tried doing this with envconfig along the lines of:

import  "github.com/kelseyhightower/envconfig"

type Parameters struct {
  s3Bucketname string `default:"my-default-bucket"`
  ...
}

var params Parameters

[...]

err := envconfig.Process("myapp", &params)
if err != nil {
  log.Fatal(err.Error())
}

cmd.PersistentFlags().StringVar(&params.s3bucketName, "s3-bucket", params.s3bucketName, "S3 Bucket to store results")

This works but it "leaks" the env variables into the help string (e.g. S3 Bucket to store results (default "my-default-bucket")), which is not ideal for keys and passwords.

I assume that I am not the first one to encounter this situation, however I could not find a common pattern as to how this is done. Can someone point me to a clean way of doing this?

Aucun commentaire:

Enregistrer un commentaire