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

Commit048506d

Browse files
dannykoppingEmyrk
authored andcommitted
Manager, with scoping
Signed-off-by: Danny Kopping <danny@coder.com>
1 parent31f8cbf commit048506d

File tree

6 files changed

+160
-209
lines changed

6 files changed

+160
-209
lines changed

‎coderd/runtimeconfig/config.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func (e *Entry[T]) StartupValue() T {
104104
}
105105

106106
// SetRuntimeValue attempts to update the runtime value of this field in the store via the given Mutator.
107-
func (e*Entry[T])SetRuntimeValue(ctx context.Context,mMutator,valT)error {
107+
func (e*Entry[T])SetRuntimeValue(ctx context.Context,mManager,valT)error {
108108
name,err:=e.name()
109109
iferr!=nil {
110110
returnerr
@@ -114,7 +114,7 @@ func (e *Entry[T]) SetRuntimeValue(ctx context.Context, m Mutator, val T) error
114114
}
115115

116116
// UnsetRuntimeValue removes the runtime value from the store.
117-
func (e*Entry[T])UnsetRuntimeValue(ctx context.Context,mMutator)error {
117+
func (e*Entry[T])UnsetRuntimeValue(ctx context.Context,mManager)error {
118118
name,err:=e.name()
119119
iferr!=nil {
120120
returnerr
@@ -124,7 +124,7 @@ func (e *Entry[T]) UnsetRuntimeValue(ctx context.Context, m Mutator) error {
124124
}
125125

126126
// Resolve attempts to resolve the runtime value of this field from the store via the given Resolver.
127-
func (e*Entry[T])Resolve(ctx context.Context,rResolver) (T,error) {
127+
func (e*Entry[T])Resolve(ctx context.Context,rManager) (T,error) {
128128
varzeroT
129129

130130
name,err:=e.name()
@@ -144,9 +144,9 @@ func (e *Entry[T]) Resolve(ctx context.Context, r Resolver) (T, error) {
144144
returninst,nil
145145
}
146146

147-
// Coalesce attempts to resolve the runtime value of this field from the store via the givenResolver. Should no runtime
147+
// Coalesce attempts to resolve the runtime value of this field from the store via the givenManager. Should no runtime
148148
// value be found, the startup value will be used.
149-
func (e*Entry[T])Coalesce(ctx context.Context,rResolver) (T,error) {
149+
func (e*Entry[T])Coalesce(ctx context.Context,rManager) (T,error) {
150150
varzeroT
151151

152152
resolved,err:=e.Resolve(ctx,r)

‎coderd/runtimeconfig/config_test.go

Lines changed: 68 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,14 @@ import (
44
"context"
55
"testing"
66

7-
"github.com/coder/serpent"
7+
"github.com/google/uuid"
88
"github.com/stretchr/testify/require"
99

10-
"github.com/coder/coder/v2/coderd/coderdtest"
10+
"github.com/coder/serpent"
11+
1112
"github.com/coder/coder/v2/coderd/database/dbmem"
1213
"github.com/coder/coder/v2/coderd/runtimeconfig"
1314
"github.com/coder/coder/v2/coderd/util/ptr"
14-
"github.com/coder/coder/v2/codersdk"
15-
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
16-
"github.com/coder/coder/v2/enterprise/coderd/license"
1715
"github.com/coder/coder/v2/testutil"
1816
)
1917

@@ -38,12 +36,8 @@ func TestUsage(t *testing.T) {
3836
t.Run("deployment value with runtimeconfig",func(t*testing.T) {
3937
t.Parallel()
4038

41-
_,altOrg:=setup(t)
42-
4339
ctx:=testutil.Context(t,testutil.WaitShort)
44-
store:=dbmem.New()
45-
resolver:=runtimeconfig.NewOrgResolver(altOrg.ID,runtimeconfig.NewStoreResolver(store))
46-
mutator:=runtimeconfig.NewOrgMutator(altOrg.ID,runtimeconfig.NewStoreMutator(store))
40+
mgr:=runtimeconfig.NewStoreManager(dbmem.New())
4741

4842
// NOTE: this field is now wrapped
4943
varfield runtimeconfig.Entry[*serpent.HostPort]
@@ -63,32 +57,30 @@ func TestUsage(t *testing.T) {
6357

6458
// One new constraint is that we have to set the name on the runtimeconfig.Entry.
6559
// Attempting to perform any operation which accesses the store will enforce the need for a name.
66-
_,err:=field.Resolve(ctx,resolver)
60+
_,err:=field.Resolve(ctx,mgr)
6761
require.ErrorIs(t,err,runtimeconfig.ErrNameNotSet)
6862

6963
// Let's set that name; the environment var name is likely to be the most stable.
7064
field.Initialize(opt.Env)
7165

7266
newVal:= serpent.HostPort{Host:"12.34.56.78",Port:"1234"}
7367
// Now that we've set it, we can update the runtime value of this field, which modifies given store.
74-
require.NoError(t,field.SetRuntimeValue(ctx,mutator,&newVal))
68+
require.NoError(t,field.SetRuntimeValue(ctx,mgr,&newVal))
7569

7670
// ...and we can retrieve the value, as well.
77-
resolved,err:=field.Resolve(ctx,resolver)
71+
resolved,err:=field.Resolve(ctx,mgr)
7872
require.NoError(t,err)
7973
require.Equal(t,newVal.String(),resolved.String())
8074

8175
// We can also remove the runtime config.
82-
require.NoError(t,field.UnsetRuntimeValue(ctx,mutator))
76+
require.NoError(t,field.UnsetRuntimeValue(ctx,mgr))
8377
})
8478
}
8579

8680
// TestConfig demonstrates creating org-level overrides for deployment-level settings.
8781
funcTestConfig(t*testing.T) {
8882
t.Parallel()
8983

90-
_,altOrg:=setup(t)
91-
9284
t.Run("new",func(t*testing.T) {
9385
t.Parallel()
9486

@@ -105,6 +97,8 @@ func TestConfig(t *testing.T) {
10597
t.Run("zero",func(t*testing.T) {
10698
t.Parallel()
10799

100+
mgr:=runtimeconfig.NewNoopManager()
101+
108102
// A zero-value declaration of a runtimeconfig.Entry should behave as a zero value of the generic type.
109103
// NB! A name has not been set for this entry; it is "uninitialized".
110104
varfield runtimeconfig.Entry[*serpent.Bool]
@@ -115,20 +109,18 @@ func TestConfig(t *testing.T) {
115109
require.NoError(t,field.SetStartupValue("true"))
116110

117111
// But attempting to resolve will produce an error.
118-
_,err:=field.Resolve(context.Background(),runtimeconfig.NewNoopResolver())
112+
_,err:=field.Resolve(context.Background(),mgr)
119113
require.ErrorIs(t,err,runtimeconfig.ErrNameNotSet)
120114
// But attempting to set the runtime value will produce an error.
121115
val:=serpent.BoolOf(ptr.Ref(true))
122-
require.ErrorIs(t,field.SetRuntimeValue(context.Background(),runtimeconfig.NewNoopMutator(),val),runtimeconfig.ErrNameNotSet)
116+
require.ErrorIs(t,field.SetRuntimeValue(context.Background(),mgr,val),runtimeconfig.ErrNameNotSet)
123117
})
124118

125119
t.Run("simple",func(t*testing.T) {
126120
t.Parallel()
127121

128122
ctx:=testutil.Context(t,testutil.WaitShort)
129-
store:=dbmem.New()
130-
resolver:=runtimeconfig.NewOrgResolver(altOrg.ID,runtimeconfig.NewStoreResolver(store))
131-
mutator:=runtimeconfig.NewOrgMutator(altOrg.ID,runtimeconfig.NewStoreMutator(store))
123+
mgr:=runtimeconfig.NewStoreManager(dbmem.New())
132124

133125
var (
134126
base=serpent.String("system@dev.coder.com")
@@ -141,16 +133,16 @@ func TestConfig(t *testing.T) {
141133
// Validate that it returns that value.
142134
require.Equal(t,base.String(),field.String())
143135
// Validate that there is no org-level override right now.
144-
_,err:=field.Resolve(ctx,resolver)
136+
_,err:=field.Resolve(ctx,mgr)
145137
require.ErrorIs(t,err,runtimeconfig.EntryNotFound)
146138
// Coalesce returns the deployment-wide value.
147-
val,err:=field.Coalesce(ctx,resolver)
139+
val,err:=field.Coalesce(ctx,mgr)
148140
require.NoError(t,err)
149141
require.Equal(t,base.String(),val.String())
150142
// Set an org-level override.
151-
require.NoError(t,field.SetRuntimeValue(ctx,mutator,&override))
143+
require.NoError(t,field.SetRuntimeValue(ctx,mgr,&override))
152144
// Coalesce now returns the org-level value.
153-
val,err=field.Coalesce(ctx,resolver)
145+
val,err=field.Coalesce(ctx,mgr)
154146
require.NoError(t,err)
155147
require.Equal(t,override.String(),val.String())
156148
})
@@ -159,9 +151,7 @@ func TestConfig(t *testing.T) {
159151
t.Parallel()
160152

161153
ctx:=testutil.Context(t,testutil.WaitShort)
162-
store:=dbmem.New()
163-
resolver:=runtimeconfig.NewOrgResolver(altOrg.ID,runtimeconfig.NewStoreResolver(store))
164-
mutator:=runtimeconfig.NewOrgMutator(altOrg.ID,runtimeconfig.NewStoreMutator(store))
154+
mgr:=runtimeconfig.NewStoreManager(dbmem.New())
165155

166156
var (
167157
base= serpent.Struct[map[string]string]{
@@ -180,34 +170,65 @@ func TestConfig(t *testing.T) {
180170
// Check that default has been set.
181171
require.Equal(t,base.String(),field.StartupValue().String())
182172
// Validate that there is no org-level override right now.
183-
_,err:=field.Resolve(ctx,resolver)
173+
_,err:=field.Resolve(ctx,mgr)
184174
require.ErrorIs(t,err,runtimeconfig.EntryNotFound)
185175
// Coalesce returns the deployment-wide value.
186-
val,err:=field.Coalesce(ctx,resolver)
176+
val,err:=field.Coalesce(ctx,mgr)
187177
require.NoError(t,err)
188178
require.Equal(t,base.Value,val.Value)
189179
// Set an org-level override.
190-
require.NoError(t,field.SetRuntimeValue(ctx,mutator,&override))
180+
require.NoError(t,field.SetRuntimeValue(ctx,mgr,&override))
191181
// Coalesce now returns the org-level value.
192-
structVal,err:=field.Resolve(ctx,resolver)
182+
structVal,err:=field.Resolve(ctx,mgr)
193183
require.NoError(t,err)
194184
require.Equal(t,override.Value,structVal.Value)
195185
})
196186
}
197187

198-
// setup creates a new API, enabled notifications + multi-org experiments, and returns the API client and a new org.
199-
funcsetup(t*testing.T) (*codersdk.Client, codersdk.Organization) {
200-
t.Helper()
201-
202-
vals:=coderdtest.DeploymentValues(t)
203-
vals.Experiments= []string{string(codersdk.ExperimentMultiOrganization)}
204-
adminClient,_,_,_:=coderdenttest.NewWithAPI(t,&coderdenttest.Options{
205-
Options:&coderdtest.Options{DeploymentValues:vals},
206-
LicenseOptions:&coderdenttest.LicenseOptions{
207-
Features: license.Features{
208-
codersdk.FeatureMultipleOrganizations:1,
209-
},
210-
},
211-
})
212-
returnadminClient,coderdenttest.CreateOrganization(t,adminClient, coderdenttest.CreateOrganizationOptions{})
188+
funcTestScoped(t*testing.T) {
189+
orgId:=uuid.New()
190+
191+
ctx:=testutil.Context(t,testutil.WaitShort)
192+
193+
// Set up a config manager and a field which will have runtime configs.
194+
mgr:=runtimeconfig.NewStoreManager(dbmem.New())
195+
field:=runtimeconfig.MustNew[*serpent.HostPort]("addr","localhost:3000")
196+
197+
// No runtime value set at this point, Coalesce will return startup value.
198+
_,err:=field.Resolve(ctx,mgr)
199+
require.ErrorIs(t,err,runtimeconfig.EntryNotFound)
200+
val,err:=field.Coalesce(ctx,mgr)
201+
require.NoError(t,err)
202+
require.Equal(t,field.StartupValue().String(),val.String())
203+
204+
// Set a runtime value which is NOT org-scoped.
205+
host,port:="localhost","1234"
206+
require.NoError(t,field.SetRuntimeValue(ctx,mgr,&serpent.HostPort{Host:host,Port:port}))
207+
val,err=field.Resolve(ctx,mgr)
208+
require.NoError(t,err)
209+
require.Equal(t,host,val.Host)
210+
require.Equal(t,port,val.Port)
211+
212+
orgMgr:=mgr.Scoped(orgId.String())
213+
// Using the org scope, nothing will be returned.
214+
_,err=field.Resolve(ctx,orgMgr)
215+
require.ErrorIs(t,err,runtimeconfig.EntryNotFound)
216+
217+
// Now set an org-scoped value.
218+
host,port="localhost","4321"
219+
require.NoError(t,field.SetRuntimeValue(ctx,orgMgr,&serpent.HostPort{Host:host,Port:port}))
220+
val,err=field.Resolve(ctx,orgMgr)
221+
require.NoError(t,err)
222+
require.Equal(t,host,val.Host)
223+
require.Equal(t,port,val.Port)
224+
225+
// Ensure the two runtime configs are NOT equal to each other nor the startup value.
226+
global,err:=field.Resolve(ctx,mgr)
227+
require.NoError(t,err)
228+
org,err:=field.Resolve(ctx,orgMgr)
229+
require.NoError(t,err)
230+
231+
require.NotEqual(t,global.String(),org.String())
232+
require.NotEqual(t,field.StartupValue().String(),global.String())
233+
require.NotEqual(t,field.StartupValue().String(),org.String())
213234
}

‎coderd/runtimeconfig/manager.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package runtimeconfig
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"errors"
7+
"fmt"
8+
9+
"golang.org/x/xerrors"
10+
11+
"github.com/coder/coder/v2/coderd/database"
12+
)
13+
14+
typeNoopManagerstruct{}
15+
16+
funcNewNoopManager()*NoopManager {
17+
return&NoopManager{}
18+
}
19+
20+
func (nNoopManager)GetRuntimeSetting(context.Context,string) (string,error) {
21+
return"",EntryNotFound
22+
}
23+
24+
func (nNoopManager)UpsertRuntimeSetting(context.Context,string,string)error {
25+
returnEntryNotFound
26+
}
27+
28+
func (nNoopManager)DeleteRuntimeSetting(context.Context,string)error {
29+
returnEntryNotFound
30+
}
31+
32+
func (nNoopManager)Scoped(string)Manager {
33+
returnn
34+
}
35+
36+
typeStoreManagerstruct {
37+
Store
38+
39+
nsstring
40+
}
41+
42+
funcNewStoreManager(storeStore)*StoreManager {
43+
ifstore==nil {
44+
panic("developer error: store must not be nil")
45+
}
46+
return&StoreManager{Store:store}
47+
}
48+
49+
func (mStoreManager)GetRuntimeSetting(ctx context.Context,keystring) (string,error) {
50+
key=m.namespacedKey(key)
51+
val,err:=m.Store.GetRuntimeConfig(ctx,key)
52+
iferr!=nil {
53+
iferrors.Is(err,sql.ErrNoRows) {
54+
return"",xerrors.Errorf("%q: %w",key,EntryNotFound)
55+
}
56+
return"",xerrors.Errorf("fetch %q: %w",key,err)
57+
}
58+
59+
returnval,nil
60+
}
61+
62+
func (mStoreManager)UpsertRuntimeSetting(ctx context.Context,key,valstring)error {
63+
err:=m.Store.UpsertRuntimeConfig(ctx, database.UpsertRuntimeConfigParams{Key:m.namespacedKey(key),Value:val})
64+
iferr!=nil {
65+
returnxerrors.Errorf("update %q: %w",err)
66+
}
67+
returnnil
68+
}
69+
70+
func (mStoreManager)DeleteRuntimeSetting(ctx context.Context,keystring)error {
71+
returnm.Store.DeleteRuntimeConfig(ctx,m.namespacedKey(key))
72+
}
73+
74+
func (mStoreManager)Scoped(nsstring)Manager {
75+
return&StoreManager{Store:m.Store,ns:ns}
76+
}
77+
78+
func (mStoreManager)namespacedKey(kstring)string {
79+
returnfmt.Sprintf("%s:%s",m.ns,k)
80+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp