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

feat: add API/SDK support for autostop extension#1778

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
johnstcn merged 11 commits intomainfromcj/autostop-extend-api
May 26, 2022
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
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
25 changes: 17 additions & 8 deletionscoderd/autobuild/executor/lifecycle_executor.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,18 @@ func (e *Executor) Run() {
func (e *Executor) runOnce(t time.Time) error {
currentTick := t.Truncate(time.Minute)
return e.db.InTx(func(db database.Store) error {
// TTL is set at the workspace level, and deadline at the workspace build level.
// When a workspace build is created, its deadline initially starts at zero.
// When provisionerd successfully completes a provision job, the deadline is
// set to now + TTL if the associated workspace has a TTL set. This deadline
// is what we compare against when performing autostop operations, rounded down
// to the minute.
//
// NOTE: Currently, if a workspace build is created with a given TTL and then
// the user either changes or unsets the TTL, the deadline for the workspace
// build will not have changed. So, autostop will still happen at the
// original TTL value from when the workspace build was created.
// Whether this is expected behavior from a user's perspective is not yet known.
eligibleWorkspaces, err := db.GetWorkspacesAutostart(e.ctx)
if err != nil {
return xerrors.Errorf("get eligible workspaces for autostart or autostop: %w", err)
Expand DownExpand Up@@ -88,18 +100,15 @@ func (e *Executor) runOnce(t time.Time) error {
switch priorHistory.Transition {
case database.WorkspaceTransitionStart:
validTransition = database.WorkspaceTransitionStop
if!ws.Ttl.Valid || ws.Ttl.Int64 == 0 {
e.log.Debug(e.ctx, "invalid or zero ws ttl, skipping",
ifpriorHistory.Deadline.IsZero() {
e.log.Debug(e.ctx, "latest workspace build has zero deadline, skipping",
slog.F("workspace_id", ws.ID),
slog.F("ttl",time.Duration(ws.Ttl.Int64)),
slog.F("workspace_build_id",priorHistory.ID),
)
continue
}
ttl := time.Duration(ws.Ttl.Int64)
// Measure TTL from the time the workspace finished building.
// Truncate to nearest minute for consistency with autostart
// behavior, and add one minute for padding.
nextTransition = priorHistory.UpdatedAt.Truncate(time.Minute).Add(ttl + time.Minute)
// Truncate to nearest minute for consistency with autostart behavior
nextTransition = priorHistory.Deadline.Truncate(time.Minute)
case database.WorkspaceTransitionStop:
validTransition = database.WorkspaceTransitionStart
sched, err := schedule.Weekly(ws.AutostartSchedule.String)
Expand Down
96 changes: 88 additions & 8 deletionscoderd/autobuild/executor/lifecycle_executor_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -190,14 +190,14 @@ func TestExecutorAutostopOK(t *testing.T) {
})
// Given: we have a user with a workspace
workspace = mustProvisionWorkspace(t, client)
ttl = *workspace.TTL
)
// Given: workspace is running
require.Equal(t, codersdk.WorkspaceTransitionStart, workspace.LatestBuild.Transition)
require.NotZero(t, workspace.LatestBuild.Deadline)

// When: the autobuild executor ticks *after* theTTL:
// When: the autobuild executor ticks *after* thedeadline:
go func() {
tickCh <-time.Now().UTC().Add(ttl +time.Minute)
tickCh <-workspace.LatestBuild.Deadline.Add(time.Minute)
close(tickCh)
}()

Expand All@@ -209,6 +209,55 @@ func TestExecutorAutostopOK(t *testing.T) {
require.Equal(t, codersdk.WorkspaceTransitionStop, ws.LatestBuild.Transition, "expected workspace not to be running")
}

func TestExecutorAutostopExtend(t *testing.T) {
t.Parallel()

var (
ctx = context.Background()
tickCh = make(chan time.Time)
client = coderdtest.New(t, &coderdtest.Options{
AutobuildTicker: tickCh,
IncludeProvisionerD: true,
})
// Given: we have a user with a workspace
workspace = mustProvisionWorkspace(t, client)
originalDeadline = workspace.LatestBuild.Deadline
)
// Given: workspace is running
require.Equal(t, codersdk.WorkspaceTransitionStart, workspace.LatestBuild.Transition)
require.NotZero(t, originalDeadline)

// Given: we extend the workspace deadline
err := client.PutExtendWorkspace(ctx, workspace.ID, codersdk.PutExtendWorkspaceRequest{
Deadline: originalDeadline.Add(30 * time.Minute),
})
require.NoError(t, err, "extend workspace deadline")

// When: the autobuild executor ticks *after* the original deadline:
go func() {
tickCh <- originalDeadline.Add(time.Minute)
}()

// Then: nothing should happen
<-time.After(5 * time.Second)
ws := mustWorkspace(t, client, workspace.ID)
require.Equal(t, workspace.LatestBuild.ID, ws.LatestBuild.ID, "expected no further workspace builds to occur")
require.Equal(t, codersdk.WorkspaceTransitionStart, ws.LatestBuild.Transition, "expected workspace to be running")

// When: the autobuild executor ticks after the *new* deadline:
go func() {
tickCh <- ws.LatestBuild.Deadline.Add(time.Minute)
close(tickCh)
}()

// Then: the workspace should be stopped
<-time.After(5 * time.Second)
ws = mustWorkspace(t, client, workspace.ID)
require.NotEqual(t, workspace.LatestBuild.ID, ws.LatestBuild.ID, "expected a workspace build to occur")
require.Equal(t, codersdk.ProvisionerJobSucceeded, ws.LatestBuild.Job.Status, "expected provisioner job to have succeeded")
require.Equal(t, codersdk.WorkspaceTransitionStop, ws.LatestBuild.Transition, "expected workspace not to be running")
}

func TestExecutorAutostopAlreadyStopped(t *testing.T) {
t.Parallel()

Expand All@@ -222,15 +271,14 @@ func TestExecutorAutostopAlreadyStopped(t *testing.T) {
workspace = mustProvisionWorkspace(t, client, func(cwr *codersdk.CreateWorkspaceRequest) {
cwr.AutostartSchedule = nil
})
ttl = *workspace.TTL
)

// Given: workspace is stopped
workspace = mustTransitionWorkspace(t, client, workspace.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)

// When: the autobuild executor ticks past the TTL
go func() {
tickCh <-time.Now().UTC().Add(ttl +time.Minute)
tickCh <-workspace.LatestBuild.Deadline.Add(time.Minute)
close(tickCh)
}()

Expand DownExpand Up@@ -264,7 +312,7 @@ func TestExecutorAutostopNotEnabled(t *testing.T) {

// When: the autobuild executor ticks past the TTL
go func() {
tickCh <-time.Now().UTC().Add(time.Minute)
tickCh <-workspace.LatestBuild.Deadline.Add(time.Minute)
close(tickCh)
}()

Expand DownExpand Up@@ -352,7 +400,7 @@ func TestExecutorWorkspaceAutostartTooEarly(t *testing.T) {
require.Equal(t, codersdk.WorkspaceTransitionStart, ws.LatestBuild.Transition, "expected workspace to be running")
}

funcTestExecutorWorkspaceTTLTooEarly(t *testing.T) {
funcTestExecutorWorkspaceAutostopBeforeDeadline(t *testing.T) {
t.Parallel()

var (
Expand All@@ -367,7 +415,7 @@ func TestExecutorWorkspaceTTLTooEarly(t *testing.T) {

// When: the autobuild executor ticks before the TTL
go func() {
tickCh <-time.Now().UTC()
tickCh <-workspace.LatestBuild.Deadline.Add(-1 * time.Minute)
close(tickCh)
}()

Expand All@@ -378,6 +426,38 @@ func TestExecutorWorkspaceTTLTooEarly(t *testing.T) {
require.Equal(t, codersdk.WorkspaceTransitionStart, ws.LatestBuild.Transition, "expected workspace to be running")
}

func TestExecutorWorkspaceAutostopNoWaitChangedMyMind(t *testing.T) {
t.Parallel()

var (
ctx = context.Background()
tickCh = make(chan time.Time)
client = coderdtest.New(t, &coderdtest.Options{
AutobuildTicker: tickCh,
IncludeProvisionerD: true,
})
// Given: we have a user with a workspace
workspace = mustProvisionWorkspace(t, client)
)

// Given: the user changes their mind and decides their workspace should not auto-stop
err := client.UpdateWorkspaceTTL(ctx, workspace.ID, codersdk.UpdateWorkspaceTTLRequest{TTL: nil})
require.NoError(t, err)

// When: the autobuild executor ticks after the deadline
go func() {
tickCh <- workspace.LatestBuild.Deadline.Add(time.Minute)
close(tickCh)
}()

// Then: the workspace should still stop - sorry!
<-time.After(5 * time.Second)
ws := mustWorkspace(t, client, workspace.ID)
require.NotEqual(t, workspace.LatestBuild.ID, ws.LatestBuild.ID, "expected a workspace build to occur")
require.Equal(t, codersdk.ProvisionerJobSucceeded, ws.LatestBuild.Job.Status, "expected provisioner job to have succeeded")
require.Equal(t, codersdk.WorkspaceTransitionStop, ws.LatestBuild.Transition, "expected workspace not to be running")
}

func TestExecutorAutostartMultipleOK(t *testing.T) {
if os.Getenv("DB") == "" {
t.Skip(`This test only really works when using a "real" database, similar to a HA setup`)
Expand Down
1 change: 1 addition & 0 deletionscoderd/coderd.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -315,6 +315,7 @@ func New(options *Options) *API {
r.Put("/", api.putWorkspaceTTL)
})
r.Get("/watch", api.watchWorkspace)
r.Put("/extend", api.putExtendWorkspace)
})
})
r.Route("/workspacebuilds/{workspacebuild}", func(r chi.Router) {
Expand Down
2 changes: 2 additions & 0 deletionscoderd/database/databasefake/databasefake.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -1485,6 +1485,7 @@ func (q *fakeQuerier) InsertWorkspaceBuild(_ context.Context, arg database.Inser
InitiatorID: arg.InitiatorID,
JobID: arg.JobID,
ProvisionerState: arg.ProvisionerState,
Deadline: arg.Deadline,
}
q.workspaceBuilds = append(q.workspaceBuilds, workspaceBuild)
return workspaceBuild, nil
Expand DownExpand Up@@ -1693,6 +1694,7 @@ func (q *fakeQuerier) UpdateWorkspaceBuildByID(_ context.Context, arg database.U
}
workspaceBuild.UpdatedAt = arg.UpdatedAt
workspaceBuild.ProvisionerState = arg.ProvisionerState
workspaceBuild.Deadline = arg.Deadline
q.workspaceBuilds[index] = workspaceBuild
return nil
}
Expand Down
3 changes: 2 additions & 1 deletioncoderd/database/dump.sql
View file
Open in desktop

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

View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
ALTER TABLE ONLY workspace_builds DROP COLUMN deadline;
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
ALTER TABLE ONLY workspace_builds ADD COLUMN deadline TIMESTAMPTZ NOT NULL DEFAULT TIMESTAMPTZ '0001-01-01 00:00:00+00:00';
1 change: 1 addition & 0 deletionscoderd/database/models.go
View file
Open in desktop

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

Loading

[8]ページ先頭

©2009-2025 Movatter.jp