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

Commite33941b

Browse files
authored
feat: allow disabling autostart and custom autostop for template (#6933)
API only, frontend in upcoming PR.
1 parent083fc89 commite33941b

File tree

65 files changed

+1432
-485
lines changed

Some content is hidden

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

65 files changed

+1432
-485
lines changed

‎cli/server.go‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"strconv"
3131
"strings"
3232
"sync"
33+
"sync/atomic"
3334
"time"
3435

3536
"github.com/coreos/go-oidc/v3/oidc"
@@ -72,6 +73,7 @@ import (
7273
"github.com/coder/coder/coderd/httpapi"
7374
"github.com/coder/coder/coderd/httpmw"
7475
"github.com/coder/coder/coderd/prometheusmetrics"
76+
"github.com/coder/coder/coderd/schedule"
7577
"github.com/coder/coder/coderd/telemetry"
7678
"github.com/coder/coder/coderd/tracing"
7779
"github.com/coder/coder/coderd/updatecheck"
@@ -632,6 +634,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
632634
LoginRateLimit:loginRateLimit,
633635
FilesRateLimit:filesRateLimit,
634636
HTTPClient:httpClient,
637+
TemplateScheduleStore:&atomic.Pointer[schedule.TemplateScheduleStore]{},
635638
SSHConfig: codersdk.SSHConfigResponse{
636639
HostnamePrefix:cfg.SSHConfig.DeploymentName.String(),
637640
SSHConfigOptions:configSSHOptions,
@@ -1019,7 +1022,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
10191022

10201023
autobuildPoller:=time.NewTicker(cfg.AutobuildPollInterval.Value())
10211024
deferautobuildPoller.Stop()
1022-
autobuildExecutor:=executor.New(ctx,options.Database,logger,autobuildPoller.C)
1025+
autobuildExecutor:=executor.New(ctx,options.Database,coderAPI.TemplateScheduleStore,logger,autobuildPoller.C)
10231026
autobuildExecutor.Run()
10241027

10251028
// Currently there is no way to ask the server to shut

‎cli/templateedit.go‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
2121
defaultTTL time.Duration
2222
maxTTL time.Duration
2323
allowUserCancelWorkspaceJobsbool
24+
allowUserAutostartbool
25+
allowUserAutostopbool
2426
)
2527
client:=new(codersdk.Client)
2628

@@ -32,17 +34,17 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
3234
),
3335
Short:"Edit the metadata of a template by name.",
3436
Handler:func(inv*clibase.Invocation)error {
35-
ifmaxTTL!=0 {
37+
ifmaxTTL!=0||!allowUserAutostart||!allowUserAutostop{
3638
entitlements,err:=client.Entitlements(inv.Context())
3739
varsdkErr*codersdk.Error
3840
ifxerrors.As(err,&sdkErr)&&sdkErr.StatusCode()==http.StatusNotFound {
39-
returnxerrors.Errorf("your deployment appears to be an AGPL deployment, so you cannot set --max-ttl")
41+
returnxerrors.Errorf("your deployment appears to be an AGPL deployment, so you cannot set --max-ttl, --allow-user-autostart=false or --allow-user-autostop=false")
4042
}elseiferr!=nil {
4143
returnxerrors.Errorf("get entitlements: %w",err)
4244
}
4345

4446
if!entitlements.Features[codersdk.FeatureAdvancedTemplateScheduling].Enabled {
45-
returnxerrors.Errorf("your license is not entitled to use advanced template scheduling, so you cannot set --max-ttl")
47+
returnxerrors.Errorf("your license is not entitled to use advanced template scheduling, so you cannot set --max-ttl, --allow-user-autostart=false or --allow-user-autostop=false")
4648
}
4749
}
4850

@@ -64,6 +66,8 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
6466
DefaultTTLMillis:defaultTTL.Milliseconds(),
6567
MaxTTLMillis:maxTTL.Milliseconds(),
6668
AllowUserCancelWorkspaceJobs:allowUserCancelWorkspaceJobs,
69+
AllowUserAutostart:allowUserAutostart,
70+
AllowUserAutostop:allowUserAutostop,
6771
}
6872

6973
_,err=client.UpdateTemplateMeta(inv.Context(),template.ID,req)
@@ -112,6 +116,18 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
112116
Default:"true",
113117
Value:clibase.BoolOf(&allowUserCancelWorkspaceJobs),
114118
},
119+
{
120+
Flag:"allow-user-autostart",
121+
Description:"Allow users to configure autostart for workspaces on this template. This can only be disabled in enterprise.",
122+
Default:"true",
123+
Value:clibase.BoolOf(&allowUserAutostart),
124+
},
125+
{
126+
Flag:"allow-user-autostop",
127+
Description:"Allow users to customize the autostop TTL for workspaces on this template. This can only be disabled in enterprise.",
128+
Default:"true",
129+
Value:clibase.BoolOf(&allowUserAutostop),
130+
},
115131
cliui.SkipPromptOption(),
116132
}
117133

‎cli/templateedit_test.go‎

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,147 @@ func TestTemplateEdit(t *testing.T) {
428428

429429
require.EqualValues(t,1,atomic.LoadInt64(&updateTemplateCalled))
430430

431+
// Assert that the template metadata did not change. We verify the
432+
// correct request gets sent to the server already.
433+
updated,err:=client.Template(context.Background(),template.ID)
434+
require.NoError(t,err)
435+
assert.Equal(t,template.Name,updated.Name)
436+
assert.Equal(t,template.Description,updated.Description)
437+
assert.Equal(t,template.Icon,updated.Icon)
438+
assert.Equal(t,template.DisplayName,updated.DisplayName)
439+
assert.Equal(t,template.DefaultTTLMillis,updated.DefaultTTLMillis)
440+
assert.Equal(t,template.MaxTTLMillis,updated.MaxTTLMillis)
441+
})
442+
})
443+
t.Run("AllowUserScheduling",func(t*testing.T) {
444+
t.Parallel()
445+
t.Run("BlockedAGPL",func(t*testing.T) {
446+
t.Parallel()
447+
client:=coderdtest.New(t,&coderdtest.Options{IncludeProvisionerDaemon:true})
448+
user:=coderdtest.CreateFirstUser(t,client)
449+
version:=coderdtest.CreateTemplateVersion(t,client,user.OrganizationID,nil)
450+
_=coderdtest.AwaitTemplateVersionJob(t,client,version.ID)
451+
template:=coderdtest.CreateTemplate(t,client,user.OrganizationID,version.ID,func(ctr*codersdk.CreateTemplateRequest) {
452+
ctr.DefaultTTLMillis=nil
453+
ctr.MaxTTLMillis=nil
454+
})
455+
456+
// Test the cli command with --allow-user-autostart.
457+
cmdArgs:= []string{
458+
"templates",
459+
"edit",
460+
template.Name,
461+
"--allow-user-autostart=false",
462+
}
463+
inv,root:=clitest.New(t,cmdArgs...)
464+
clitest.SetupConfig(t,client,root)
465+
466+
ctx:=testutil.Context(t,testutil.WaitLong)
467+
err:=inv.WithContext(ctx).Run()
468+
require.Error(t,err)
469+
require.ErrorContains(t,err,"appears to be an AGPL deployment")
470+
471+
// Test the cli command with --allow-user-autostop.
472+
cmdArgs= []string{
473+
"templates",
474+
"edit",
475+
template.Name,
476+
"--allow-user-autostop=false",
477+
}
478+
inv,root=clitest.New(t,cmdArgs...)
479+
clitest.SetupConfig(t,client,root)
480+
481+
ctx=testutil.Context(t,testutil.WaitLong)
482+
err=inv.WithContext(ctx).Run()
483+
require.Error(t,err)
484+
require.ErrorContains(t,err,"appears to be an AGPL deployment")
485+
486+
// Assert that the template metadata did not change.
487+
updated,err:=client.Template(context.Background(),template.ID)
488+
require.NoError(t,err)
489+
assert.Equal(t,template.Name,updated.Name)
490+
assert.Equal(t,template.Description,updated.Description)
491+
assert.Equal(t,template.Icon,updated.Icon)
492+
assert.Equal(t,template.DisplayName,updated.DisplayName)
493+
assert.Equal(t,template.DefaultTTLMillis,updated.DefaultTTLMillis)
494+
assert.Equal(t,template.MaxTTLMillis,updated.MaxTTLMillis)
495+
assert.Equal(t,template.AllowUserAutostart,updated.AllowUserAutostart)
496+
assert.Equal(t,template.AllowUserAutostop,updated.AllowUserAutostop)
497+
})
498+
499+
t.Run("BlockedNotEntitled",func(t*testing.T) {
500+
t.Parallel()
501+
client:=coderdtest.New(t,&coderdtest.Options{IncludeProvisionerDaemon:true})
502+
user:=coderdtest.CreateFirstUser(t,client)
503+
version:=coderdtest.CreateTemplateVersion(t,client,user.OrganizationID,nil)
504+
_=coderdtest.AwaitTemplateVersionJob(t,client,version.ID)
505+
template:=coderdtest.CreateTemplate(t,client,user.OrganizationID,version.ID)
506+
507+
// Make a proxy server that will return a valid entitlements
508+
// response, but without advanced scheduling entitlement.
509+
proxy:=httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter,r*http.Request) {
510+
ifr.URL.Path=="/api/v2/entitlements" {
511+
res:= codersdk.Entitlements{
512+
Features:map[codersdk.FeatureName]codersdk.Feature{},
513+
Warnings: []string{},
514+
Errors: []string{},
515+
HasLicense:true,
516+
Trial:true,
517+
RequireTelemetry:false,
518+
}
519+
for_,feature:=rangecodersdk.FeatureNames {
520+
res.Features[feature]= codersdk.Feature{
521+
Entitlement:codersdk.EntitlementNotEntitled,
522+
Enabled:false,
523+
Limit:nil,
524+
Actual:nil,
525+
}
526+
}
527+
httpapi.Write(r.Context(),w,http.StatusOK,res)
528+
return
529+
}
530+
531+
// Otherwise, proxy the request to the real API server.
532+
httputil.NewSingleHostReverseProxy(client.URL).ServeHTTP(w,r)
533+
}))
534+
deferproxy.Close()
535+
536+
// Create a new client that uses the proxy server.
537+
proxyURL,err:=url.Parse(proxy.URL)
538+
require.NoError(t,err)
539+
proxyClient:=codersdk.New(proxyURL)
540+
proxyClient.SetSessionToken(client.SessionToken())
541+
542+
// Test the cli command with --allow-user-autostart.
543+
cmdArgs:= []string{
544+
"templates",
545+
"edit",
546+
template.Name,
547+
"--allow-user-autostart=false",
548+
}
549+
inv,root:=clitest.New(t,cmdArgs...)
550+
clitest.SetupConfig(t,proxyClient,root)
551+
552+
ctx:=testutil.Context(t,testutil.WaitLong)
553+
err=inv.WithContext(ctx).Run()
554+
require.Error(t,err)
555+
require.ErrorContains(t,err,"license is not entitled")
556+
557+
// Test the cli command with --allow-user-autostop.
558+
cmdArgs= []string{
559+
"templates",
560+
"edit",
561+
template.Name,
562+
"--allow-user-autostop=false",
563+
}
564+
inv,root=clitest.New(t,cmdArgs...)
565+
clitest.SetupConfig(t,proxyClient,root)
566+
567+
ctx=testutil.Context(t,testutil.WaitLong)
568+
err=inv.WithContext(ctx).Run()
569+
require.Error(t,err)
570+
require.ErrorContains(t,err,"license is not entitled")
571+
431572
// Assert that the template metadata did not change.
432573
updated,err:=client.Template(context.Background(),template.ID)
433574
require.NoError(t,err)
@@ -437,6 +578,98 @@ func TestTemplateEdit(t *testing.T) {
437578
assert.Equal(t,template.DisplayName,updated.DisplayName)
438579
assert.Equal(t,template.DefaultTTLMillis,updated.DefaultTTLMillis)
439580
assert.Equal(t,template.MaxTTLMillis,updated.MaxTTLMillis)
581+
assert.Equal(t,template.AllowUserAutostart,updated.AllowUserAutostart)
582+
assert.Equal(t,template.AllowUserAutostop,updated.AllowUserAutostop)
583+
})
584+
t.Run("Entitled",func(t*testing.T) {
585+
t.Parallel()
586+
client:=coderdtest.New(t,&coderdtest.Options{IncludeProvisionerDaemon:true})
587+
user:=coderdtest.CreateFirstUser(t,client)
588+
version:=coderdtest.CreateTemplateVersion(t,client,user.OrganizationID,nil)
589+
_=coderdtest.AwaitTemplateVersionJob(t,client,version.ID)
590+
template:=coderdtest.CreateTemplate(t,client,user.OrganizationID,version.ID)
591+
592+
// Make a proxy server that will return a valid entitlements
593+
// response, including a valid advanced scheduling entitlement.
594+
varupdateTemplateCalledint64
595+
proxy:=httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter,r*http.Request) {
596+
ifr.URL.Path=="/api/v2/entitlements" {
597+
res:= codersdk.Entitlements{
598+
Features:map[codersdk.FeatureName]codersdk.Feature{},
599+
Warnings: []string{},
600+
Errors: []string{},
601+
HasLicense:true,
602+
Trial:true,
603+
RequireTelemetry:false,
604+
}
605+
for_,feature:=rangecodersdk.FeatureNames {
606+
varoneint64=1
607+
res.Features[feature]= codersdk.Feature{
608+
Entitlement:codersdk.EntitlementNotEntitled,
609+
Enabled:true,
610+
Limit:&one,
611+
Actual:&one,
612+
}
613+
}
614+
httpapi.Write(r.Context(),w,http.StatusOK,res)
615+
return
616+
}
617+
ifstrings.HasPrefix(r.URL.Path,"/api/v2/templates/") {
618+
body,err:=io.ReadAll(r.Body)
619+
require.NoError(t,err)
620+
_=r.Body.Close()
621+
622+
varreq codersdk.UpdateTemplateMeta
623+
err=json.Unmarshal(body,&req)
624+
require.NoError(t,err)
625+
assert.False(t,req.AllowUserAutostart)
626+
assert.False(t,req.AllowUserAutostop)
627+
628+
r.Body=io.NopCloser(bytes.NewReader(body))
629+
atomic.AddInt64(&updateTemplateCalled,1)
630+
// We still want to call the real route.
631+
}
632+
633+
// Otherwise, proxy the request to the real API server.
634+
httputil.NewSingleHostReverseProxy(client.URL).ServeHTTP(w,r)
635+
}))
636+
deferproxy.Close()
637+
638+
// Create a new client that uses the proxy server.
639+
proxyURL,err:=url.Parse(proxy.URL)
640+
require.NoError(t,err)
641+
proxyClient:=codersdk.New(proxyURL)
642+
proxyClient.SetSessionToken(client.SessionToken())
643+
644+
// Test the cli command.
645+
cmdArgs:= []string{
646+
"templates",
647+
"edit",
648+
template.Name,
649+
"--allow-user-autostart=false",
650+
"--allow-user-autostop=false",
651+
}
652+
inv,root:=clitest.New(t,cmdArgs...)
653+
clitest.SetupConfig(t,proxyClient,root)
654+
655+
ctx:=testutil.Context(t,testutil.WaitLong)
656+
err=inv.WithContext(ctx).Run()
657+
require.NoError(t,err)
658+
659+
require.EqualValues(t,1,atomic.LoadInt64(&updateTemplateCalled))
660+
661+
// Assert that the template metadata did not change. We verify the
662+
// correct request gets sent to the server already.
663+
updated,err:=client.Template(context.Background(),template.ID)
664+
require.NoError(t,err)
665+
assert.Equal(t,template.Name,updated.Name)
666+
assert.Equal(t,template.Description,updated.Description)
667+
assert.Equal(t,template.Icon,updated.Icon)
668+
assert.Equal(t,template.DisplayName,updated.DisplayName)
669+
assert.Equal(t,template.DefaultTTLMillis,updated.DefaultTTLMillis)
670+
assert.Equal(t,template.MaxTTLMillis,updated.MaxTTLMillis)
671+
assert.Equal(t,template.AllowUserAutostart,updated.AllowUserAutostart)
672+
assert.Equal(t,template.AllowUserAutostop,updated.AllowUserAutostop)
440673
})
441674
})
442675
}

‎cli/testdata/coder_templates_edit_--help.golden‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ Usage: coder templates edit [flags] <template>
33
Edit the metadata of a template by name.
44

55
Options
6+
--allow-user-autostart bool (default: true)
7+
Allow users to configure autostart for workspaces on this template.
8+
This can only be disabled in enterprise.
9+
10+
--allow-user-autostop bool (default: true)
11+
Allow users to customize the autostop TTL for workspaces on this
12+
template. This can only be disabled in enterprise.
13+
614
--allow-user-cancel-workspace-jobs bool (default: true)
715
Allow users to cancel in-progress workspace jobs.
816

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp