Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
5509824
feat: add template max_ttl
deansheather Feb 8, 2023
4c6a501
chore: split enterprise code for max_ttl into interface
deansheather Feb 15, 2023
7892c5a
Merge branch 'main' into dean/schedule-max-ttl
deansheather Feb 20, 2023
9e37cc7
feat: add API for setting template max_ttl
deansheather Feb 20, 2023
ad64806
working API and dashboard
deansheather Feb 20, 2023
96fd840
working CLI
deansheather Feb 20, 2023
bc39570
Merge branch 'main' into dean/schedule-max-ttl
deansheather Feb 23, 2023
4e1d948
fixup! Merge branch 'main' into dean/schedule-max-ttl
deansheather Feb 23, 2023
69035a6
feat: block disabling auto off if template has max ttl
deansheather Feb 23, 2023
ce7ec39
Merge branch 'main' into dean/schedule-max-ttl
deansheather Mar 1, 2023
89ccfc8
fixup! Merge branch 'main' into dean/schedule-max-ttl
deansheather Mar 1, 2023
feb7b3c
feat: apply template max TTL to workspace TTL on update
deansheather Mar 1, 2023
d54d798
chore: fix tests and differences between sql and memory db
deansheather Mar 1, 2023
6caeb00
Few refactorings for ttl fields
BrunoQuaresma Mar 1, 2023
49a2ec7
Merge branch 'main' into dean/schedule-max-ttl
deansheather Mar 1, 2023
e28e32e
fixup! Merge branch 'main' into dean/schedule-max-ttl
deansheather Mar 1, 2023
651149b
fixup! Merge branch 'main' into dean/schedule-max-ttl
deansheather Mar 1, 2023
25f7d2c
chore: add test for activitybump max_ttl
deansheather Mar 2, 2023
e3d8557
chore: add tests for updating max_ttl on template
deansheather Mar 2, 2023
f62ba67
Merge branch 'main' into dean/schedule-max-ttl
deansheather Mar 2, 2023
2b029d8
fixup! Merge branch 'main' into dean/schedule-max-ttl
deansheather Mar 2, 2023
e60919e
chore: fix security.yaml not having protoc
deansheather Mar 2, 2023
ef3bebf
Merge branch 'main' into dean/schedule-max-ttl
deansheather Mar 2, 2023
351b708
fixup! Merge branch 'main' into dean/schedule-max-ttl
deansheather Mar 2, 2023
847bc4c
chore: move schedule code to new package
deansheather Mar 2, 2023
0611794
fixup! chore: move schedule code to new package
deansheather Mar 2, 2023
e44f589
chore: add alpha label to max_ttl
deansheather Mar 6, 2023
6bb65ac
Merge branch 'main' into dean/schedule-max-ttl
deansheather Mar 6, 2023
a972c57
Merge branch 'main' into dean/schedule-max-ttl
BrunoQuaresma Mar 6, 2023
3ce2bc3
Fix test
BrunoQuaresma Mar 6, 2023
4beff52
fixup! Fix test
deansheather Mar 6, 2023
5e97e96
Fix storybook
BrunoQuaresma Mar 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: add API for setting template max_ttl
  • Loading branch information
deansheather committed Feb 20, 2023
commit 9e37cc73cf7238e73002b17f7c142af075e61937
4 changes: 4 additions & 0 deletions coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions coderd/database/dbauthz/querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,13 @@ func (q *querier) UpdateTemplateMetaByID(ctx context.Context, arg database.Updat
return updateWithReturn(q.log, q.auth, fetch, q.db.UpdateTemplateMetaByID)(ctx, arg)
}

func (q *querier) UpdateTemplateScheduleByID(ctx context.Context, arg database.UpdateTemplateScheduleByIDParams) (database.Template, error) {
fetch := func(ctx context.Context, arg database.UpdateTemplateScheduleByIDParams) (database.Template, error) {
return q.db.GetTemplateByID(ctx, arg.ID)
}
return updateWithReturn(q.log, q.auth, fetch, q.db.UpdateTemplateScheduleByID)(ctx, arg)
}

func (q *querier) UpdateTemplateVersionByID(ctx context.Context, arg database.UpdateTemplateVersionByIDParams) error {
template, err := q.db.GetTemplateByID(ctx, arg.TemplateID.UUID)
if err != nil {
Expand Down
25 changes: 23 additions & 2 deletions coderd/database/dbfake/databasefake.go
Original file line number Diff line number Diff line change
Expand Up @@ -1644,8 +1644,8 @@ func (q *fakeQuerier) UpdateTemplateMetaByID(_ context.Context, arg database.Upd
return database.Template{}, err
}

q.mutex.RLock()
defer q.mutex.RUnlock()
q.mutex.Lock()
defer q.mutex.Unlock()

for idx, tpl := range q.templates {
if tpl.ID != arg.ID {
Expand All @@ -1656,7 +1656,28 @@ func (q *fakeQuerier) UpdateTemplateMetaByID(_ context.Context, arg database.Upd
tpl.DisplayName = arg.DisplayName
tpl.Description = arg.Description
tpl.Icon = arg.Icon
q.templates[idx] = tpl
return tpl, nil
}

return database.Template{}, sql.ErrNoRows
}

func (q *fakeQuerier) UpdateTemplateScheduleByID(_ context.Context, arg database.UpdateTemplateScheduleByIDParams) (database.Template, error) {
if err := validateDatabaseType(arg); err != nil {
return database.Template{}, err
}

q.mutex.Lock()
defer q.mutex.Unlock()

for idx, tpl := range q.templates {
if tpl.ID != arg.ID {
continue
}
tpl.UpdatedAt = database.Now()
tpl.DefaultTTL = arg.DefaultTTL
tpl.MaxTTL = arg.MaxTTL
q.templates[idx] = tpl
return tpl, nil
}
Expand Down
1 change: 1 addition & 0 deletions coderd/database/querier.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 54 additions & 7 deletions coderd/database/queries.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion coderd/database/queries/provisionerjobs.sql
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ WHERE
-- Ensure the caller has the correct provisioner.
AND nested.provisioner = ANY(@types :: provisioner_type [ ])
-- Ensure the caller satisfies all job tags.
AND nested.tags <@ @tags :: jsonb
AND nested.tags <@ @tags :: jsonb
ORDER BY
nested.created_at
FOR UPDATE
Expand Down
21 changes: 16 additions & 5 deletions coderd/database/queries/templates.sql
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,22 @@ UPDATE
SET
updated_at = $2,
description = $3,
default_ttl = $4,
name = $5,
icon = $6,
display_name = $7,
allow_user_cancel_workspace_jobs = $8
name = $4,
icon = $5,
display_name = $6,
allow_user_cancel_workspace_jobs = $7
WHERE
id = $1
RETURNING
*;

-- name: UpdateTemplateScheduleByID :one
UPDATE
templates
SET
updated_at = $2,
default_ttl = $3,
max_ttl = $4
WHERE
id = $1
RETURNING
Expand Down
18 changes: 16 additions & 2 deletions coderd/provisionerdserver/templatescheduleoptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type TemplateScheduleOptions struct {
// scheduling options set by the template/site admin.
type TemplateScheduleStore interface {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering why is TemplateScheduleStore an interface. I see only one implementation here: agplTemplateScheduleStore. Is it for testing purposes?

Ok, I see now, there is one for enterprise too.

WDYT about renaming stores to be more descriptive instead of agpl... and enterprise.... If we decide to introduce another pricing plan, then we will need to rename them accordingly :) I would move these implementations to a seperate package, maybe schedule or scheduling?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We currently have a schedule package but it's under autobuild/schedule, whereas this package doesn't pertain to autostart at all, only autostop...

Maybe I should move the existing schedule package to coderd/schedule and then copy this code to it instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is done, I've moved coderd/autobuild/schedule to coderd/schedule and added the code from this file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not entirely what I meant. Currently, the agpl... and enterprise... logic are scattered. The idea is place them in the same package, but rename accordingly. These are just different strategies, so it's perfectly fine to keep them together.

For instance, agplTemplateScheduleStore -> userControlledTemplateScheduleStore, enterpriseTemplateScheduleStore -> maxTTLTemplateScheduleStore.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot place them in the same package as AGPL code can't be mixed with enterprise code, and there needs to be an implementation outside of AGPL otherwise AGPL-compiled binaries wouldn't be able to compile properly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can change the names of them but I feel the names you propose are too specific especially if we add other features to them in the future.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, that's unfortunate. Let's leave as is.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same pattern we use for other enterprise features that hide behind interfaces btw

GetTemplateScheduleOptions(ctx context.Context, db database.Store, templateID uuid.UUID) (TemplateScheduleOptions, error)
SetTemplateScheduleOptions(ctx context.Context, db database.Store, template database.Template, opts TemplateScheduleOptions) (database.Template, error)
}

type agplTemplateScheduleStore struct{}
Expand All @@ -33,7 +34,7 @@ func NewAGPLTemplateScheduleStore() TemplateScheduleStore {
return &agplTemplateScheduleStore{}
}

func (s *agplTemplateScheduleStore) GetTemplateScheduleOptions(ctx context.Context, db database.Store, templateID uuid.UUID) (TemplateScheduleOptions, error) {
func (*agplTemplateScheduleStore) GetTemplateScheduleOptions(ctx context.Context, db database.Store, templateID uuid.UUID) (TemplateScheduleOptions, error) {
tpl, err := db.GetTemplateByID(ctx, templateID)
if err != nil {
return TemplateScheduleOptions{}, err
Expand All @@ -42,6 +43,19 @@ func (s *agplTemplateScheduleStore) GetTemplateScheduleOptions(ctx context.Conte
return TemplateScheduleOptions{
UserSchedulingEnabled: true,
DefaultTTL: time.Duration(tpl.DefaultTTL),
MaxTTL: 0,
// Disregard the value in the database, since MaxTTL is an enterprise
// feature.
MaxTTL: 0,
}, nil
}

func (*agplTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.Context, db database.Store, tpl database.Template, opts TemplateScheduleOptions) (database.Template, error) {
return db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{
ID: tpl.ID,
UpdatedAt: database.Now(),
DefaultTTL: int64(opts.DefaultTTL),
// Don't allow changing it, but keep the value in the DB (to avoid
// clearing settings if the license has an issue).
MaxTTL: tpl.MaxTTL,
})
}
18 changes: 15 additions & 3 deletions coderd/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/httpapi"
"github.com/coder/coder/coderd/httpmw"
"github.com/coder/coder/coderd/provisionerdserver"
"github.com/coder/coder/coderd/rbac"
"github.com/coder/coder/coderd/telemetry"
"github.com/coder/coder/codersdk"
Expand Down Expand Up @@ -479,7 +480,6 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
displayName := req.DisplayName
desc := req.Description
icon := req.Icon
defaultTTL := time.Duration(req.DefaultTTLMillis) * time.Millisecond
allowUserCancelWorkspaceJobs := req.AllowUserCancelWorkspaceJobs

if name == "" {
Expand All @@ -497,11 +497,23 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
DisplayName: displayName,
Description: desc,
Icon: icon,
DefaultTTL: int64(defaultTTL),
AllowUserCancelWorkspaceJobs: allowUserCancelWorkspaceJobs,
})
if err != nil {
return err
return xerrors.Errorf("update template metadata: %w", err)
}

defaultTTL := time.Duration(req.DefaultTTLMillis) * time.Millisecond
maxTTL := time.Duration(req.MaxTTLMillis) * time.Millisecond
if defaultTTL != time.Duration(template.DefaultTTL) || maxTTL != time.Duration(template.MaxTTL) {
updated, err = (*api.TemplateScheduleStore.Load()).SetTemplateScheduleOptions(ctx, tx, updated, provisionerdserver.TemplateScheduleOptions{
UserSchedulingEnabled: true,
DefaultTTL: defaultTTL,
MaxTTL: maxTTL,
})
if err != nil {
return xerrors.Errorf("set template schedule options: %w", err)
}
}

return nil
Expand Down
24 changes: 24 additions & 0 deletions coderd/templates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,30 @@ func TestPatchTemplateMeta(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, updated.Icon, "")
})

t.Run("MaxTTLEnterpriseOnly", func(t *testing.T) {
t.Parallel()

client := coderdtest.New(t, nil)
user := coderdtest.CreateFirstUser(t, client)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
require.EqualValues(t, 0, template.MaxTTLMillis)
req := codersdk.UpdateTemplateMeta{
MaxTTLMillis: time.Hour.Milliseconds(),
}

ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()

updated, err := client.UpdateTemplateMeta(ctx, template.ID, req)
require.NoError(t, err)
require.EqualValues(t, 0, updated.MaxTTLMillis)

template, err = client.Template(ctx, template.ID)
require.NoError(t, err)
require.EqualValues(t, 0, template.MaxTTLMillis)
})
}

func TestDeleteTemplate(t *testing.T) {
Expand Down
23 changes: 15 additions & 8 deletions codersdk/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ type Template struct {
Description string `json:"description"`
Icon string `json:"icon"`
DefaultTTLMillis int64 `json:"default_ttl_ms"`
CreatedByID uuid.UUID `json:"created_by_id" format:"uuid"`
CreatedByName string `json:"created_by_name"`
// MaxTTLMillis is an enterprise feature. It's value is only used if your
// license is entitled to use the advanced template scheduling feature.
MaxTTLMillis int64 `json:"max_ttl_ms"`
CreatedByID uuid.UUID `json:"created_by_id" format:"uuid"`
CreatedByName string `json:"created_by_name"`

AllowUserCancelWorkspaceJobs bool `json:"allow_user_cancel_workspace_jobs"`
}
Expand Down Expand Up @@ -75,12 +78,16 @@ type UpdateTemplateACL struct {
}

type UpdateTemplateMeta struct {
Name string `json:"name,omitempty" validate:"omitempty,template_name"`
DisplayName string `json:"display_name,omitempty" validate:"omitempty,template_display_name"`
Description string `json:"description,omitempty"`
Icon string `json:"icon,omitempty"`
DefaultTTLMillis int64 `json:"default_ttl_ms,omitempty"`
AllowUserCancelWorkspaceJobs bool `json:"allow_user_cancel_workspace_jobs,omitempty"`
Name string `json:"name,omitempty" validate:"omitempty,template_name"`
DisplayName string `json:"display_name,omitempty" validate:"omitempty,template_display_name"`
Description string `json:"description,omitempty"`
Icon string `json:"icon,omitempty"`
DefaultTTLMillis int64 `json:"default_ttl_ms,omitempty"`
// MaxTTLMillis can only be set if your license includes the advanced
// template scheduling feature. If you attempt to set this value while
// unlicensed, it will be ignored.
MaxTTLMillis int64 `json:"max_ttl_ms,omitempty"`
AllowUserCancelWorkspaceJobs bool `json:"allow_user_cancel_workspace_jobs,omitempty"`
}

type TemplateExample struct {
Expand Down
Loading