dflag

package
v1.33.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 21, 2022 License: Apache-2.0, Apache-2.0 Imports: 15 Imported by: 0

README

This came from https://github.com/ldemailly/go-flagz, a fork of the code originally on https://github.com/mwitkow/go-flagz and https://github.com/improbable-eng/go-flagz with initial changes to get the go modules to work, reduce boiler plate needed for configmap watcher, avoid panic when there is extra whitespace, make the watcher work with regular files and relative paths and switched to standard golang flags. And now further changes, simplification, etc... as part of fortio.

Thanks to @mwitkow for having created this originally.

Fortio Dynamic Flags (was Go FlagZ)

Apache 2.0 License

Dynamic, thread-safe flag variables that can be modified at runtime through files, URL endpoint, or Kubernetes configmap changes.

For a similar project for JVM languages (Java, scala) see java-flagz

Now rewritten and simplified and extended thanks to Go 1.18 generics (use versions prior to 1.33 if you want to use the older per type implementation)

This sounds crazy. Why?

File-based or command-line configuration can only be changed when a service restarts. Dynamic flags provide flexibility in normal operations and emergencies. Two examples:

  • A new feature launches that you want to A/B test. You want to gradually enable it for a certain fraction of user requests (1%, 5%, 20%, 50%, 100%) without the need to restart servers.
  • Your service is getting overloaded and you want to disable certain costly features. You can't afford restarting because you'd lose important capacity.

All of this can be done simultaneously across a whole shard of your services.

Features

  • compatible with standard go flag package
  • dynamic flag that are thread-safe and efficient:
    • DynBool
    • DynInt64
    • DynFloat64
    • DynString
    • DynDuration
    • DynStringSlice
    • DynStringSet
    • DynJSON - a flag that takes an arbitrary JSON struct
  • validator functions for each flag, allows the user to provide checks for newly set values
  • notifier functions allow user code to be subscribed to flag changes
  • Kubernetes ConfigMap watcher, see configmap/README.md.
  • a HandlerFunc endpoint.ListFlags that allows for easy inspection of the service's runtime configuration
  • a HandlerFunc endpoint.SetFlag that let's you update the flag values

Here's a teaser of the debug endpoint:

Status Endpoint

Examples

Declare a single flag.FlagSet in some public package (e.g. common.SharedFlagSet) that you'll use throughout your server or stick to flag.CommandLine default flagset for your binary.

Dynamic JSON flag with a validator and notifier
var (
  limitsConfigFlag = dflag.DynJSON(
    common.SharedFlagSet, 
    "rate_limiting_config", 
    &rateLimitConfig{ DefaultRate: 10, Policy: "allow"},
    "Config for service's rate limit",
  ).WithValidator(rateLimitConfigValidator).WithNotifier(onRateLimitChange)
)

This declares a JSON flag of type rateLimitConfig with a default value. Whenever the config changes (statically or dynamically) the rateLimitConfigValidator will be called. If it returns no errors, the flag will be updated and onRateLimitChange will be called with both old and new, allowing the rate-limit mechanism to re-tune.

Dynamic feature flags

var (
  featuresFlag = dflag.DynStringSlice(common.SharedFlagSet, "enabled_features", []string{"fast_index"}, "list of enabled feature markers")
)
...
func MyHandler(resp http.ResponseWriter, req *http.Request) {
   ...
   if existsInStringSlice("fast_index", featuresFlag.Get()) {
     doFastIndex(req)
   }
   ...
}

All access to featuresFlag, which is a []string flag, is synchronised across go-routines using atomic pointer swaps.

Complete example

See a http server complete example.

Status

This code is production quality. It's been running happily in production in its earlier incarnation at Improbable for years and now everywhere fortio runs.

License

dflag (was go-flagz) is released under the Apache 2.0 license. See the LICENSE file for details.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ChecksumFlagSet

func ChecksumFlagSet(flagSet *flag.FlagSet, flagFilter func(flag *flag.Flag) bool) []byte

ChecksumFlagSet will generate a FNV of the *set* values in a FlagSet.

func ComaStringToSlice added in v1.33.0

func ComaStringToSlice(input string) []string

ComaStringToSlice converts a coma separated string to a slice.

func Errorf added in v1.33.0

func Errorf(t *testing.T, format string, rest ...interface{})

Errorf is a local variant to get the right line numbers.

func IsFlagDynamic

func IsFlagDynamic(f *flag.Flag) bool

