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

Commit997493d

Browse files
authored
feat: add template setting to require active template version (#10277)
1 parent1ad998e commit997493d

File tree

47 files changed

+802
-70
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+802
-70
lines changed

‎cli/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -940,7 +940,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
940940
autobuildTicker:=time.NewTicker(vals.AutobuildPollInterval.Value())
941941
deferautobuildTicker.Stop()
942942
autobuildExecutor:=autobuild.NewExecutor(
943-
ctx,options.Database,options.Pubsub,coderAPI.TemplateScheduleStore,&coderAPI.Auditor,logger,autobuildTicker.C)
943+
ctx,options.Database,options.Pubsub,coderAPI.TemplateScheduleStore,&coderAPI.Auditor,coderAPI.AccessControlStore,logger,autobuildTicker.C)
944944
autobuildExecutor.Run()
945945

946946
hangDetectorTicker:=time.NewTicker(vals.JobHangDetectorInterval.Value())

‎cli/templates.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import (
66
"github.com/google/uuid"
77
"golang.org/x/xerrors"
88

9-
"github.com/coder/pretty"
10-
119
"github.com/coder/coder/v2/cli/clibase"
1210
"github.com/coder/coder/v2/cli/cliui"
1311
"github.com/coder/coder/v2/codersdk"

‎cli/templateversionarchive.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import (
88

99
"golang.org/x/xerrors"
1010

11-
"github.com/coder/pretty"
12-
1311
"github.com/coder/coder/v2/cli/clibase"
1412
"github.com/coder/coder/v2/cli/cliui"
1513
"github.com/coder/coder/v2/codersdk"

‎cli/templateversions.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import (
88
"github.com/google/uuid"
99
"golang.org/x/xerrors"
1010

11-
"github.com/coder/pretty"
12-
1311
"github.com/coder/coder/v2/cli/clibase"
1412
"github.com/coder/coder/v2/cli/cliui"
1513
"github.com/coder/coder/v2/codersdk"

‎coderd/apidoc/docs.go

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/apidoc/swagger.json

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/autobuild/lifecycle_executor.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type Executor struct {
3232
db database.Store
3333
ps pubsub.Pubsub
3434
templateScheduleStore*atomic.Pointer[schedule.TemplateScheduleStore]
35+
accessControlStore*atomic.Pointer[dbauthz.AccessControlStore]
3536
auditor*atomic.Pointer[audit.Auditor]
3637
log slog.Logger
3738
tick<-chan time.Time
@@ -46,7 +47,7 @@ type Stats struct {
4647
}
4748

4849
// New returns a new wsactions executor.
49-
funcNewExecutor(ctx context.Context,db database.Store,ps pubsub.Pubsub,tss*atomic.Pointer[schedule.TemplateScheduleStore],auditor*atomic.Pointer[audit.Auditor],log slog.Logger,tick<-chan time.Time)*Executor {
50+
funcNewExecutor(ctx context.Context,db database.Store,ps pubsub.Pubsub,tss*atomic.Pointer[schedule.TemplateScheduleStore],auditor*atomic.Pointer[audit.Auditor],acs*atomic.Pointer[dbauthz.AccessControlStore],log slog.Logger,tick<-chan time.Time)*Executor {
5051
le:=&Executor{
5152
//nolint:gocritic // Autostart has a limited set of permissions.
5253
ctx:dbauthz.AsAutostart(ctx),
@@ -56,6 +57,7 @@ func NewExecutor(ctx context.Context, db database.Store, ps pubsub.Pubsub, tss *
5657
tick:tick,
5758
log:log.Named("autobuild"),
5859
auditor:auditor,
60+
accessControlStore:acs,
5961
}
6062
returnle
6163
}
@@ -159,6 +161,12 @@ func (e *Executor) runOnce(t time.Time) Stats {
159161
returnnil
160162
}
161163

164+
template,err:=tx.GetTemplateByID(e.ctx,ws.TemplateID)
165+
iferr!=nil {
166+
log.Warn(e.ctx,"get template by id",slog.Error(err))
167+
}
168+
accessControl:= (*(e.accessControlStore.Load())).GetTemplateAccessControl(template)
169+
162170
latestJob,err:=tx.GetProvisionerJobByID(e.ctx,latestBuild.JobID)
163171
iferr!=nil {
164172
log.Warn(e.ctx,"get last provisioner job for workspace %q: %w",slog.Error(err))
@@ -179,7 +187,7 @@ func (e *Executor) runOnce(t time.Time) Stats {
179187
Reason(reason)
180188
log.Debug(e.ctx,"auto building workspace",slog.F("transition",nextTransition))
181189
ifnextTransition==database.WorkspaceTransitionStart&&
182-
ws.AutomaticUpdates==database.AutomaticUpdatesAlways {
190+
useActiveVersion(accessControl,ws) {
183191
log.Debug(e.ctx,"autostarting with active version")
184192
builder=builder.ActiveVersion()
185193
}
@@ -470,3 +478,7 @@ func auditBuild(ctx context.Context, log slog.Logger, auditor audit.Auditor, par
470478
AdditionalFields:raw,
471479
})
472480
}
481+
482+
funcuseActiveVersion(opts dbauthz.TemplateAccessControl,ws database.Workspace)bool {
483+
returnopts.RequireActiveVersion||ws.AutomaticUpdates==database.AutomaticUpdatesAlways
484+
}

‎coderd/autobuild/lifecycle_executor_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,56 @@ func TestExecutorAutostopTemplateDisabled(t *testing.T) {
783783
assert.Len(t,stats.Transitions,0)
784784
}
785785

786+
// Test that an AGPL AccessControlStore properly disables
787+
// functionality.
788+
funcTestExecutorRequireActiveVersion(t*testing.T) {
789+
t.Parallel()
790+
791+
var (
792+
sched=mustSchedule(t,"CRON_TZ=UTC 0 * * * *")
793+
ticker=make(chan time.Time)
794+
statCh=make(chan autobuild.Stats)
795+
796+
ownerClient=coderdtest.New(t,&coderdtest.Options{
797+
AutobuildTicker:ticker,
798+
IncludeProvisionerDaemon:true,
799+
AutobuildStats:statCh,
800+
TemplateScheduleStore:schedule.NewAGPLTemplateScheduleStore(),
801+
})
802+
)
803+
owner:=coderdtest.CreateFirstUser(t,ownerClient)
804+
805+
// Create an active and inactive template version. We'll
806+
// build a regular member's workspace using a non-active
807+
// template version and assert that the field is not abided
808+
// since there is no enterprise license.
809+
activeVersion:=coderdtest.CreateTemplateVersion(t,ownerClient,owner.OrganizationID,nil)
810+
template:=coderdtest.CreateTemplate(t,ownerClient,owner.OrganizationID,activeVersion.ID,func(ctr*codersdk.CreateTemplateRequest) {
811+
ctr.RequireActiveVersion=true
812+
ctr.VersionID=activeVersion.ID
813+
})
814+
inactiveVersion:=coderdtest.CreateTemplateVersion(t,ownerClient,owner.OrganizationID,nil,func(ctvr*codersdk.CreateTemplateVersionRequest) {
815+
ctvr.TemplateID=template.ID
816+
})
817+
coderdtest.AwaitTemplateVersionJobCompleted(t,ownerClient,activeVersion.ID)
818+
memberClient,_:=coderdtest.CreateAnotherUser(t,ownerClient,owner.OrganizationID)
819+
ws:=coderdtest.CreateWorkspace(t,memberClient,owner.OrganizationID,uuid.Nil,func(cwr*codersdk.CreateWorkspaceRequest) {
820+
cwr.TemplateVersionID=inactiveVersion.ID
821+
cwr.AutostartSchedule=ptr.Ref(sched.String())
822+
})
823+
_=coderdtest.AwaitWorkspaceBuildJobCompleted(t,ownerClient,ws.LatestBuild.ID)
824+
ws=coderdtest.MustTransitionWorkspace(t,memberClient,ws.ID,database.WorkspaceTransitionStart,database.WorkspaceTransitionStop,func(req*codersdk.CreateWorkspaceBuildRequest) {
825+
req.TemplateVersionID=inactiveVersion.ID
826+
})
827+
require.Equal(t,inactiveVersion.ID,ws.LatestBuild.TemplateVersionID)
828+
ticker<-sched.Next(ws.LatestBuild.CreatedAt)
829+
stats:=<-statCh
830+
require.Len(t,stats.Transitions,1)
831+
832+
ws=coderdtest.MustWorkspace(t,memberClient,ws.ID)
833+
require.Equal(t,inactiveVersion.ID,ws.LatestBuild.TemplateVersionID)
834+
}
835+
786836
// TestExecutorFailedWorkspace test AGPL functionality which mainly
787837
// ensures that autostop actions as a result of a failed workspace
788838
// build do not trigger.

‎coderd/coderd.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ type Options struct {
131131
SetUserSiteRolesfunc(ctx context.Context,logger slog.Logger,tx database.Store,userID uuid.UUID,roles []string)error
132132
TemplateScheduleStore*atomic.Pointer[schedule.TemplateScheduleStore]
133133
UserQuietHoursScheduleStore*atomic.Pointer[schedule.UserQuietHoursScheduleStore]
134+
AccessControlStore*atomic.Pointer[dbauthz.AccessControlStore]
134135
// AppSecurityKey is the crypto key used to sign and encrypt tokens related to
135136
// workspace applications. It consists of both a signing and encryption key.
136137
AppSecurityKey workspaceapps.SecurityKey
@@ -208,11 +209,20 @@ func New(options *Options) *API {
208209
ifoptions.Authorizer==nil {
209210
options.Authorizer=rbac.NewCachingAuthorizer(options.PrometheusRegistry)
210211
}
212+
213+
ifoptions.AccessControlStore==nil {
214+
options.AccessControlStore=&atomic.Pointer[dbauthz.AccessControlStore]{}
215+
vartacs dbauthz.AccessControlStore= dbauthz.AGPLTemplateAccessControlStore{}
216+
options.AccessControlStore.Store(&tacs)
217+
}
218+
211219
options.Database=dbauthz.New(
212220
options.Database,
213221
options.Authorizer,
214222
options.Logger.Named("authz_querier"),
223+
options.AccessControlStore,
215224
)
225+
216226
experiments:=ReadExperiments(
217227
options.Logger,options.DeploymentValues.Experiments.Value(),
218228
)
@@ -369,6 +379,7 @@ func New(options *Options) *API {
369379
Auditor: atomic.Pointer[audit.Auditor]{},
370380
TemplateScheduleStore:options.TemplateScheduleStore,
371381
UserQuietHoursScheduleStore:options.UserQuietHoursScheduleStore,
382+
AccessControlStore:options.AccessControlStore,
372383
Experiments:experiments,
373384
healthCheckGroup:&singleflight.Group[string,*healthcheck.Report]{},
374385
Acquirer:provisionerdserver.NewAcquirer(
@@ -1008,6 +1019,9 @@ type API struct {
10081019
UserQuietHoursScheduleStore*atomic.Pointer[schedule.UserQuietHoursScheduleStore]
10091020
// DERPMapper mutates the DERPMap to include workspace proxies.
10101021
DERPMapper atomic.Pointer[func(derpMap*tailcfg.DERPMap)*tailcfg.DERPMap]
1022+
// AccessControlStore is a pointer to an atomic pointer since it is
1023+
// passed to dbauthz.
1024+
AccessControlStore*atomic.Pointer[dbauthz.AccessControlStore]
10111025

10121026
HTTPAuth*HTTPAuthorizer
10131027

‎coderd/coderdtest/coderdtest.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,6 @@ func NewOptions(t testing.TB, options *Options) (func(http.Handler), context.Can
218218

219219
ifoptions.Database==nil {
220220
options.Database,options.Pubsub=dbtestutil.NewDB(t)
221-
options.Database=dbauthz.New(options.Database,options.Authorizer,options.Logger.Leveled(slog.LevelDebug))
222221
}
223222

224223
// Some routes expect a deployment ID, so just make sure one exists.
@@ -260,6 +259,10 @@ func NewOptions(t testing.TB, options *Options) (func(http.Handler), context.Can
260259
t.Cleanup(closeBatcher)
261260
}
262261

262+
accessControlStore:=&atomic.Pointer[dbauthz.AccessControlStore]{}
263+
varacs dbauthz.AccessControlStore= dbauthz.AGPLTemplateAccessControlStore{}
264+
accessControlStore.Store(&acs)
265+
263266
vartemplateScheduleStore atomic.Pointer[schedule.TemplateScheduleStore]
264267
ifoptions.TemplateScheduleStore==nil {
265268
options.TemplateScheduleStore=schedule.NewAGPLTemplateScheduleStore()
@@ -279,6 +282,7 @@ func NewOptions(t testing.TB, options *Options) (func(http.Handler), context.Can
279282
options.Pubsub,
280283
&templateScheduleStore,
281284
&auditor,
285+
accessControlStore,
282286
*options.Logger,
283287
options.AutobuildTicker,
284288
).WithStatsChannel(options.AutobuildStats)
@@ -416,6 +420,7 @@ func NewOptions(t testing.TB, options *Options) (func(http.Handler), context.Can
416420
Authorizer:options.Authorizer,
417421
Telemetry:telemetry.NewNoop(),
418422
TemplateScheduleStore:&templateScheduleStore,
423+
AccessControlStore:accessControlStore,
419424
TLSCertificates:options.TLSCertificates,
420425
TrialGenerator:options.TrialGenerator,
421426
TailnetCoordinator:options.Coordinator,
@@ -915,7 +920,7 @@ func CreateWorkspace(t testing.TB, client *codersdk.Client, organization uuid.UU
915920
}
916921

917922
// TransitionWorkspace is a convenience method for transitioning a workspace from one state to another.
918-
funcMustTransitionWorkspace(t testing.TB,client*codersdk.Client,workspaceID uuid.UUID,from,to database.WorkspaceTransition) codersdk.Workspace {
923+
funcMustTransitionWorkspace(t testing.TB,client*codersdk.Client,workspaceID uuid.UUID,from,to database.WorkspaceTransition,muts...func(req*codersdk.CreateWorkspaceBuildRequest)) codersdk.Workspace {
919924
t.Helper()
920925
ctx:=context.Background()
921926
workspace,err:=client.Workspace(ctx,workspaceID)
@@ -925,10 +930,16 @@ func MustTransitionWorkspace(t testing.TB, client *codersdk.Client, workspaceID
925930
template,err:=client.Template(ctx,workspace.TemplateID)
926931
require.NoError(t,err,"fetch workspace template")
927932

928-
build,err:=client.CreateWorkspaceBuild(ctx,workspace.ID, codersdk.CreateWorkspaceBuildRequest{
933+
req:= codersdk.CreateWorkspaceBuildRequest{
929934
TemplateVersionID:template.ActiveVersionID,
930935
Transition:codersdk.WorkspaceTransition(to),
931-
})
936+
}
937+
938+
for_,mut:=rangemuts {
939+
mut(&req)
940+
}
941+
942+
build,err:=client.CreateWorkspaceBuild(ctx,workspace.ID,req)
932943
require.NoError(t,err,"unexpected error transitioning workspace to %s",to)
933944

934945
_=AwaitWorkspaceBuildJobCompleted(t,client,build.ID)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package dbauthz
2+
3+
import (
4+
"context"
5+
6+
"github.com/google/uuid"
7+
8+
"github.com/coder/coder/v2/coderd/database"
9+
)
10+
11+
// AccessControlStore fetches access control-related configuration
12+
// that is used when determining whether an actor is authorized
13+
// to interact with an RBAC object.
14+
typeAccessControlStoreinterface {
15+
GetTemplateAccessControl(t database.Template)TemplateAccessControl
16+
SetTemplateAccessControl(ctx context.Context,store database.Store,id uuid.UUID,optsTemplateAccessControl)error
17+
}
18+
19+
typeTemplateAccessControlstruct {
20+
RequireActiveVersionbool
21+
}
22+
23+
// AGPLTemplateAccessControlStore always returns the defaults for access control
24+
// settings.
25+
typeAGPLTemplateAccessControlStorestruct{}
26+
27+
var_AccessControlStore=AGPLTemplateAccessControlStore{}
28+
29+
func (AGPLTemplateAccessControlStore)GetTemplateAccessControl(database.Template)TemplateAccessControl {
30+
returnTemplateAccessControl{
31+
RequireActiveVersion:false,
32+
}
33+
}
34+
35+
func (AGPLTemplateAccessControlStore)SetTemplateAccessControl(context.Context, database.Store, uuid.UUID,TemplateAccessControl)error {
36+
returnnil
37+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp