Skip to content

feat: implement runtime configuration package with multi-org support #14624

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Touchups
Signed-off-by: Danny Kopping <danny@coder.com>
  • Loading branch information
dannykopping authored and Emyrk committed Sep 6, 2024
commit e67da12bb14e9f601cc5bda5d34e6c6e183a7d3d
30 changes: 25 additions & 5 deletions coderd/runtimeconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,19 @@ import (

var ErrKeyNotSet = xerrors.New("key is not set")

// TODO: comment
// Value wraps the type used by the serpent library for its option values.
// This gives us a seam should serpent ever move away from its current implementation.
type Value pflag.Value

// Entry is designed to wrap any type which satisfies the Value interface, which currently all serpent.Option instances do.
// serpent.Option provide configurability to Value instances, and we use this Entry type to extend the functionality of
// those Value instances.
type Entry[T Value] struct {
k string
v T
}

// New creates a new T instance with a defined key and value.
func New[T Value](key, val string) (out Entry[T], err error) {
out.k = key

Expand All @@ -29,6 +34,7 @@ func New[T Value](key, val string) (out Entry[T], err error) {
return out, nil
}

// MustNew is like New but panics if an error occurs.
func MustNew[T Value](key, val string) Entry[T] {
out, err := New[T](key, val)
if err != nil {
Expand All @@ -37,13 +43,15 @@ func MustNew[T Value](key, val string) Entry[T] {
return out
}

// val fronts the T value in the struct, and initializes it should the value be nil.
func (e *Entry[T]) val() T {
if reflect.ValueOf(e.v).IsNil() {
e.v = create[T]()
}
return e.v
}

// key returns the configured key, or fails with ErrKeyNotSet.
func (e *Entry[T]) key() (string, error) {
if e.k == "" {
return "", ErrKeyNotSet
Expand All @@ -52,29 +60,36 @@ func (e *Entry[T]) key() (string, error) {
return e.k, nil
}

// SetKey allows the key to be set.
func (e *Entry[T]) SetKey(k string) {
e.k = k
}

// Set is an alias of SetStartupValue.
func (e *Entry[T]) Set(s string) error {
return e.SetStartupValue(s)
}

func (e *Entry[T]) SetStartupValue(s string) error {
return e.val().Set(s)
}

// MustSet is like Set but panics on error.
func (e *Entry[T]) MustSet(s string) {
err := e.val().Set(s)
if err != nil {
panic(err)
}
}

// SetStartupValue sets the value of the wrapped field. This ONLY sets the value locally, not in the store.
// See SetRuntimeValue.
func (e *Entry[T]) SetStartupValue(s string) error {
return e.val().Set(s)
}

// Type returns the wrapped value's type.
func (e *Entry[T]) Type() string {
return e.val().Type()
}

// String returns the wrapper value's string representation.
func (e *Entry[T]) String() string {
return e.val().String()
}
Expand All @@ -86,6 +101,7 @@ func (e *Entry[T]) StartupValue() T {
return e.val()
}

// SetRuntimeValue attempts to update the runtime value of this field in the store via the given Mutator.
func (e *Entry[T]) SetRuntimeValue(ctx context.Context, m Mutator, val T) error {
key, err := e.key()
if err != nil {
Expand All @@ -95,6 +111,7 @@ func (e *Entry[T]) SetRuntimeValue(ctx context.Context, m Mutator, val T) error
return m.UpsertRuntimeSetting(ctx, key, val.String())
}

// UnsetRuntimeValue removes the runtime value from the store.
func (e *Entry[T]) UnsetRuntimeValue(ctx context.Context, m Mutator) error {
key, err := e.key()
if err != nil {
Expand All @@ -104,6 +121,7 @@ func (e *Entry[T]) UnsetRuntimeValue(ctx context.Context, m Mutator) error {
return m.DeleteRuntimeSetting(ctx, key)
}

// Resolve attempts to resolve the runtime value of this field from the store via the given Resolver.
func (e *Entry[T]) Resolve(ctx context.Context, r Resolver) (T, error) {
var zero T

Expand All @@ -124,6 +142,8 @@ func (e *Entry[T]) Resolve(ctx context.Context, r Resolver) (T, error) {
return inst, nil
}

// Coalesce attempts to resolve the runtime value of this field from the store via the given Resolver. Should no runtime
// value be found, the startup value will be used.
func (e *Entry[T]) Coalesce(ctx context.Context, r Resolver) (T, error) {
var zero T

Expand Down
9 changes: 5 additions & 4 deletions coderd/runtimeconfig/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ package runtimeconfig

import "context"

type Initializer interface {
Init(key string)
}

// Resolver is an interface for resolving runtime settings.
type Resolver interface {
// GetRuntimeSetting gets a runtime setting by key.
GetRuntimeSetting(ctx context.Context, key string) (string, error)
}

// Mutator is an interface for mutating runtime settings.
type Mutator interface {
// UpsertRuntimeSetting upserts a runtime setting by key.
UpsertRuntimeSetting(ctx context.Context, key, val string) error
// DeleteRuntimeSetting deletes a runtime setting by key.
DeleteRuntimeSetting(ctx context.Context, key string) error
}