mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
feat: add template setting to require active template version (#10277)
This commit is contained in:
@@ -32,6 +32,7 @@ type Executor struct {
|
||||
db database.Store
|
||||
ps pubsub.Pubsub
|
||||
templateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore]
|
||||
accessControlStore *atomic.Pointer[dbauthz.AccessControlStore]
|
||||
auditor *atomic.Pointer[audit.Auditor]
|
||||
log slog.Logger
|
||||
tick <-chan time.Time
|
||||
@@ -46,7 +47,7 @@ type Stats struct {
|
||||
}
|
||||
|
||||
// New returns a new wsactions executor.
|
||||
func NewExecutor(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 {
|
||||
func NewExecutor(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 {
|
||||
le := &Executor{
|
||||
//nolint:gocritic // Autostart has a limited set of permissions.
|
||||
ctx: dbauthz.AsAutostart(ctx),
|
||||
@@ -56,6 +57,7 @@ func NewExecutor(ctx context.Context, db database.Store, ps pubsub.Pubsub, tss *
|
||||
tick: tick,
|
||||
log: log.Named("autobuild"),
|
||||
auditor: auditor,
|
||||
accessControlStore: acs,
|
||||
}
|
||||
return le
|
||||
}
|
||||
@@ -159,6 +161,12 @@ func (e *Executor) runOnce(t time.Time) Stats {
|
||||
return nil
|
||||
}
|
||||
|
||||
template, err := tx.GetTemplateByID(e.ctx, ws.TemplateID)
|
||||
if err != nil {
|
||||
log.Warn(e.ctx, "get template by id", slog.Error(err))
|
||||
}
|
||||
accessControl := (*(e.accessControlStore.Load())).GetTemplateAccessControl(template)
|
||||
|
||||
latestJob, err := tx.GetProvisionerJobByID(e.ctx, latestBuild.JobID)
|
||||
if err != nil {
|
||||
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 {
|
||||
Reason(reason)
|
||||
log.Debug(e.ctx, "auto building workspace", slog.F("transition", nextTransition))
|
||||
if nextTransition == database.WorkspaceTransitionStart &&
|
||||
ws.AutomaticUpdates == database.AutomaticUpdatesAlways {
|
||||
useActiveVersion(accessControl, ws) {
|
||||
log.Debug(e.ctx, "autostarting with active version")
|
||||
builder = builder.ActiveVersion()
|
||||
}
|
||||
@@ -470,3 +478,7 @@ func auditBuild(ctx context.Context, log slog.Logger, auditor audit.Auditor, par
|
||||
AdditionalFields: raw,
|
||||
})
|
||||
}
|
||||
|
||||
func useActiveVersion(opts dbauthz.TemplateAccessControl, ws database.Workspace) bool {
|
||||
return opts.RequireActiveVersion || ws.AutomaticUpdates == database.AutomaticUpdatesAlways
|
||||
}
|
||||
|
||||
@@ -783,6 +783,56 @@ func TestExecutorAutostopTemplateDisabled(t *testing.T) {
|
||||
assert.Len(t, stats.Transitions, 0)
|
||||
}
|
||||
|
||||
// Test that an AGPL AccessControlStore properly disables
|
||||
// functionality.
|
||||
func TestExecutorRequireActiveVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
sched = mustSchedule(t, "CRON_TZ=UTC 0 * * * *")
|
||||
ticker = make(chan time.Time)
|
||||
statCh = make(chan autobuild.Stats)
|
||||
|
||||
ownerClient = coderdtest.New(t, &coderdtest.Options{
|
||||
AutobuildTicker: ticker,
|
||||
IncludeProvisionerDaemon: true,
|
||||
AutobuildStats: statCh,
|
||||
TemplateScheduleStore: schedule.NewAGPLTemplateScheduleStore(),
|
||||
})
|
||||
)
|
||||
owner := coderdtest.CreateFirstUser(t, ownerClient)
|
||||
|
||||
// Create an active and inactive template version. We'll
|
||||
// build a regular member's workspace using a non-active
|
||||
// template version and assert that the field is not abided
|
||||
// since there is no enterprise license.
|
||||
activeVersion := coderdtest.CreateTemplateVersion(t, ownerClient, owner.OrganizationID, nil)
|
||||
template := coderdtest.CreateTemplate(t, ownerClient, owner.OrganizationID, activeVersion.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.RequireActiveVersion = true
|
||||
ctr.VersionID = activeVersion.ID
|
||||
})
|
||||
inactiveVersion := coderdtest.CreateTemplateVersion(t, ownerClient, owner.OrganizationID, nil, func(ctvr *codersdk.CreateTemplateVersionRequest) {
|
||||
ctvr.TemplateID = template.ID
|
||||
})
|
||||
coderdtest.AwaitTemplateVersionJobCompleted(t, ownerClient, activeVersion.ID)
|
||||
memberClient, _ := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID)
|
||||
ws := coderdtest.CreateWorkspace(t, memberClient, owner.OrganizationID, uuid.Nil, func(cwr *codersdk.CreateWorkspaceRequest) {
|
||||
cwr.TemplateVersionID = inactiveVersion.ID
|
||||
cwr.AutostartSchedule = ptr.Ref(sched.String())
|
||||
})
|
||||
_ = coderdtest.AwaitWorkspaceBuildJobCompleted(t, ownerClient, ws.LatestBuild.ID)
|
||||
ws = coderdtest.MustTransitionWorkspace(t, memberClient, ws.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop, func(req *codersdk.CreateWorkspaceBuildRequest) {
|
||||
req.TemplateVersionID = inactiveVersion.ID
|
||||
})
|
||||
require.Equal(t, inactiveVersion.ID, ws.LatestBuild.TemplateVersionID)
|
||||
ticker <- sched.Next(ws.LatestBuild.CreatedAt)
|
||||
stats := <-statCh
|
||||
require.Len(t, stats.Transitions, 1)
|
||||
|
||||
ws = coderdtest.MustWorkspace(t, memberClient, ws.ID)
|
||||
require.Equal(t, inactiveVersion.ID, ws.LatestBuild.TemplateVersionID)
|
||||
}
|
||||
|
||||
// TestExecutorFailedWorkspace test AGPL functionality which mainly
|
||||
// ensures that autostop actions as a result of a failed workspace
|
||||
// build do not trigger.
|
||||
|
||||
Reference in New Issue
Block a user