Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat: Add codersdk.NullTime, change workspace build deadline #3552

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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 25, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions cli/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ func workspaceListRowFromWorkspace(now time.Time, usersByID map[uuid.UUID]coders
if !ptr.NilOrZero(workspace.TTLMillis) {
dur := time.Duration(*workspace.TTLMillis) * time.Millisecond
autostopDisplay = durationDisplay(dur)
if !workspace.LatestBuild.Deadline.IsZero() && workspace.LatestBuild.Deadline.After(now) && status == "Running" {
remaining := time.Until(workspace.LatestBuild.Deadline)
if !workspace.LatestBuild.Deadline.IsZero() && workspace.LatestBuild.Deadline.Time.After(now) && status == "Running" {
remaining := time.Until(workspace.LatestBuild.Deadline.Time)
autostopDisplay = fmt.Sprintf("%s (%s)", autostopDisplay, relative(remaining))
}
}
Expand Down
4 changes: 2 additions & 2 deletions cli/schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,8 @@ func displaySchedule(workspace codersdk.Workspace, out io.Writer) error {
if workspace.LatestBuild.Transition != "start" {
schedNextStop = "-"
} else {
schedNextStop = workspace.LatestBuild.Deadline.In(loc).Format(timeFormat + " on " + dateFormat)
schedNextStop = fmt.Sprintf("%s (in %s)", schedNextStop, durationDisplay(time.Until(workspace.LatestBuild.Deadline)))
schedNextStop = workspace.LatestBuild.Deadline.Time.In(loc).Format(timeFormat + " on " + dateFormat)
schedNextStop = fmt.Sprintf("%s (in %s)", schedNextStop, durationDisplay(time.Until(workspace.LatestBuild.Deadline.Time)))
}
}

Expand Down
6 changes: 3 additions & 3 deletions cli/schedule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ func TestScheduleOverride(t *testing.T) {

// Assert test invariant: workspace build has a deadline set equal to now plus ttl
initDeadline := time.Now().Add(time.Duration(*workspace.TTLMillis) * time.Millisecond)
require.WithinDuration(t, initDeadline, workspace.LatestBuild.Deadline, time.Minute)
require.WithinDuration(t, initDeadline, workspace.LatestBuild.Deadline.Time, time.Minute)

cmd, root := clitest.New(t, cmdArgs...)
clitest.SetupConfig(t, client, root)
Expand All @@ -252,7 +252,7 @@ func TestScheduleOverride(t *testing.T) {
// Then: the deadline of the latest build is updated assuming the units are minutes
updated, err := client.Workspace(ctx, workspace.ID)
require.NoError(t, err)
require.WithinDuration(t, expectedDeadline, updated.LatestBuild.Deadline, time.Minute)
require.WithinDuration(t, expectedDeadline, updated.LatestBuild.Deadline.Time, time.Minute)
})

t.Run("InvalidDuration", func(t *testing.T) {
Expand All @@ -279,7 +279,7 @@ func TestScheduleOverride(t *testing.T) {

// Assert test invariant: workspace build has a deadline set equal to now plus ttl
initDeadline := time.Now().Add(time.Duration(*workspace.TTLMillis) * time.Millisecond)
require.WithinDuration(t, initDeadline, workspace.LatestBuild.Deadline, time.Minute)
require.WithinDuration(t, initDeadline, workspace.LatestBuild.Deadline.Time, time.Minute)

cmd, root := clitest.New(t, cmdArgs...)
clitest.SetupConfig(t, client, root)
Expand Down
8 changes: 5 additions & 3 deletions cli/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ import (
"github.com/coder/coder/peer/peerwg"
)

var workspacePollInterval = time.Minute
var autostopNotifyCountdown = []time.Duration{30 * time.Minute}
var (
workspacePollInterval = time.Minute
autostopNotifyCountdown = []time.Duration{30 * time.Minute}
)

func ssh() *cobra.Command {
var (
Expand Down Expand Up @@ -385,7 +387,7 @@ func notifyCondition(ctx context.Context, client *codersdk.Client, workspaceID u
return time.Time{}, nil
}

deadline = ws.LatestBuild.Deadline
deadline = ws.LatestBuild.Deadline.Time
callback = func() {
ttl := deadline.Sub(now)
var title, body string
Expand Down
18 changes: 9 additions & 9 deletions coderd/autobuild/executor/lifecycle_executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func TestExecutorAutostopOK(t *testing.T) {

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

Expand Down Expand Up @@ -229,15 +229,15 @@ func TestExecutorAutostopExtend(t *testing.T) {
require.NotZero(t, originalDeadline)

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

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

// Then: nothing should happen and the workspace should stay running
Expand Down Expand Up @@ -281,7 +281,7 @@ func TestExecutorAutostopAlreadyStopped(t *testing.T) {

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

Expand Down Expand Up @@ -323,7 +323,7 @@ func TestExecutorAutostopNotEnabled(t *testing.T) {

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

Expand Down Expand Up @@ -415,7 +415,7 @@ func TestExecutorWorkspaceAutostopBeforeDeadline(t *testing.T) {

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

Expand Down Expand Up @@ -447,11 +447,11 @@ func TestExecutorWorkspaceAutostopNoWaitChangedMyMind(t *testing.T) {

// Then: the deadline should still be the original value
updated := coderdtest.MustWorkspace(t, client, workspace.ID)
assert.WithinDuration(t, workspace.LatestBuild.Deadline, updated.LatestBuild.Deadline, time.Minute)
assert.WithinDuration(t, workspace.LatestBuild.Deadline.Time, updated.LatestBuild.Deadline.Time, time.Minute)

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

// Then: the workspace should stop
Expand All @@ -478,7 +478,7 @@ func TestExecutorWorkspaceAutostopNoWaitChangedMyMind(t *testing.T) {

// When: the relentless onward march of time continues
go func() {
tickCh <- workspace.LatestBuild.Deadline.Add(newTTL + time.Minute)
tickCh <- workspace.LatestBuild.Deadline.Time.Add(newTTL + time.Minute)
close(tickCh)
}()

Expand Down
5 changes: 3 additions & 2 deletions coderd/workspacebuilds.go
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,8 @@ func convertWorkspaceBuild(
buildInitiator *database.User,
workspace database.Workspace,
workspaceBuild database.WorkspaceBuild,
job database.ProvisionerJob) codersdk.WorkspaceBuild {
job database.ProvisionerJob,
) codersdk.WorkspaceBuild {
//nolint:unconvert
if workspace.ID != workspaceBuild.WorkspaceID {
panic("workspace and build do not match")
Expand Down Expand Up @@ -671,7 +672,7 @@ func convertWorkspaceBuild(
InitiatorID: workspaceBuild.InitiatorID,
InitiatorUsername: initiatorName,
Job: convertProvisionerJob(job),
Deadline: workspaceBuild.Deadline,
Deadline: codersdk.NewNullTime(workspaceBuild.Deadline, !workspaceBuild.Deadline.IsZero()),
Reason: codersdk.BuildReason(workspaceBuild.Reason),
}
}
Expand Down
6 changes: 3 additions & 3 deletions coderd/workspaces_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1169,7 +1169,7 @@ func TestWorkspaceExtend(t *testing.T) {

workspace, err := client.Workspace(ctx, workspace.ID)
require.NoError(t, err, "fetch provisioned workspace")
oldDeadline := workspace.LatestBuild.Deadline
oldDeadline := workspace.LatestBuild.Deadline.Time

// Updating the deadline should succeed
req := codersdk.PutExtendWorkspaceRequest{
Expand All @@ -1181,7 +1181,7 @@ func TestWorkspaceExtend(t *testing.T) {
// Ensure deadline set correctly
updated, err := client.Workspace(ctx, workspace.ID)
require.NoError(t, err, "failed to fetch updated workspace")
require.WithinDuration(t, newDeadline, updated.LatestBuild.Deadline, time.Minute)
require.WithinDuration(t, newDeadline, updated.LatestBuild.Deadline.Time, time.Minute)

// Zero time should fail
err = client.PutExtendWorkspace(ctx, workspace.ID, codersdk.PutExtendWorkspaceRequest{
Expand Down Expand Up @@ -1220,7 +1220,7 @@ func TestWorkspaceExtend(t *testing.T) {
// Ensure deadline still set correctly
updated, err = client.Workspace(ctx, workspace.ID)
require.NoError(t, err, "failed to fetch updated workspace")
require.WithinDuration(t, oldDeadline.Add(-time.Hour), updated.LatestBuild.Deadline, time.Minute)
require.WithinDuration(t, oldDeadline.Add(-time.Hour), updated.LatestBuild.Deadline.Time, time.Minute)
}

func TestWorkspaceWatcher(t *testing.T) {
Expand Down
59 changes: 59 additions & 0 deletions codersdk/time.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package codersdk

import (
"bytes"
"database/sql"
"encoding/json"
"time"

"golang.org/x/xerrors"
)

var nullBytes = []byte("null")

// NullTime represents a nullable time.Time.
// @typescript-ignore NullTime
type NullTime struct {
sql.NullTime
}

// NewNullTime returns a new NullTime with the given time.Time.
func NewNullTime(t time.Time, valid bool) NullTime {
return NullTime{
NullTime: sql.NullTime{
Time: t,
Valid: valid,
},
}
}

// MarshalJSON implements json.Marshaler.
func (t NullTime) MarshalJSON() ([]byte, error) {
if !t.Valid {
return []byte("null"), nil
}
b, err := t.Time.MarshalJSON()
if err != nil {
return nil, xerrors.Errorf("codersdk.NullTime: json encode failed: %w", err)
}
return b, nil
}

// UnmarshalJSON implements json.Unmarshaler.
func (t *NullTime) UnmarshalJSON(data []byte) error {
t.Valid = false
if bytes.Equal(data, nullBytes) {
return nil
}
err := json.Unmarshal(data, &t.Time)
if err != nil {
return xerrors.Errorf("codersdk.NullTime: json decode failed: %w", err)
}
t.Valid = true
return nil
}

// IsZero return true if the time is null or zero.
func (t NullTime) IsZero() bool {
return !t.Valid || t.Time.IsZero()
}
Loading