Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit13ea99a

Browse files
committed
chore: comments, cleanup, error fixes
1 parent7452a23 commit13ea99a

File tree

10 files changed

+196
-120
lines changed

10 files changed

+196
-120
lines changed

‎coderd/database/dbmem/dbmem.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"golang.org/x/xerrors"
2323

2424
"github.com/coder/coder/v2/coderd/notifications/types"
25-
"github.com/coder/coder/v2/coderd/runtimeconfig"
2625

2726
"github.com/coder/coder/v2/coderd/database"
2827
"github.com/coder/coder/v2/coderd/database/dbtime"
@@ -3522,7 +3521,7 @@ func (q *FakeQuerier) GetRuntimeConfig(_ context.Context, key string) (string, e
35223521

35233522
val,ok:=q.runtimeConfig[key]
35243523
if!ok {
3525-
return"",runtimeconfig.EntryNotFound
3524+
return"",sql.ErrNoRows
35263525
}
35273526

35283527
returnval,nil

‎coderd/runtimeconfig/deploymententry.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type SerpentEntry interface {
2121

2222
// DeploymentEntry extends a runtime entry with a startup value.
2323
// This allows for a single entry to source its value from startup or runtime.
24+
// DeploymentEntry will never return ErrEntryNotFound, as it will always return a value.
2425
typeDeploymentEntry[TSerpentEntry]struct {
2526
RuntimeEntry[T]
2627
startupValueT
@@ -52,7 +53,7 @@ func (e *DeploymentEntry[T]) Coalesce(ctx context.Context, r Resolver) (T, error
5253

5354
resolved,err:=e.Resolve(ctx,r)
5455
iferr!=nil {
55-
iferrors.Is(err,EntryNotFound) {
56+
iferrors.Is(err,ErrEntryNotFound) {
5657
returne.StartupValue(),nil
5758
}
5859
returnzero,err
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package runtimeconfig_test
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/google/uuid"
9+
"github.com/stretchr/testify/require"
10+
11+
"github.com/coder/coder/v2/coderd/database/dbmem"
12+
"github.com/coder/coder/v2/coderd/database/dbtestutil"
13+
"github.com/coder/coder/v2/coderd/runtimeconfig"
14+
"github.com/coder/coder/v2/coderd/util/ptr"
15+
"github.com/coder/coder/v2/codersdk"
16+
"github.com/coder/coder/v2/testutil"
17+
"github.com/coder/serpent"
18+
)
19+
20+
funcExampleDeploymentValues() {
21+
ctx:=context.Background()
22+
db:=dbmem.New()
23+
st:=runtimeconfig.NewStoreManager()
24+
25+
// Define the field, this will usually live on Deployment Values.
26+
varstringField runtimeconfig.DeploymentEntry[*serpent.String]
27+
// All fields need to be initialized with their "key". This will be used
28+
// to uniquely identify the field in the store.
29+
stringField.Initialize("string-field")
30+
31+
// The startup value configured by the deployment env vars
32+
// This acts as a default value if no runtime value is set.
33+
// Can be used to support migrating a value from startup to runtime.
34+
_=stringField.SetStartupValue("default")
35+
36+
// Runtime values take priority over startup values.
37+
_=stringField.SetRuntimeValue(ctx,st.Resolver(db),serpent.StringOf(ptr.Ref("hello world")))
38+
39+
// Resolve the value of the field.
40+
val,err:=stringField.Resolve(ctx,st.Resolver(db))
41+
iferr!=nil {
42+
panic(err)
43+
}
44+
fmt.Println(val)
45+
// Output: hello world
46+
}
47+
48+
// TestSerpentDeploymentEntry uses the package as the serpent options will use it.
49+
// Some of the usage might feel awkward, since the serpent package values come from
50+
// the serpent parsing (strings), not manual assignment.
51+
funcTestSerpentDeploymentEntry(t*testing.T) {
52+
t.Parallel()
53+
54+
ctx:=testutil.Context(t,testutil.WaitMedium)
55+
db,_:=dbtestutil.NewDB(t)
56+
st:=runtimeconfig.NewStoreManager()
57+
58+
// TestEntries is how entries are defined in deployment values.
59+
typeTestEntriesstruct {
60+
String runtimeconfig.DeploymentEntry[*serpent.String]
61+
Bool runtimeconfig.DeploymentEntry[*serpent.Bool]
62+
// codersdk.Feature is arbitrary, just using an actual struct to test.
63+
Struct runtimeconfig.DeploymentEntry[*serpent.Struct[codersdk.Feature]]
64+
}
65+
66+
varentriesTestEntries
67+
// Init fields
68+
entries.String.Initialize("string-field")
69+
entries.Bool.Initialize("bool-field")
70+
entries.Struct.Initialize("struct-field")
71+
72+
// When using Coalesce, the default value is the empty value
73+
stringVal,err:=entries.String.Coalesce(ctx,st.Resolver(db))
74+
require.NoError(t,err)
75+
require.Equal(t,"",stringVal.String())
76+
77+
// Set some defaults for some
78+
_=entries.String.SetStartupValue("default")
79+
_=entries.Struct.SetStartupValue((&serpent.Struct[codersdk.Feature]{
80+
Value: codersdk.Feature{
81+
Entitlement:codersdk.EntitlementEntitled,
82+
Enabled:false,
83+
Limit:ptr.Ref(int64(100)),
84+
Actual:nil,
85+
},
86+
}).String())
87+
88+
// Retrieve startup values
89+
stringVal,err=entries.String.Coalesce(ctx,st.Resolver(db))
90+
require.NoError(t,err)
91+
require.Equal(t,"default",stringVal.String())
92+
93+
structVal,err:=entries.Struct.Coalesce(ctx,st.Resolver(db))
94+
require.NoError(t,err)
95+
require.Equal(t,structVal.Value.Entitlement,codersdk.EntitlementEntitled)
96+
require.Equal(t,structVal.Value.Limit,ptr.Ref(int64(100)))
97+
98+
// Override some defaults
99+
err=entries.String.SetRuntimeValue(ctx,st.Resolver(db),serpent.StringOf(ptr.Ref("hello world")))
100+
require.NoError(t,err)
101+
102+
err=entries.Struct.SetRuntimeValue(ctx,st.Resolver(db),&serpent.Struct[codersdk.Feature]{
103+
Value: codersdk.Feature{
104+
Entitlement:codersdk.EntitlementGracePeriod,
105+
},
106+
})
107+
require.NoError(t,err)
108+
109+
// Retrieve runtime values
110+
stringVal,err=entries.String.Coalesce(ctx,st.Resolver(db))
111+
require.NoError(t,err)
112+
require.Equal(t,"hello world",stringVal.String())
113+
114+
structVal,err=entries.Struct.Coalesce(ctx,st.Resolver(db))
115+
require.NoError(t,err)
116+
require.Equal(t,structVal.Value.Entitlement,codersdk.EntitlementGracePeriod)
117+
118+
// Test using org scoped resolver
119+
orgID:=uuid.New()
120+
orgResolver:=st.OrganizationResolver(db,orgID)
121+
// No org runtime set
122+
stringVal,err=entries.String.Coalesce(ctx,orgResolver)
123+
require.NoError(t,err)
124+
require.Equal(t,"default",stringVal.String())
125+
// Update org runtime
126+
err=entries.String.SetRuntimeValue(ctx,orgResolver,serpent.StringOf(ptr.Ref("hello organizations")))
127+
require.NoError(t,err)
128+
// Verify org runtime
129+
stringVal,err=entries.String.Coalesce(ctx,orgResolver)
130+
require.NoError(t,err)
131+
require.Equal(t,"hello organizations",stringVal.String())
132+
}

‎coderd/runtimeconfig/doc.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Package runtimeconfig contains logic for managing runtime configuration values
2+
// stored in the database. Each coderd should have a Manager singleton instance
3+
// that can create a Resolver for runtime configuration CRUD.
4+
//
5+
// TODO: Implement a caching layer for the Resolver so that we don't hit the
6+
// database on every request. Configuration values are not expected to change
7+
// frequently, so we should use pubsub to notify for updates.
8+
// When implemented, the runtimeconfig will essentially be an in memory lookup
9+
// with a database for persistence.
10+
package runtimeconfig

‎coderd/runtimeconfig/entry.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import (
77
"golang.org/x/xerrors"
88
)
99

10-
varErrNameNotSet=xerrors.New("name is not set")
11-
10+
// EntryMarshaller requires all entries to marshal to and from a string.
11+
// The final store value is a database `text` column.
12+
// This also is compatible with serpent values.
1213
typeEntryMarshallerinterface {
1314
fmt.Stringer
1415
}

‎coderd/runtimeconfig/entry_test.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,16 @@ func TestEntry(t *testing.T) {
7979
// Validate that it returns that value.
8080
require.Equal(t,base.String(),field.String())
8181
// Validate that there is no org-level override right now.
82-
_,err:=field.Resolve(ctx,mgr.DeploymentResolver(db))
83-
require.ErrorIs(t,err,runtimeconfig.EntryNotFound)
82+
_,err:=field.Resolve(ctx,mgr.Resolver(db))
83+
require.ErrorIs(t,err,runtimeconfig.ErrEntryNotFound)
8484
// Coalesce returns the deployment-wide value.
85-
val,err:=field.Coalesce(ctx,mgr.DeploymentResolver(db))
85+
val,err:=field.Coalesce(ctx,mgr.Resolver(db))
8686
require.NoError(t,err)
8787
require.Equal(t,base.String(),val.String())
8888
// Set an org-level override.
89-
require.NoError(t,field.SetRuntimeValue(ctx,mgr.DeploymentResolver(db),&override))
89+
require.NoError(t,field.SetRuntimeValue(ctx,mgr.Resolver(db),&override))
9090
// Coalesce now returns the org-level value.
91-
val,err=field.Coalesce(ctx,mgr.DeploymentResolver(db))
91+
val,err=field.Coalesce(ctx,mgr.Resolver(db))
9292
require.NoError(t,err)
9393
require.Equal(t,override.String(),val.String())
9494
})
@@ -119,16 +119,16 @@ func TestEntry(t *testing.T) {
119119
// Check that default has been set.
120120
require.Equal(t,base.String(),field.StartupValue().String())
121121
// Validate that there is no org-level override right now.
122-
_,err:=field.Resolve(ctx,mgr.DeploymentResolver(db))
123-
require.ErrorIs(t,err,runtimeconfig.EntryNotFound)
122+
_,err:=field.Resolve(ctx,mgr.Resolver(db))
123+
require.ErrorIs(t,err,runtimeconfig.ErrEntryNotFound)
124124
// Coalesce returns the deployment-wide value.
125-
val,err:=field.Coalesce(ctx,mgr.DeploymentResolver(db))
125+
val,err:=field.Coalesce(ctx,mgr.Resolver(db))
126126
require.NoError(t,err)
127127
require.Equal(t,base.Value,val.Value)
128128
// Set an org-level override.
129-
require.NoError(t,field.SetRuntimeValue(ctx,mgr.DeploymentResolver(db),&override))
129+
require.NoError(t,field.SetRuntimeValue(ctx,mgr.Resolver(db),&override))
130130
// Coalesce now returns the org-level value.
131-
structVal,err:=field.Resolve(ctx,mgr.DeploymentResolver(db))
131+
structVal,err:=field.Resolve(ctx,mgr.Resolver(db))
132132
require.NoError(t,err)
133133
require.Equal(t,override.Value,structVal.Value)
134134
})

‎coderd/runtimeconfig/manager.go

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,28 @@
11
package runtimeconfig
22

33
import (
4-
"time"
5-
64
"github.com/google/uuid"
7-
8-
"github.com/coder/coder/v2/coderd/util/syncmap"
95
)
106

11-
// StoreManager is the shared singleton that produces resolvers for runtime configuration.
7+
// StoreManager is the singleton that produces resolvers for runtime configuration.
8+
// TODO: Implement caching layer.
129
typeStoreManagerstruct{}
1310

1411
funcNewStoreManager()Manager {
1512
return&StoreManager{}
1613
}
1714

18-
func (*StoreManager)DeploymentResolver(dbStore)Resolver {
15+
// Resolver is the deployment wide namespace for runtime configuration.
16+
// If you are trying to namespace a configuration, orgs for example, use
17+
// OrganizationResolver.
18+
func (*StoreManager)Resolver(dbStore)Resolver {
1919
returnNewStoreResolver(db)
2020
}
2121

22+
// OrganizationResolver will namespace all runtime configuration to the provided
23+
// organization ID. Configuration values stored with a given organization ID require
24+
// that the organization ID be provided to retrieve the value.
25+
// No values set here will ever be returned by the call to 'Resolver()'.
2226
func (*StoreManager)OrganizationResolver(dbStore,orgID uuid.UUID)Resolver {
2327
returnOrganizationResolver(orgID,NewStoreResolver(db))
2428
}
25-
26-
typecacheEntrystruct {
27-
valuestring
28-
lastUpdated time.Time
29-
}
30-
31-
// MemoryCacheManager is an example of how a caching layer can be added to the
32-
// resolver from the manager.
33-
// TODO: Delete MemoryCacheManager and implement it properly in 'StoreManager'.
34-
// TODO: Handle pubsub-based cache invalidation.
35-
typeMemoryCacheManagerstruct {
36-
cache*syncmap.Map[string,cacheEntry]
37-
}
38-
39-
funcNewMemoryCacheManager()*MemoryCacheManager {
40-
return&MemoryCacheManager{
41-
cache:syncmap.New[string,cacheEntry](),
42-
}
43-
}
44-
45-
func (m*MemoryCacheManager)DeploymentResolver(dbStore)Resolver {
46-
returnNewMemoryCachedResolver(m.cache,NewStoreResolver(db))
47-
}
48-
49-
func (m*MemoryCacheManager)OrganizationResolver(dbStore,orgID uuid.UUID)Resolver {
50-
returnOrganizationResolver(orgID,NewMemoryCachedResolver(m.cache,NewStoreResolver(db)))
51-
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp