Skip to content

clarktrimble/launch

Repository files navigation

Launch

launch

Lightly Wrapped envconfig for Golang

Why?

Why wrap the excellent envconfig module?

  • "-h" flag for a list of environment variables available for configuration
  • "-c" flag to show what would be loaded from environment
  • redact type for non-disclosure via json.Marshal
  • usage blerb for a gentle reminder as to purpose
  • top-level error checking, logged via interface
  • simple spinner for Herculean command-line utils
  • demonstrate encapsulated Config for dependencies
  • but as much as anything to test and reuse surprisingly fiddly code

Help Flag

~/proj/launch$ go run examples/thingone/main.go -h

'thingone' demonstrates use of the launch pkg.

The following environment variables are available for configuration:

KEY                   TYPE       DEFAULT    REQUIRED    DESCRIPTION
DEMO_THINGTWO         String     bargle                 the second thing
DEMO_TOKEN            Redact                true        secret for auth
DEMO_SVC_IMPORTANT    String                true        an important value
DEMO_SVC_NOTSOMUCH    Integer    42                     a less important one

Copy and paste from here into env file!

Config Flag and Redact

~/proj/launch$ make
go generate ./...
golangci-lint run ./...
go test -count 1 github.com/clarktrimble/launch github.com/clarktrimble/launch/spinner
ok      github.com/clarktrimble/launch  0.005s
ok      github.com/clarktrimble/launch/spinner  0.014s
:: Building thingone
go build -ldflags '-X main.version=spin.14.d94b8a6' -o bin/thingone examples/thingone/main.go
:: Done

~/proj/launch$ . examples/thingone/env.sh ## source env file

~/proj/launch$ bin/thingone -c
{
  "version": "spin.14.d94b8a6",
  "thing_two": "thingone",
  "token": "--redacted--",
  "demo_svc": {
    "important": "Brush and floss every day!",
    "not_so_much": 42
  }
}

Nice for a quick sanity check!

Notice how version sneaks in with the build and token is redacted.

Encapsulated Config

In some package:

  type Config struct {
    Important string `json:"important" required:"true"`
    NotSoMuch int    `json:"not_so_much" default:"42"`
  }

  func (cfg *Config) New() (svc *SvcLayer, err error) {
    // ...
    return
  }

In main:

  type Config struct {
    Version  string           `json:"version" ignored:"true"`
    Svc      *svclayer.Config `json:"demo_svc"`
  }

  func main() {

    cfg := &Config{Version: version}
    launch.Load(cfg, cfgPrefix)
    // ...

    svc, err := cfg.Svc.New()
    launch.Check(context.Background(), lgr, err)
    // ...
  }

Voila! The package's configuration requirements are encapsulated.

Check out the post over on Ba Blog for more.

Check

Eww, not another module with it's own logger! Hold up, launch logs via an interface:

type Logger interface {
  Error(ctx context.Context, msg string, err error, kv ...interface{})
}

Triggering an error in thingone:

~/proj/launch$ DEMO_SVC_NOTSOMUCH=-1 bin/thingone
msg > starting up
kvs > ::config::{"version":"spin.17.62d3015","thing_two":"thingone","token":"--redacted--","demo_svc":{"important":"Brush and floss every day!","not_so_much":-1}}

err > fatal top-level error nsm may not be negative, got: -1
github.com/clarktrimble/launch/examples/thingone/svc.New
        /home/trimble/proj/launch/examples/thingone/svc/svc.go:26
github.com/clarktrimble/launch/examples/thingone/svc.(*Config).New
        /home/trimble/proj/launch/examples/thingone/svc/svc.go:41
...

We'll want to keep this sort of thing to a minimum in main of course. Nice to capture the error in the logs though when it's expeditious.

Notice that token is still redacted.

Hat tip to the legendary Dave Cheney for the stack trace.

minlog

Eww, what's with the scuff log messages?

The logging seen above is from the humble minlog, shoe-horned into the example. For a more featured logger that emits json, have a look at github.com/clarktrimble/sabot.

Spinner

Spinner shows a ... well, spinner, showing the next character in a slice each time Spin is called.

  sp := spinner.New()
  for i := 0; i < 99; i++ {
    sp.Spin()
    time.Sleep(time.Millisecond * time.Duration(rand.Intn(99)))
  }

  fmt.Printf("%d operations in %.2f seconds\n", sp.Count, sp.Elapsed())

A little fluffy perhaps, but nice to have when wielding a sluggish util.

About

Lightly Wrapped envconfig for Golang main

Resources

License

Stars

Watchers

Forks

Packages

No packages published