-
Notifications
You must be signed in to change notification settings - Fork 894
chore: refactor entry into deployment and runtime #14575
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
Changes from 1 commit
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
e25a2f5
chore: refactor entry into deployment and runtime
Emyrk a47156d
refactor resolvers and managers
Emyrk 9329805
chore: implement store manager
Emyrk 0e280ca
remove dead code
Emyrk 62fa3b0
remove comment
Emyrk f0ce1c4
fixups
Emyrk 357438b
fixup authz test
Emyrk 522cde1
Update coderd/runtimeconfig/manager.go
Emyrk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
refactor resolvers and managers
- Loading branch information
commit a47156df7030fe98da6fd3b581fe42f5035781b2
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package runtimeconfig | ||
|
||
import ( | ||
"sync" | ||
"time" | ||
) | ||
|
||
type memoryCache struct { | ||
stale time.Duration | ||
mu sync.Mutex | ||
} | ||
|
||
func newMemoryCache(stale time.Duration) *memoryCache { | ||
return &memoryCache{stale: stale} | ||
} | ||
|
||
type MemoryCacheResolver struct { | ||
} | ||
|
||
func NewMemoryCacheResolver() *MemoryCacheResolver { | ||
return &MemoryCacheResolver{} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,80 +1,49 @@ | ||
package runtimeconfig | ||
|
||
import ( | ||
"context" | ||
"database/sql" | ||
"errors" | ||
"fmt" | ||
"time" | ||
|
||
"golang.org/x/xerrors" | ||
"github.com/google/uuid" | ||
|
||
"github.com/coder/coder/v2/coderd/database" | ||
"github.com/coder/coder/v2/coderd/util/syncmap" | ||
) | ||
|
||
type NoopManager struct{} | ||
|
||
func NewNoopManager() *NoopManager { | ||
return &NoopManager{} | ||
type StoreManager struct { | ||
} | ||
|
||
func (NoopManager) GetRuntimeSetting(context.Context, string) (string, error) { | ||
return "", EntryNotFound | ||
func NewStoreManager() *StoreManager { | ||
return &StoreManager{} | ||
} | ||
|
||
func (NoopManager) UpsertRuntimeSetting(context.Context, string, string) error { | ||
return EntryNotFound | ||
func (m *StoreManager) DeploymentResolver(db Store) Resolver { | ||
return NewStoreResolver(db) | ||
} | ||
|
||
func (NoopManager) DeleteRuntimeSetting(context.Context, string) error { | ||
return EntryNotFound | ||
func (m *StoreManager) OrganizationResolver(db Store, orgID uuid.UUID) Resolver { | ||
return OrganizationResolver(orgID, NewStoreResolver(db)) | ||
} | ||
|
||
func (n NoopManager) Scoped(string) Manager { | ||
return n | ||
type cacheEntry struct { | ||
value string | ||
lastUpdated time.Time | ||
} | ||
|
||
type StoreManager struct { | ||
Store | ||
|
||
ns string | ||
} | ||
|
||
func NewStoreManager(store Store) *StoreManager { | ||
if store == nil { | ||
panic("developer error: store must not be nil") | ||
} | ||
return &StoreManager{Store: store} | ||
type MemoryCacheManager struct { | ||
cache *syncmap.Map[string, cacheEntry] | ||
wrapped Manager | ||
} | ||
|
||
func (m StoreManager) GetRuntimeSetting(ctx context.Context, key string) (string, error) { | ||
key = m.namespacedKey(key) | ||
val, err := m.Store.GetRuntimeConfig(ctx, key) | ||
if err != nil { | ||
if errors.Is(err, sql.ErrNoRows) { | ||
return "", xerrors.Errorf("%q: %w", key, EntryNotFound) | ||
} | ||
return "", xerrors.Errorf("fetch %q: %w", key, err) | ||
func NewMemoryCacheManager(wrapped Manager) *MemoryCacheManager { | ||
return &MemoryCacheManager{ | ||
cache: syncmap.New[string, cacheEntry](), | ||
wrapped: wrapped, | ||
} | ||
|
||
return val, nil | ||
} | ||
|
||
func (m StoreManager) UpsertRuntimeSetting(ctx context.Context, key, val string) error { | ||
err := m.Store.UpsertRuntimeConfig(ctx, database.UpsertRuntimeConfigParams{Key: m.namespacedKey(key), Value: val}) | ||
if err != nil { | ||
return xerrors.Errorf("update %q: %w", key, err) | ||
} | ||
return nil | ||
} | ||
|
||
func (m StoreManager) DeleteRuntimeSetting(ctx context.Context, key string) error { | ||
return m.Store.DeleteRuntimeConfig(ctx, m.namespacedKey(key)) | ||
} | ||
|
||
func (m StoreManager) Scoped(ns string) Manager { | ||
return &StoreManager{Store: m.Store, ns: ns} | ||
func (m *MemoryCacheManager) DeploymentResolver(db Store) Resolver { | ||
return NewMemoryCachedResolver(m.cache, m.wrapped.DeploymentResolver(db)) | ||
} | ||
|
||
func (m StoreManager) namespacedKey(k string) string { | ||
return fmt.Sprintf("%s:%s", m.ns, k) | ||
func (m *MemoryCacheManager) OrganizationResolver(db Store, orgID uuid.UUID) Resolver { | ||
return OrganizationResolver(orgID, NewMemoryCachedResolver(m.cache, m.wrapped.DeploymentResolver(db))) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
package runtimeconfig | ||
|
||
import ( | ||
"context" | ||
"database/sql" | ||
"errors" | ||
"fmt" | ||
"time" | ||
|
||
"github.com/google/uuid" | ||
"golang.org/x/xerrors" | ||
|
||
"github.com/coder/coder/v2/coderd/database" | ||
"github.com/coder/coder/v2/coderd/util/syncmap" | ||
) | ||
|
||
type NoopResolver struct{} | ||
|
||
func NewNoopResolver() *NoopResolver { | ||
return &NoopResolver{} | ||
} | ||
|
||
func (NoopResolver) GetRuntimeSetting(context.Context, string) (string, error) { | ||
return "", EntryNotFound | ||
} | ||
|
||
func (NoopResolver) UpsertRuntimeSetting(context.Context, string, string) error { | ||
return EntryNotFound | ||
} | ||
|
||
func (NoopResolver) DeleteRuntimeSetting(context.Context, string) error { | ||
return EntryNotFound | ||
} | ||
|
||
// StoreResolver uses the database as the underlying store for runtime settings. | ||
type StoreResolver struct { | ||
db Store | ||
} | ||
|
||
func NewStoreResolver(db Store) *StoreResolver { | ||
return &StoreResolver{db: db} | ||
} | ||
|
||
func (m StoreResolver) GetRuntimeSetting(ctx context.Context, key string) (string, error) { | ||
val, err := m.db.GetRuntimeConfig(ctx, key) | ||
if err != nil { | ||
if errors.Is(err, sql.ErrNoRows) { | ||
return "", xerrors.Errorf("%q: %w", key, EntryNotFound) | ||
} | ||
return "", xerrors.Errorf("fetch %q: %w", key, err) | ||
} | ||
|
||
return val, nil | ||
} | ||
|
||
func (m StoreResolver) UpsertRuntimeSetting(ctx context.Context, key, val string) error { | ||
err := m.db.UpsertRuntimeConfig(ctx, database.UpsertRuntimeConfigParams{Key: key, Value: val}) | ||
if err != nil { | ||
return xerrors.Errorf("update %q: %w", key, err) | ||
} | ||
return nil | ||
} | ||
|
||
func (m StoreResolver) DeleteRuntimeSetting(ctx context.Context, key string) error { | ||
return m.db.DeleteRuntimeConfig(ctx, key) | ||
} | ||
|
||
type NamespacedResolver struct { | ||
ns string | ||
wrapped Resolver | ||
} | ||
|
||
func OrganizationResolver(orgID uuid.UUID, wrapped Resolver) NamespacedResolver { | ||
return NamespacedResolver{ns: orgID.String(), wrapped: wrapped} | ||
} | ||
|
||
func (m NamespacedResolver) GetRuntimeSetting(ctx context.Context, key string) (string, error) { | ||
return m.wrapped.GetRuntimeSetting(ctx, m.namespacedKey(key)) | ||
} | ||
|
||
func (m NamespacedResolver) UpsertRuntimeSetting(ctx context.Context, key, val string) error { | ||
return m.wrapped.UpsertRuntimeSetting(ctx, m.namespacedKey(key), val) | ||
} | ||
|
||
func (m NamespacedResolver) DeleteRuntimeSetting(ctx context.Context, key string) error { | ||
return m.wrapped.DeleteRuntimeSetting(ctx, m.namespacedKey(key)) | ||
} | ||
|
||
func (m NamespacedResolver) namespacedKey(k string) string { | ||
return fmt.Sprintf("%s:%s", m.ns, k) | ||
} | ||
|
||
// MemoryCachedResolver is a super basic implementation of a cache for runtime | ||
// settings. Essentially, it reuses the shared "cache" that all resolvers should | ||
// use. | ||
type MemoryCachedResolver struct { | ||
cache *syncmap.Map[string, cacheEntry] | ||
|
||
wrapped Resolver | ||
} | ||
|
||
func NewMemoryCachedResolver(cache *syncmap.Map[string, cacheEntry], wrapped Resolver) *MemoryCachedResolver { | ||
return &MemoryCachedResolver{ | ||
cache: cache, | ||
wrapped: wrapped, | ||
} | ||
} | ||
|
||
func (m *MemoryCachedResolver) GetRuntimeSetting(ctx context.Context, key string) (string, error) { | ||
cv, ok := m.cache.Load(key) | ||
if ok { | ||
return cv.value, nil | ||
} | ||
|
||
v, err := m.wrapped.GetRuntimeSetting(ctx, key) | ||
if err != nil { | ||
return "", err | ||
} | ||
m.cache.Store(key, cacheEntry{value: v, lastUpdated: time.Now()}) | ||
return v, nil | ||
} | ||
|
||
func (m *MemoryCachedResolver) UpsertRuntimeSetting(ctx context.Context, key, val string) error { | ||
err := m.wrapped.UpsertRuntimeSetting(ctx, key, val) | ||
if err != nil { | ||
return err | ||
} | ||
m.cache.Store(key, cacheEntry{value: val, lastUpdated: time.Now()}) | ||
return nil | ||
} | ||
|
||
func (m *MemoryCachedResolver) DeleteRuntimeSetting(ctx context.Context, key string) error { | ||
err := m.wrapped.DeleteRuntimeSetting(ctx, key) | ||
if err != nil { | ||
return err | ||
} | ||
m.cache.Delete(key) | ||
return nil | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: panic here if
cache
is nilThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should actually delete the cache resolver before we merge to
main
. Was just testing out some api usage with it.