Skip to content

Commit 52230fa

Browse files
authored
feat: make default autobuild poll intervals configurable (#1618)
* feat: make default poll intervals for autobuild and ssh ttl polling configurable
1 parent 992b583 commit 52230fa

File tree

6 files changed

+104
-20
lines changed

6 files changed

+104
-20
lines changed

cli/cliflag/cliflag.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"os"
1616
"strconv"
1717
"strings"
18+
"time"
1819

1920
"github.com/spf13/pflag"
2021
)
@@ -83,6 +84,23 @@ func BoolVarP(flagset *pflag.FlagSet, ptr *bool, name string, shorthand string,
8384
flagset.BoolVarP(ptr, name, shorthand, valb, fmtUsage(usage, env))
8485
}
8586

87+
// DurationVarP sets a time.Duration flag on the given flag set.
88+
func DurationVarP(flagset *pflag.FlagSet, ptr *time.Duration, name string, shorthand string, env string, def time.Duration, usage string) {
89+
val, ok := os.LookupEnv(env)
90+
if !ok || val == "" {
91+
flagset.DurationVarP(ptr, name, shorthand, def, fmtUsage(usage, env))
92+
return
93+
}
94+
95+
valb, err := time.ParseDuration(val)
96+
if err != nil {
97+
flagset.DurationVarP(ptr, name, shorthand, def, fmtUsage(usage, env))
98+
return
99+
}
100+
101+
flagset.DurationVarP(ptr, name, shorthand, valb, fmtUsage(usage, env))
102+
}
103+
86104
func fmtUsage(u string, env string) string {
87105
if env == "" {
88106
return fmt.Sprintf("%s.", u)

cli/cliflag/cliflag_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"strconv"
66
"testing"
7+
"time"
78

89
"github.com/spf13/pflag"
910
"github.com/stretchr/testify/require"
@@ -183,6 +184,45 @@ func TestCliflag(t *testing.T) {
183184
require.NoError(t, err)
184185
require.Equal(t, def, got)
185186
})
187+
188+
t.Run("DurationDefault", func(t *testing.T) {
189+
var ptr time.Duration
190+
flagset, name, shorthand, env, usage := randomFlag()
191+
def, _ := cryptorand.Duration()
192+
193+
cliflag.DurationVarP(flagset, &ptr, name, shorthand, env, def, usage)
194+
got, err := flagset.GetDuration(name)
195+
require.NoError(t, err)
196+
require.Equal(t, def, got)
197+
require.Contains(t, flagset.FlagUsages(), usage)
198+
require.Contains(t, flagset.FlagUsages(), fmt.Sprintf(" - consumes $%s", env))
199+
})
200+
201+
t.Run("DurationEnvVar", func(t *testing.T) {
202+
var ptr time.Duration
203+
flagset, name, shorthand, env, usage := randomFlag()
204+
envValue, _ := cryptorand.Duration()
205+
t.Setenv(env, envValue.String())
206+
def, _ := cryptorand.Duration()
207+
208+
cliflag.DurationVarP(flagset, &ptr, name, shorthand, env, def, usage)
209+
got, err := flagset.GetDuration(name)
210+
require.NoError(t, err)
211+
require.Equal(t, envValue, got)
212+
})
213+
214+
t.Run("DurationFailParse", func(t *testing.T) {
215+
var ptr time.Duration
216+
flagset, name, shorthand, env, usage := randomFlag()
217+
envValue, _ := cryptorand.String(10)
218+
t.Setenv(env, envValue)
219+
def, _ := cryptorand.Duration()
220+
221+
cliflag.DurationVarP(flagset, &ptr, name, shorthand, env, def, usage)
222+
got, err := flagset.GetDuration(name)
223+
require.NoError(t, err)
224+
require.Equal(t, def, got)
225+
})
186226
}
187227

