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

Skip to content

fix: cli: prettify schedule when printing output #1440

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
May 16, 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
10 changes: 9 additions & 1 deletion cli/autostart.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,15 @@ func autostartShow() *cobra.Command {
return nil
}

_, _ = fmt.Fprintf(cmd.OutOrStdout(), "schedule: %s\nnext: %s\n", workspace.AutostartSchedule, validSchedule.Next(time.Now()))
next := validSchedule.Next(time.Now())
loc, _ := time.LoadLocation(validSchedule.Timezone())

_, _ = fmt.Fprintf(cmd.OutOrStdout(),
"schedule: %s\ntimezone: %s\nnext: %s\n",
validSchedule.Cron(),
validSchedule.Timezone(),
next.In(loc),
)

return nil
},
Expand Down
3 changes: 2 additions & 1 deletion cli/autostart_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ func TestAutostart(t *testing.T) {

err = cmd.Execute()
require.NoError(t, err, "unexpected error")
require.Contains(t, stdoutBuf.String(), "schedule: "+sched)
// CRON_TZ gets stripped
require.Contains(t, stdoutBuf.String(), "schedule: 30 17 * * 1-5")
})

t.Run("EnableDisableOK", func(t *testing.T) {
Expand Down
10 changes: 9 additions & 1 deletion cli/autostop.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,15 @@ func autostopShow() *cobra.Command {
return nil
}

_, _ = fmt.Fprintf(cmd.OutOrStdout(), "schedule: %s\nnext: %s\n", workspace.AutostopSchedule, validSchedule.Next(time.Now()))
next := validSchedule.Next(time.Now())
loc, _ := time.LoadLocation(validSchedule.Timezone())

_, _ = fmt.Fprintf(cmd.OutOrStdout(),
"schedule: %s\ntimezone: %s\nnext: %s\n",
validSchedule.Cron(),
validSchedule.Timezone(),
next.In(loc),
)

return nil
},
Expand Down
3 changes: 2 additions & 1 deletion cli/autostop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ func TestAutostop(t *testing.T) {

err = cmd.Execute()
require.NoError(t, err, "unexpected error")
require.Contains(t, stdoutBuf.String(), "schedule: "+sched)
// CRON_TZ gets stripped
require.Contains(t, stdoutBuf.String(), "schedule: 30 17 * * 1-5")
})

t.Run("EnableDisableOK", func(t *testing.T) {
Expand Down
13 changes: 9 additions & 4 deletions cli/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/spf13/cobra"

"github.com/coder/coder/cli/cliui"
"github.com/coder/coder/coderd/autobuild/schedule"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/codersdk"
)
Expand Down Expand Up @@ -108,14 +109,18 @@ func list() *cobra.Command {
durationDisplay = durationDisplay[:len(durationDisplay)-2]
}

autostartDisplay := "not enabled"
autostartDisplay := "-"
if workspace.AutostartSchedule != "" {
autostartDisplay = workspace.AutostartSchedule
if sched, err := schedule.Weekly(workspace.AutostartSchedule); err == nil {
autostartDisplay = sched.Cron()
}
}

autostopDisplay := "not enabled"
autostopDisplay := "-"
if workspace.AutostopSchedule != "" {
autostopDisplay = workspace.AutostopSchedule
if sched, err := schedule.Weekly(workspace.AutostopSchedule); err == nil {
autostopDisplay = sched.Cron()
}
}

user := usersByID[workspace.OwnerID]
Expand Down
44 changes: 37 additions & 7 deletions coderd/autobuild/schedule/schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,18 @@ var defaultParser = cron.NewParser(parserFormat)
// us_sched, _ := schedule.Weekly("CRON_TZ=US/Central 30 9 1-5")
// fmt.Println(sched.Next(time.Now()).Format(time.RFC3339))
// // Output: 2022-04-04T14:30:00Z
func Weekly(spec string) (*Schedule, error) {
if err := validateWeeklySpec(spec); err != nil {
func Weekly(raw string) (*Schedule, error) {
if err := validateWeeklySpec(raw); err != nil {
return nil, xerrors.Errorf("validate weekly schedule: %w", err)
}

specSched, err := defaultParser.Parse(spec)
// If schedule does not specify a timezone, default to UTC. Otherwise,
// the library will default to time.Local which we want to avoid.
if !strings.HasPrefix(raw, "CRON_TZ=") {
raw = "CRON_TZ=UTC " + raw
}

specSched, err := defaultParser.Parse(raw)
if err != nil {
return nil, xerrors.Errorf("parse schedule: %w", err)
}
Expand All @@ -49,9 +55,16 @@ func Weekly(spec string) (*Schedule, error) {
return nil, xerrors.Errorf("expected *cron.SpecSchedule but got %T", specSched)
}

// Strip the leading CRON_TZ prefix so we just store the cron string.
// The timezone info is available in SpecSchedule.
cronStr := raw
if strings.HasPrefix(raw, "CRON_TZ=") {
cronStr = strings.Join(strings.Fields(raw)[1:], " ")
}

cronSched := &Schedule{
sched: schedule,
spec: spec,
sched: schedule,
cronStr: cronStr,
}
return cronSched, nil
}
Expand All @@ -61,12 +74,29 @@ func Weekly(spec string) (*Schedule, error) {
type Schedule struct {
sched *cron.SpecSchedule
// XXX: there isn't any nice way for robfig/cron to serialize
spec string
cronStr string
}

// String serializes the schedule to its original human-friendly format.
// The leading CRON_TZ is maintained.
func (s Schedule) String() string {
return s.spec
var sb strings.Builder
_, _ = sb.WriteString("CRON_TZ=")
_, _ = sb.WriteString(s.sched.Location.String())
_, _ = sb.WriteString(" ")
_, _ = sb.WriteString(s.cronStr)
return sb.String()
}

// Timezone returns the timezone for the schedule.
func (s Schedule) Timezone() string {
return s.sched.Location.String()
}

// Cron returns the cron spec for the schedule with the leading CRON_TZ
// stripped, if present.
func (s Schedule) Cron() string {
return s.cronStr
}

// Next returns the next time in the schedule relative to t.
Expand Down
10 changes: 9 additions & 1 deletion coderd/autobuild/schedule/schedule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,26 @@ func Test_Weekly(t *testing.T) {
at time.Time
expectedNext time.Time
expectedError string
expectedCron string
expectedTz string
}{
{
name: "with timezone",
spec: "CRON_TZ=US/Central 30 9 * * 1-5",
at: time.Date(2022, 4, 1, 14, 29, 0, 0, time.UTC),
expectedNext: time.Date(2022, 4, 1, 14, 30, 0, 0, time.UTC),
expectedError: "",
expectedCron: "30 9 * * 1-5",
expectedTz: "US/Central",
},
{
name: "without timezone",
spec: "30 9 * * 1-5",
spec: "CRON_TZ=UTC 30 9 * * 1-5",
at: time.Date(2022, 4, 1, 9, 29, 0, 0, time.Local),
expectedNext: time.Date(2022, 4, 1, 9, 30, 0, 0, time.Local),
expectedError: "",
expectedCron: "30 9 * * 1-5",
expectedTz: "UTC",
},
{
name: "invalid schedule",
Expand Down Expand Up @@ -86,6 +92,8 @@ func Test_Weekly(t *testing.T) {
require.NoError(t, err)
require.Equal(t, testCase.expectedNext, nextTime)
require.Equal(t, testCase.spec, actual.String())
require.Equal(t, testCase.expectedCron, actual.Cron())
require.Equal(t, testCase.expectedTz, actual.Timezone())
} else {
require.EqualError(t, err, testCase.expectedError)
require.Nil(t, actual)
Expand Down