feat(cli): add local and UTC time options to ping cmd (#16648)

It's sometimes useful to see when each pong was received, for
correlating these times with other events.

---------

Signed-off-by: Danny Kopping <danny@coder.com>
This commit is contained in:
Danny Kopping
2025-02-24 07:38:17 +02:00
committed by GitHub
parent 39f42bc11d
commit 4c438bd4d3
4 changed files with 103 additions and 2 deletions

View File

@ -21,13 +21,14 @@ import (
"github.com/coder/pretty" "github.com/coder/pretty"
"github.com/coder/serpent"
"github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/cli/cliutil" "github.com/coder/coder/v2/cli/cliutil"
"github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/coderd/util/ptr"
"github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/healthsdk" "github.com/coder/coder/v2/codersdk/healthsdk"
"github.com/coder/coder/v2/codersdk/workspacesdk" "github.com/coder/coder/v2/codersdk/workspacesdk"
"github.com/coder/serpent"
) )
type pingSummary struct { type pingSummary struct {
@ -86,6 +87,8 @@ func (r *RootCmd) ping() *serpent.Command {
pingNum int64 pingNum int64
pingTimeout time.Duration pingTimeout time.Duration
pingWait time.Duration pingWait time.Duration
pingTimeLocal bool
pingTimeUTC bool
appearanceConfig codersdk.AppearanceConfig appearanceConfig codersdk.AppearanceConfig
) )
@ -217,6 +220,10 @@ func (r *RootCmd) ping() *serpent.Command {
ctx, cancel := context.WithTimeout(ctx, pingTimeout) ctx, cancel := context.WithTimeout(ctx, pingTimeout)
dur, p2p, pong, err = conn.Ping(ctx) dur, p2p, pong, err = conn.Ping(ctx)
pongTime := time.Now()
if pingTimeUTC {
pongTime = pongTime.UTC()
}
cancel() cancel()
results.addResult(pong) results.addResult(pong)
if err != nil { if err != nil {
@ -268,7 +275,13 @@ func (r *RootCmd) ping() *serpent.Command {
) )
} }
_, _ = fmt.Fprintf(inv.Stdout, "pong from %s %s in %s\n", var displayTime string
if pingTimeLocal || pingTimeUTC {
displayTime = pretty.Sprintf(cliui.DefaultStyles.DateTimeStamp, "[%s] ", pongTime.Format(time.RFC3339))
}
_, _ = fmt.Fprintf(inv.Stdout, "%spong from %s %s in %s\n",
displayTime,
pretty.Sprint(cliui.DefaultStyles.Keyword, workspaceName), pretty.Sprint(cliui.DefaultStyles.Keyword, workspaceName),
via, via,
pretty.Sprint(cliui.DefaultStyles.DateTimeStamp, dur.String()), pretty.Sprint(cliui.DefaultStyles.DateTimeStamp, dur.String()),
@ -321,6 +334,16 @@ func (r *RootCmd) ping() *serpent.Command {
Description: "Specifies the number of pings to perform. By default, pings will continue until interrupted.", Description: "Specifies the number of pings to perform. By default, pings will continue until interrupted.",
Value: serpent.Int64Of(&pingNum), Value: serpent.Int64Of(&pingNum),
}, },
{
Flag: "time",
Description: "Show the response time of each pong in local time.",
Value: serpent.BoolOf(&pingTimeLocal),
},
{
Flag: "utc",
Description: "Show the response time of each pong in UTC (implies --time).",
Value: serpent.BoolOf(&pingTimeUTC),
},
} }
return cmd return cmd
} }

View File

@ -69,4 +69,60 @@ func TestPing(t *testing.T) {
cancel() cancel()
<-cmdDone <-cmdDone
}) })
t.Run("1PingWithTime", func(t *testing.T) {
t.Parallel()
tests := []struct {
name string
utc bool
}{
{name: "LocalTime"}, // --time renders the pong response time.
{name: "UTC", utc: true}, // --utc implies --time, so we expect it to also contain the pong time.
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
client, workspace, agentToken := setupWorkspaceForAgent(t)
args := []string{"ping", "-n", "1", workspace.Name, "--time"}
if tc.utc {
args = append(args, "--utc")
}
inv, root := clitest.New(t, args...)
clitest.SetupConfig(t, client, root)
pty := ptytest.New(t)
inv.Stdin = pty.Input()
inv.Stderr = pty.Output()
inv.Stdout = pty.Output()
_ = agenttest.New(t, client.URL, agentToken)
_ = coderdtest.AwaitWorkspaceAgents(t, client, workspace.ID)
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
cmdDone := tGo(t, func() {
err := inv.WithContext(ctx).Run()
assert.NoError(t, err)
})
// RFC3339 is the format used to render the pong times.
rfc3339 := `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?`
// Validate that dates are rendered as specified.
if tc.utc {
rfc3339 += `Z`
} else {
rfc3339 += `(?:Z|[+-]\d{2}:\d{2})`
}
pty.ExpectRegexMatch(`\[` + rfc3339 + `\] pong from ` + workspace.Name)
cancel()
<-cmdDone
})
}
})
} }

View File

@ -10,9 +10,15 @@ OPTIONS:
Specifies the number of pings to perform. By default, pings will Specifies the number of pings to perform. By default, pings will
continue until interrupted. continue until interrupted.
--time bool
Show the response time of each pong in local time.
-t, --timeout duration (default: 5s) -t, --timeout duration (default: 5s)
Specifies how long to wait for a ping to complete. Specifies how long to wait for a ping to complete.
--utc bool
Show the response time of each pong in UTC (implies --time).
--wait duration (default: 1s) --wait duration (default: 1s)
Specifies how long to wait between pings. Specifies how long to wait between pings.

View File

@ -36,3 +36,19 @@ Specifies how long to wait for a ping to complete.
| Type | <code>int</code> | | Type | <code>int</code> |
Specifies the number of pings to perform. By default, pings will continue until interrupted. Specifies the number of pings to perform. By default, pings will continue until interrupted.
### --time
| | |
|------|-------------------|
| Type | <code>bool</code> |
Show the response time of each pong in local time.
### --utc
| | |
|------|-------------------|
| Type | <code>bool</code> |
Show the response time of each pong in UTC (implies --time).