188228
func randomFlag() (*pflag.FlagSet, string, string, string, string) {

cli/server.go

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,18 @@ import (
6060
// nolint:gocyclo
6161
func server() *cobra.Command {
6262
var (
63-
accessURL string
64-
address string
65-
promEnabled bool
66-
promAddress string
67-
pprofEnabled bool
68-
pprofAddress string
69-
cacheDir string
70-
dev bool
71-
devUserEmail string
72-
devUserPassword string
73-
postgresURL string
63+
accessURL string
64+
address string
65+
autobuildPollInterval time.Duration
66+
promEnabled bool
67+
promAddress string
68+
pprofEnabled bool
69+
pprofAddress string
70+
cacheDir string
71+
dev bool
72+
devUserEmail string
73+
devUserPassword string
74+
postgresURL string
7475
// provisionerDaemonCount is a uint8 to ensure a number > 0.
7576
provisionerDaemonCount uint8
7677
oauth2GithubClientID string
@@ -361,10 +362,10 @@ func server() *cobra.Command {
361362
return xerrors.Errorf("notify systemd: %w", err)
362363
}
363364

364-
lifecyclePoller := time.NewTicker(time.Minute)
365-
defer lifecyclePoller.Stop()
366-
lifecycleExecutor := executor.New(cmd.Context(), options.Database, logger, lifecyclePoller.C)
367-
lifecycleExecutor.Run()
365+
autobuildPoller := time.NewTicker(autobuildPollInterval)
366+
defer autobuildPoller.Stop()
367+
autobuildExecutor := executor.New(cmd.Context(), options.Database, logger, autobuildPoller.C)
368+
autobuildExecutor.Run()
368369

369370
// Because the graceful shutdown includes cleaning up workspaces in dev mode, we're
370371
// going to make it harder to accidentally skip the graceful shutdown by hitting ctrl+c
@@ -454,6 +455,7 @@ func server() *cobra.Command {
454455
},
455456
}
456457

458+
cliflag.DurationVarP(root.Flags(), &autobuildPollInterval, "autobuild-poll-interval", "", "CODER_AUTOBUILD_POLL_INTERVAL", time.Minute, "Specifies the interval at which to poll for and execute automated workspace build operations.")
457459
cliflag.StringVarP(root.Flags(), &accessURL, "access-url", "", "CODER_ACCESS_URL", "", "Specifies the external URL to access Coder.")
458460
cliflag.StringVarP(root.Flags(), &address, "address", "a", "CODER_ADDRESS", "127.0.0.1:3000", "The address to serve the API and dashboard.")
459461
cliflag.BoolVarP(root.Flags(), &promEnabled, "prometheus-enable", "", "CODER_PROMETHEUS_ENABLE", false, "Enable serving prometheus metrics on the addressdefined by --prometheus-address.")

cli/ssh.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@ import (
2525
"github.com/coder/coder/cryptorand"
2626
)
2727

28-
var autostopPollInterval = 30 * time.Second
28+
var workspacePollInterval = time.Minute
2929
var autostopNotifyCountdown = []time.Duration{30 * time.Minute}
3030

3131
func ssh() *cobra.Command {
3232
var (
33-
stdio bool
34-
shuffle bool
33+
stdio bool
34+
shuffle bool
35+
wsPollInterval time.Duration
3536
)
3637
cmd := &cobra.Command{
3738
Annotations: workspaceCommand,
@@ -155,6 +156,7 @@ func ssh() *cobra.Command {
155156
}
156157
cliflag.BoolVarP(cmd.Flags(), &stdio, "stdio", "", "CODER_SSH_STDIO", false, "Specifies whether to emit SSH output over stdin/stdout.")
157158
cliflag.BoolVarP(cmd.Flags(), &shuffle, "shuffle", "", "CODER_SSH_SHUFFLE", false, "Specifies whether to choose a random workspace")
159+
cliflag.DurationVarP(cmd.Flags(), &wsPollInterval, "workspace-poll-interval", "", "CODER_WORKSPACE_POLL_INTERVAL", workspacePollInterval, "Specifies how often to poll for workspace automated shutdown.")
158160
_ = cmd.Flags().MarkHidden("shuffle")
159161

160162
return cmd
@@ -252,14 +254,14 @@ func getWorkspaceAndAgent(cmd *cobra.Command, client *codersdk.Client, orgID uui
252254
func tryPollWorkspaceAutostop(ctx context.Context, client *codersdk.Client, workspace codersdk.Workspace) (stop func()) {
253255
lock := flock.New(filepath.Join(os.TempDir(), "coder-autostop-notify-"+workspace.ID.String()))
254256
condition := notifyCondition(ctx, client, workspace.ID, lock)
255-
return notify.Notify(condition, autostopPollInterval, autostopNotifyCountdown...)
257+
return notify.Notify(condition, workspacePollInterval, autostopNotifyCountdown...)
256258
}
257259

258260
// Notify the user if the workspace is due to shutdown.
259261
func notifyCondition(ctx context.Context, client *codersdk.Client, workspaceID uuid.UUID, lock *flock.Flock) notify.Condition {
260262
return func(now time.Time) (deadline time.Time, callback func()) {
261263
// Keep trying to regain the lock.
262-
locked, err := lock.TryLockContext(ctx, autostopPollInterval)
264+
locked, err := lock.TryLockContext(ctx, workspacePollInterval)
263265
if err != nil || !locked {
264266
return time.Time{}, nil
265267
}

cryptorand/numbers.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cryptorand
33
import (
44
"crypto/rand"
55
"encoding/binary"
6+
"time"
67

78
"golang.org/x/xerrors"
89
)
@@ -193,3 +194,13 @@ func Bool() (bool, error) {
193194
// True if the least significant bit is 1
194195
return i&1 == 1, nil
195196
}
197+
198+
// Duration returns a random time.Duration value
199+
func Duration() (time.Duration, error) {
200+
i, err := Int63()
201+
if err != nil {
202+
return time.Duration(0), err
203+
}
204+
205+
return time.Duration(i), nil
206+
}

cryptorand/numbers_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,14 @@ func TestBool(t *testing.T) {
175175
require.True(t, percentage > 48, "expected more than 48 percent of values to be true")
176176
require.True(t, percentage < 52, "expected less than 52 percent of values to be true")
177177
}
178+
179+
func TestDuration(t *testing.T) {
180+
t.Parallel()
181+
182+
for i := 0; i < 20; i++ {
183+
v, err := cryptorand.Duration()
184+
require.NoError(t, err, "unexpected error from Duration")
185+
t.Logf("value: %v <- random?", v)
186+
require.True(t, v >= 0.0, "values must be positive")
187+
}
188+
}

0 commit comments

Comments
 (0)