Skip to content

Commit

Permalink
Merge pull request #1 from Pondidum/exit-code
Browse files Browse the repository at this point in the history
Status: add exit codes, tracing
  • Loading branch information
Pondidum committed Nov 14, 2022
2 parents f82098e + 81d258c commit 6d8cfe8
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 43 deletions.
3 changes: 2 additions & 1 deletion backends/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ type User struct {
type Flag struct {
Key string
DefaultValue bool
Value bool
}

type Backend interface {
State(ctx context.Context, flag Flag, user User) (bool, error)
State(ctx context.Context, flag Flag, user User) (Flag, error)
Close(ctx context.Context) error
}
10 changes: 7 additions & 3 deletions backends/launchdarkly/ld.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,23 +54,27 @@ func (ldb *LaunchDarklyBackend) Close(ctx context.Context) error {
return ldb.client.Close()
}

func (ldb *LaunchDarklyBackend) State(ctx context.Context, flag backends.Flag, user backends.User) (bool, error) {
func (ldb *LaunchDarklyBackend) State(ctx context.Context, flag backends.Flag, user backends.User) (backends.Flag, error) {
ctx, span := tr.Start(ctx, "state")
defer span.End()

u := createUser(ctx, user)

span.SetAttributes(attribute.String("flag.key", flag.Key))

flag.Value = flag.DefaultValue

variation, detail, err := ldb.client.BoolVariationDetail(flag.Key, u, flag.DefaultValue)
if err != nil {
return flag.DefaultValue, tracing.Error(span, err)
return flag, tracing.Error(span, err)
}

span.SetAttributes(attribute.String("reason", detail.Reason.String()))
span.SetAttributes(attribute.Bool("variation", variation))

return variation, nil
flag.Value = variation

return flag, nil
}

func createUser(ctx context.Context, user backends.User) lduser.User {
Expand Down
13 changes: 12 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
# Changelog

## [0.0.1] - 2022-11-14

### Added

- Exit with code `0` if a flag is `true`, and `1` otherwise
- Add `--silent` flag, to suppress console information

### Changed

- Expand what information is written to traces

## [0.0.0] - 2022-11-11

### Added

- Initial Version
- Read a flag from LaunchDarkly
- Read a flag from LaunchDarkly
18 changes: 0 additions & 18 deletions command/helpers.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package command

import (
"encoding/json"
"fmt"
"strings"

"github.com/mitchellh/cli"
)

func parseKeyValuePairs(tags []string) (map[string]string, error) {
Expand Down Expand Up @@ -33,18 +30,3 @@ func parseKeyValuePairs(tags []string) (map[string]string, error) {

return m, nil
}

func print(ui cli.Ui, format string, vals map[string]interface{}) error {

switch format {
case "json":
b, err := json.Marshal(vals)
if err != nil {
return err
}
ui.Output(string(b))

}

return nil
}
28 changes: 26 additions & 2 deletions command/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package command

import (
"context"
"encoding/json"
"flagon/backends"
"flagon/backends/launchdarkly"
"flagon/tracing"
Expand All @@ -24,6 +25,7 @@ type Meta struct {

backend string
output string
silent bool

ldFlags launchdarkly.LaunchDarklyConfiguration
}
Expand Down Expand Up @@ -105,6 +107,7 @@ func (m *Meta) allFlags() []FlagGroup {

common.StringVar(&m.backend, "backend", "launchdarkly", "which flag service to use")
common.StringVar(&m.output, "output", "json", "specifies the output format")
common.BoolVar(&m.silent, "silent", false, "don't print anything to stdout/stderr")

return []FlagGroup{
{Name: "Command", FlagSet: m.cmd.Flags()},
Expand Down Expand Up @@ -134,6 +137,25 @@ func (m *Meta) createBackend(ctx context.Context) (backends.Backend, error) {
}
}

func (m *Meta) print(vals interface{}) error {

if m.silent {
return nil
}

switch m.output {
case "json":
b, err := json.Marshal(vals)
if err != nil {
return err
}
m.Ui.Output(string(b))

}

return nil
}

func (m *Meta) Run(args []string) int {
ctx := context.Background()

Expand All @@ -152,8 +174,10 @@ func (m *Meta) Run(args []string) int {
tracing.StoreFlags(ctx, f)

if err := m.cmd.RunContext(ctx, f.Args()); err != nil {
tracing.Error(span, err)
m.Ui.Error(err.Error())
if !IsSilentError(err) {
tracing.Error(span, err)
m.Ui.Error(err.Error())
}

return 1
}
Expand Down
12 changes: 12 additions & 0 deletions command/silent_error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package command

type SilentError struct{}

func (e *SilentError) Error() string {
return ""
}

func IsSilentError(err error) bool {
_, ok := err.(*SilentError)
return ok
}
26 changes: 19 additions & 7 deletions command/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strconv"

"github.com/spf13/pflag"
"go.opentelemetry.io/otel/attribute"
)

type StateCommand struct {
Expand Down Expand Up @@ -57,6 +58,10 @@ func (c *StateCommand) RunContext(ctx context.Context, args []string) error {
Key: args[0],
DefaultValue: defaultValue,
}
span.SetAttributes(
attribute.String("flag.key", flag.Key),
attribute.Bool("flag.default", flag.DefaultValue),
)

attrs, err := parseKeyValuePairs(c.userAttributes)
if err != nil {
Expand All @@ -67,15 +72,22 @@ func (c *StateCommand) RunContext(ctx context.Context, args []string) error {
Key: c.userKey,
Attributes: attrs,
}
span.SetAttributes(attribute.String("user.key", user.Key))
span.SetAttributes(tracing.FromMap("user.", user.Attributes)...)

value, err := backend.State(ctx, flag, user)
if err != nil {
if flag, err = backend.State(ctx, flag, user); err != nil {
return tracing.Error(span, err)
}

if err := c.print(flag); err != nil {
return tracing.Error(span, err)
}

return print(c.Ui, c.output, map[string]interface{}{
"flag": flag.Key,
"default": flag.DefaultValue,
"state": value,
})
span.SetAttributes(attribute.Bool("flag.value", flag.Value))

if flag.Value {
return nil
}

return &SilentError{}
}
47 changes: 44 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,47 @@
## Usage

```
> flagon state "some-flag-name" --user "$user_id" --prop "branch=$branch" --output json
# { "name": "some-flag-name", default: "off", state: "on" }
```
> flagon state "some-flag-name" --user "$user_id" --attr "branch=$branch"
# { "name": "some-flag-name", "defaultValue": false, "value": true }
```

For example, in a CI system where you wish to select which deployment process is used:

```shell
email=$(git show --quiet --pretty="format:%ce")
branch=$(git rev-parse --abbrev-ref HEAD)

if flagon state "ci-replacement-deploy" --user "${email}" --attr "branch=${branch}" --silent; then
./build/replacement-deploy.sh "${branch}" "${commit}"
else
./build/deploy.sh "${branch}" "${commit}"
fi
```

## Configuration

### Common

| Flag | Default | Description |
|-------------|-----------------|-----------------------------------------------------------------------------|
| `--backend` | `launchdarkly` | The backend to query flags from |
| `--output` | `json` | The output format to write to the console. Currently only supports `json` |
| `--silent` | `false` | Silence any console output |

### Telemetry

| EnvVar | Default | Description |
|---------------------------------------|-------------------|-----------------------------------------------------------------|
| `OTEL_TRACE_EXPORTER` | ` ` | Which exporter to use: `otlp`, `stdout`, `stderr` |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | `localhost:4317` | Set the Exporter endpoint |
| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | ` ` | Set the Exporter endpoint, takes priority over `OTEL_EXPORTER_OTLP_ENDPOINT` |
| `OTEL_EXPORTER_OTLP_HEADERS` | ` ` | A Csv of Headers and Values to pass to the tracing service, for example `Authentication: Bearer 13213213,X-Environment: Production` |
| `OTEL_DEBUG` | `false` | Print debug information from tracing to the console |

### Backend: LaunchDarkly

| EnvVar | Flag | Default | Description |
|---------------------|----------------|----------|------------------------------------------------------------------------------|
| `FLAGON_LD_SDKKEY` | `--ld-sdk-key` | | The [project](https://app.launchdarkly.com/settings/projects) SDK Key to use |
| `FLAGON_LD_TIMEOUT` | `--ld-timeout` | `10s` | How long to wait for successful connection |
| `FLAGON_LD_DEBUG` | `--ld-debug` | `0` | Set to `true` (or `1`) to see debug information from the LaunchDarkly client |
17 changes: 9 additions & 8 deletions tracing/span.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@ import (
"go.opentelemetry.io/otel/trace"
)

func SetAttributes(ctx context.Context, attrs ...attribute.KeyValue) {
s := trace.SpanFromContext(ctx)
s.SetAttributes(attrs...)
}
func FromMap[V any](prefix string, m map[string]V) []attribute.KeyValue {

func SetAttribute(ctx context.Context, key string, val interface{}) {
s := trace.SpanFromContext(ctx)
s.SetAttributes(asAttribute(key, val))
attrs := make([]attribute.KeyValue, 0, len(m))

for k, v := range m {
attrs = append(attrs, asAttribute(k, v))
}

return attrs
}

func asAttribute(key string, v interface{}) attribute.KeyValue {
func asAttribute(key string, v any) attribute.KeyValue {

switch val := v.(type) {

Expand Down

0 comments on commit 6d8cfe8

Please sign in to comment.