IsFlagDynamic returns whether the given Flag has been created in a Dynamic mode.

func Parse added in v1.33.0

func Parse[T any](input string) (val T, err error)

Parse converts from string to our supported types (it's the missing generics strconv.Parse[T]).

func ReadFileFlags

func ReadFileFlags(flagSet *flag.FlagSet) error

ReadFileFlags parses the flagset to discover all "fileread" flags and evaluates them.

By reading and evaluating it means: attempts to read the file and set the value.

func ValidateDynFloat64Range

func ValidateDynFloat64Range(fromInclusive float64, toInclusive float64) func(float64) error

ValidateDynFloat64Range returns a validator that checks if the float value is in range.

func ValidateDynInt64Range

func ValidateDynInt64Range(fromInclusive int64, toInclusive int64) func(int64) error

ValidateDynInt64Range returns a validator function that checks if the integer value is in range.

func ValidateDynSetMinElements added in v1.33.0

func ValidateDynSetMinElements[T comparable](count int) func(Set[T]) error

ValidateDynSetMinElements validates that the given Set has at least x elements.

func ValidateDynSliceMinElements added in v1.33.0

func ValidateDynSliceMinElements[T any](count int) func([]T) error

ValidateDynSliceMinElements validates that the given Set has at least x elements.

func ValidateDynStringMatchesRegex

func ValidateDynStringMatchesRegex(matcher *regexp.Regexp) func(string) error

ValidateDynStringMatchesRegex returns a validator function that checks all flag's values against regex.

func ValidateDynStringSetMinElements

func ValidateDynStringSetMinElements(count int) func(Set[string]) error

func ValidateDynStringSliceMinElements

func ValidateDynStringSliceMinElements(count int) func([]string) error

ValidateDynStringSliceMinElements validates that the given string slice has at least x elements.

func ValidateRange added in v1.33.0

func ValidateRange[T constraints.Ordered](fromInclusive T, toInclusive T) func(T) error

ValidateRange returns a validator that checks if the value is in the given range.

Types

type DynBoolValue added in v1.6.2

type DynBoolValue struct {
	DynamicBoolValueTag
	DynValue[bool]
}

DynStringSetValue implements a dynamic set of strings.

func DynBool added in v1.6.2

func DynBool(flagSet *flag.FlagSet, name string, value bool, usage string) *DynBoolValue

DynBool creates a `Flag` that represents `bool` which is safe to change dynamically at runtime.

type DynDurationValue

type DynDurationValue = DynValue[time.Duration] // For backward compatibility

func DynDuration

func DynDuration(flagSet *flag.FlagSet, name string, value time.Duration, usage string) *DynDurationValue

DynDuration creates a `Flag` that represents `time.Duration` which is safe to change dynamically at runtime.

type DynFloat64Value

type DynFloat64Value = DynValue[float64] // For backward compatibility

func DynFloat64

func DynFloat64(flagSet *flag.FlagSet, name string, value float64, usage string) *DynFloat64Value

DynFloat64 creates a `Flag` that represents `float64` which is safe to change dynamically at runtime.

type DynInt64Value

type DynInt64Value = DynValue[int64] // For backward compatibility

func DynInt64

func DynInt64(flagSet *flag.FlagSet, name string, value int64, usage string) *DynInt64Value

DynInt64 creates a `Flag` that represents `int64` which is safe to change dynamically at runtime.

type DynJSONValue

type DynJSONValue struct {
	DynValue[interface{}]
	// contains filtered or unexported fields
}

DynJSONValue is a flag-related JSON struct value wrapper.

func DynJSON

func DynJSON(flagSet *flag.FlagSet, name string, value interface{}, usage string) *DynJSONValue

DynJSON creates a `Flag` that is backed by an arbitrary JSON which is safe to change dynamically at runtime. The `value` must be a pointer to a struct that is JSON (un)marshallable. New values based on the default constructor of `value` type will be created on each update.

func (*DynJSONValue) IsJSON

func (d *DynJSONValue) IsJSON() bool

IsJSON always return true (method is present for the DynamicJSONFlagValue interface tagging).

func (*DynJSONValue) Set

func (d *DynJSONValue) Set(rawInput string) error

Set updates the value from a string representation in a thread-safe manner. This operation may return an error if the provided `input` doesn't parse, or the resulting value doesn't pass an optional validator. If a notifier is set on the value, it will be invoked in a separate go-routine.

func (*DynJSONValue) String

func (d *DynJSONValue) String() string

String returns the canonical string representation of the type.

type DynStringSetValue

type DynStringSetValue struct {
	*DynValue[Set[string]]
}

DynStringSetValue implements a dynamic set of strings.

func DynStringSet

func DynStringSet(flagSet *flag.FlagSet, name string, value []string, usage string) *DynStringSetValue

DynStringSet creates a `Flag` that represents `map[string]struct{}` which is safe to change dynamically at runtime. Unlike `pflag.StringSlice`, consecutive sets don't append to the slice, but override it.

func (*DynStringSetValue) Contains

func (d *DynStringSetValue) Contains(val string) bool

Contains returns whether the specified string is in the flag.

func (*DynStringSetValue) String

func (d *DynStringSetValue) String() string

String represents the canonical representation of the type.

type DynStringSliceValue

type DynStringSliceValue = DynValue[[]string] // For backward compatibility

func DynStringSlice

func DynStringSlice(flagSet *flag.FlagSet, name string, value []string, usage string) *DynStringSliceValue

DynStringSlice creates a `Flag` that represents `[]string` which is safe to change dynamically at runtime. Unlike `pflag.StringSlice`, consecutive sets don't append to the slice, but override it.

type DynStringValue

type DynStringValue = DynValue[string] // For backward compatibility

func DynString

func DynString(flagSet *flag.FlagSet, name string, value string, usage string) *DynStringValue

DynString creates a `Flag` that represents `string` which is safe to change dynamically at runtime.

type DynValue added in v1.33.0

type DynValue[T DynValueTypes] struct {
	DynamicFlagValueTag
	// contains filtered or unexported fields
}

func Dyn added in v1.33.0

func Dyn[T DynValueTypes](flagSet *flag.FlagSet, name string, value T, usage string) *DynValue[T]

func (*DynValue[T]) Get added in v1.33.0

func (d *DynValue[T]) Get() T

Get retrieves the value in a thread-safe manner.

func (*DynValue[T]) PostSet added in v1.33.0

func (d *DynValue[T]) PostSet(val T) error

func (*DynValue[T]) Set added in v1.33.0

func (d *DynValue[T]) Set(rawInput string) error

Set updates the value from a string representation in a thread-safe manner. This operation may return an error if the provided `input` doesn't parse, or the resulting value doesn't pass an optional validator. If a notifier is set on the value, it will be invoked in a separate go-routine.

func (*DynValue[T]) String added in v1.33.0

func (d *DynValue[T]) String() string

String returns the canonical string representation of the type.

func (*DynValue[T]) Type added in v1.33.0

func (d *DynValue[T]) Type() string

Type is an indicator of what this flag represents.

func (*DynValue[T]) WithFileFlag added in v1.33.0

func (d *DynValue[T]) WithFileFlag(defaultPath string) (*DynValue[T], *FileReadValue)

WithFileFlag adds an companion <name>_path flag that allows this value to be read from a file with dflag.ReadFileFlags.

This is useful for reading large JSON files as flags. If the companion flag's value (whether default or overwritten) is set to empty string, nothing is read.

Flag value reads are subject to notifiers and validators.

func (*DynValue[T]) WithInputMutator added in v1.33.0

func (d *DynValue[T]) WithInputMutator(mutator func(inp string) string) *DynValue[T]

WithInputMutator changes the default input string processing (TrimSpace).

func (*DynValue[T]) WithNotifier added in v1.33.0

func (d *DynValue[T]) WithNotifier(notifier func(oldValue T, newValue T)) *DynValue[T]

WithNotifier adds a function is called every time a new value is successfully set. Each notifier is executed in a new go-routine.

func (*DynValue[T]) WithSyncNotifier added in v1.33.0

func (d *DynValue[T]) WithSyncNotifier(notifier func(oldValue T, newValue T)) *DynValue[T]

WithSyncNotifier adds a function is called synchronously every time a new value is successfully set.

func (*DynValue[T]) WithValidator added in v1.33.0

func (d *DynValue[T]) WithValidator(validator func(T) error) *DynValue[T]

WithValidator adds a function that checks values before they're set. Any error returned by the validator will lead to the value being rejected. Validators are executed on the same go-routine as the call to `Set`.

func (*DynValue[T]) WithValueMutator added in v1.33.0

func (d *DynValue[T]) WithValueMutator(mutator func(inp T) T) *DynValue[T]

WithValueMutator adds a function that changes the value of a flag as needed.

type DynValueTypes added in v1.33.0

type DynValueTypes interface {
	bool | time.Duration | float64 | int64 | string | []string | Set[string] | interface{}
}

type DynamicBoolValueTag added in v1.33.0

type DynamicBoolValueTag struct{}

func (*DynamicBoolValueTag) IsBoolFlag added in v1.33.0

func (*DynamicBoolValueTag) IsBoolFlag() bool

type DynamicFlagValue

type DynamicFlagValue interface {
	IsDynamicFlag() bool
}

DynamicFlagValue interface is a tag to know if a type is dynamic or not.

type DynamicFlagValueTag

type DynamicFlagValueTag struct{}

DynamicFlagValueTag is a struct all dynamic flag inherit for marking they are dynamic.

func (*DynamicFlagValueTag) IsDynamicFlag

func (*DynamicFlagValueTag) IsDynamicFlag() bool

IsDynamicFlag always returns true.

type DynamicJSONFlagValue

type DynamicJSONFlagValue interface {
	IsJSON() bool
}

DynamicJSONFlagValue is a tag interface for JSON dynamic flags.

type FileReadValue

type FileReadValue struct {
	DynamicFlagValueTag
	// contains filtered or unexported fields
}

FileReadValue is a flag that wraps another flag and makes it readable from a local file in the filesystem.

func FileReadFlag

func FileReadFlag(flagSet *flag.FlagSet, parentFlagName string, defaultFilePath string) *FileReadValue

FileReadFlag creates a `Flag` that allows you to pass a flag.

If defaultFilePath is non empty, the dflag.ReadFileFlags will expect the file to be there.

func (*FileReadValue) Set

func (f *FileReadValue) Set(path string) error

Set updates the value from a string representation of the file path.

func (*FileReadValue) String

func (f *FileReadValue) String() string

type Set added in v1.33.0

type Set[T comparable] map[T]struct{}

func SetFromSlice added in v1.33.0

func SetFromSlice[T comparable](items []T) Set[T]

SetFromSlice constructs a Set from a slice.

type TestSuite added in v1.32.2

type TestSuite struct {
	// contains filtered or unexported fields
}

TestSuite to be used as base struct for test suites.

func (*TestSuite) SetT added in v1.32.2

func (s *TestSuite) SetT(t *testing.T)

SetT sets the testing.T in the suite object.

func (*TestSuite) T added in v1.32.2

func (s *TestSuite) T() *testing.T

T returns the current testing.T.

type Testify added in v1.32.2

type Testify struct{}

Testify is a short replacement for github.com/stretchr/testify/assert.

func (*Testify) Contains added in v1.32.2

func (d *Testify) Contains(t *testing.T, haystack, needle string, msg ...string)

Contains checks that needle is in haystack.

func (*Testify) Equal added in v1.32.2

func (d *Testify) Equal(t *testing.T, a, b interface{}, msg ...string)

Equal also checks for a equal b.

func (*Testify) EqualValues added in v1.32.2

func (d *Testify) EqualValues(t *testing.T, a, b interface{}, msg ...string)

EqualValues checks for a equal b.

func (*Testify) Error added in v1.32.2

func (d *Testify) Error(t *testing.T, err error, msg ...string)

Error checks/expects an error.

func (*Testify) Fail added in v1.32.2

func (d *Testify) Fail(t *testing.T, msg string)

Fail fails the test.

func (*Testify) False added in v1.32.2

func (d *Testify) False(t *testing.T, b bool, msg ...string)

False checks bool is false.

func (*Testify) NoError added in v1.32.2

func (d *Testify) NoError(t *testing.T, err error, msg ...string)

NoError checks for no errors (nil).

func (*Testify) NotEqual added in v1.32.2

func (d *Testify) NotEqual(t *testing.T, a, b interface{}, msg ...string)

NotEqual checks for a not equal b.

func (*Testify) ObjectsAreEqualValues added in v1.32.2

func (d *Testify) ObjectsAreEqualValues(a, b interface{}) bool

ObjectsAreEqualValues returns true if a == b (through refection).

func (*Testify) Run added in v1.32.2

func (d *Testify) Run(t *testing.T, suite hasT)

Run runs the test suite with SetupTest first and TearDownTest after.

func (*Testify) True added in v1.32.2

func (d *Testify) True(t *testing.T, b bool, msg ...string)

True checks bool is true.

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL