mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add coder exp sync list command (#26443)
Add a new subcommand to list all registered sync units and their current
statuses. This provides a quick overview of the dependency coordination
state in a workspace without needing to query each unit individually.
The command supports both table (default) and JSON output formats.
```
$ coder exp sync list
UNIT STATUS READY
unit-a started true
unit-b completed true
unit-c pending false
$ coder exp sync list --output json
[
{
"unit_name": "my-unit",
"status": "started",
"is_ready": true
}
]
```
When no units are registered, the command prints `No units registered`.
<details><summary>Changes across layers</summary>
- `agent/unit`: add `Manager.ListUnits()` method
- `agent/agentsocket/proto`: add `SyncList` RPC, bump API to v1.2
- `agent/agentsocket`: add service and client implementations
- `cli`: add `sync_list.go` command, register in `sync.go`
- Tests: three golden-file test cases (empty list, multiple units, JSON)
</details>
> Generated by Coder Agents on behalf of @SasSwart
---------
Co-authored-by: Cian Johnston <cian@coder.com>
This commit is contained in:
co-authored by
Cian Johnston
parent
74a7ad0929
commit
7d95153bf4
@@ -98,6 +98,10 @@ func TestCommandHelp(t *testing.T) {
|
||||
Name: "coder exp sync status --help",
|
||||
Cmd: []string{"exp", "sync", "status", "--help"},
|
||||
},
|
||||
clitest.CommandHelpCase{
|
||||
Name: "coder exp sync list --help",
|
||||
Cmd: []string{"exp", "sync", "list", "--help"},
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ func (r *RootCmd) syncCommand() *serpent.Command {
|
||||
r.syncWant(&socketPath),
|
||||
r.syncComplete(&socketPath),
|
||||
r.syncStatus(&socketPath),
|
||||
r.syncList(&socketPath),
|
||||
},
|
||||
Options: serpent.OptionSet{
|
||||
{
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/v2/agent/agentsocket"
|
||||
"github.com/coder/coder/v2/cli/cliui"
|
||||
"github.com/coder/serpent"
|
||||
)
|
||||
|
||||
func (*RootCmd) syncList(socketPath *string) *serpent.Command {
|
||||
formatter := cliui.NewOutputFormatter(
|
||||
cliui.TableFormat(
|
||||
[]agentsocket.SyncListItem{},
|
||||
[]string{
|
||||
"unit",
|
||||
"status",
|
||||
"ready",
|
||||
},
|
||||
),
|
||||
cliui.JSONFormat(),
|
||||
)
|
||||
|
||||
cmd := &serpent.Command{
|
||||
Use: "list",
|
||||
Short: "List all registered units and their statuses",
|
||||
Long: "List all units currently registered with the workspace agent. Shows each unit's name, status, and whether it is ready to start.",
|
||||
Handler: func(i *serpent.Invocation) error {
|
||||
ctx := i.Context()
|
||||
|
||||
opts := []agentsocket.Option{}
|
||||
if *socketPath != "" {
|
||||
opts = append(opts, agentsocket.WithPath(*socketPath))
|
||||
}
|
||||
|
||||
client, err := agentsocket.NewClient(ctx, opts...)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("connect to agent socket: %w", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
items, err := client.SyncList(ctx)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("list units failed: %w", err)
|
||||
}
|
||||
|
||||
if len(items) == 0 && formatter.FormatID() == "table" {
|
||||
cliui.Info(i.Stdout, "No units registered")
|
||||
return nil
|
||||
}
|
||||
|
||||
out, err := formatter.Format(ctx, items)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("format output: %w", err)
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintln(i.Stdout, out)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
formatter.AttachOptions(&cmd.Options)
|
||||
return cmd
|
||||
}
|
||||
+1
-1
@@ -36,7 +36,7 @@ func (*RootCmd) syncStatus(socketPath *string) *serpent.Command {
|
||||
cmd := &serpent.Command{
|
||||
Use: "status <unit>",
|
||||
Short: "Show unit status and dependency state",
|
||||
Long: "Show the current status of a unit, whether it is ready to start, and lists its dependencies. Shows which dependencies are satisfied and which are still pending. Supports multiple output formats.",
|
||||
Long: "Show the current status of a unit, whether it is ready to start, and lists its dependencies. Shows which dependencies are satisfied and which are still pending.",
|
||||
Handler: func(i *serpent.Invocation) error {
|
||||
ctx := i.Context()
|
||||
|
||||
|
||||
@@ -409,4 +409,81 @@ func TestSyncCommands_Golden(t *testing.T) {
|
||||
|
||||
clitest.TestGoldenFile(t, "TestSyncCommands_Golden/status_json_format", outBuf.Bytes(), nil)
|
||||
})
|
||||
|
||||
t.Run("list_no_units", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
path, cleanup := setupSocketServer(t)
|
||||
defer cleanup()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
|
||||
var outBuf bytes.Buffer
|
||||
inv, _ := clitest.New(t, "exp", "sync", "list", "--socket-path", path)
|
||||
inv.Stdout = &outBuf
|
||||
inv.Stderr = &outBuf
|
||||
|
||||
err := inv.WithContext(ctx).Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
clitest.TestGoldenFile(t, "TestSyncCommands_Golden/list_no_units", outBuf.Bytes(), nil)
|
||||
})
|
||||
|
||||
t.Run("list_with_units", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
path, cleanup := setupSocketServer(t)
|
||||
defer cleanup()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
|
||||
// Register some units in various states.
|
||||
client, err := agentsocket.NewClient(ctx, agentsocket.WithPath(path))
|
||||
require.NoError(t, err)
|
||||
// unit-a: started
|
||||
err = client.SyncStart(ctx, "unit-a")
|
||||
require.NoError(t, err)
|
||||
// unit-b: completed
|
||||
err = client.SyncStart(ctx, "unit-b")
|
||||
require.NoError(t, err)
|
||||
err = client.SyncComplete(ctx, "unit-b")
|
||||
require.NoError(t, err)
|
||||
// unit-c: pending (has unsatisfied dependency on unit-a completing)
|
||||
err = client.SyncWant(ctx, "unit-c", "unit-a")
|
||||
require.NoError(t, err)
|
||||
client.Close()
|
||||
|
||||
var outBuf bytes.Buffer
|
||||
inv, _ := clitest.New(t, "exp", "sync", "list", "--socket-path", path)
|
||||
inv.Stdout = &outBuf
|
||||
inv.Stderr = &outBuf
|
||||
|
||||
err = inv.WithContext(ctx).Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
clitest.TestGoldenFile(t, "TestSyncCommands_Golden/list_with_units", outBuf.Bytes(), nil)
|
||||
})
|
||||
|
||||
t.Run("list_json_format", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
path, cleanup := setupSocketServer(t)
|
||||
defer cleanup()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
|
||||
// Register a unit.
|
||||
client, err := agentsocket.NewClient(ctx, agentsocket.WithPath(path))
|
||||
require.NoError(t, err)
|
||||
err = client.SyncStart(ctx, "my-unit")
|
||||
require.NoError(t, err)
|
||||
client.Close()
|
||||
|
||||
var outBuf bytes.Buffer
|
||||
inv, _ := clitest.New(t, "exp", "sync", "list", "--output", "json", "--socket-path", path)
|
||||
inv.Stdout = &outBuf
|
||||
inv.Stderr = &outBuf
|
||||
|
||||
err = inv.WithContext(ctx).Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
clitest.TestGoldenFile(t, "TestSyncCommands_Golden/list_json_format", outBuf.Bytes(), nil)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"unit_name": "my-unit",
|
||||
"status": "started",
|
||||
"is_ready": true
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
No units registered
|
||||
@@ -0,0 +1,4 @@
|
||||
UNIT STATUS READY
|
||||
unit-a started true
|
||||
unit-b completed true
|
||||
unit-c pending false
|
||||
+1
@@ -13,6 +13,7 @@ USAGE:
|
||||
|
||||
SUBCOMMANDS:
|
||||
complete Mark a unit as complete
|
||||
list List all registered units and their statuses
|
||||
ping Test agent socket connectivity and health
|
||||
start Wait until all unit dependencies are satisfied
|
||||
status Show unit status and dependency state
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
coder v0.0.0-devel
|
||||
|
||||
USAGE:
|
||||
coder exp sync list [flags]
|
||||
|
||||
List all registered units and their statuses
|
||||
|
||||
List all units currently registered with the workspace agent. Shows each
|
||||
unit's name, status, and whether it is ready to start.
|
||||
|
||||
OPTIONS:
|
||||
-c, --column [unit|status|ready] (default: unit,status,ready)
|
||||
Columns to display in table output.
|
||||
|
||||
-o, --output table|json (default: table)
|
||||
Output format.
|
||||
|
||||
———
|
||||
Run `coder --help` for a list of global options.
|
||||
+1
-1
@@ -7,7 +7,7 @@ USAGE:
|
||||
|
||||
Show the current status of a unit, whether it is ready to start, and lists its
|
||||
dependencies. Shows which dependencies are satisfied and which are still
|
||||
pending. Supports multiple output formats.
|
||||
pending.
|
||||
|
||||
OPTIONS:
|
||||
-c, --column [depends on|required status|current status|satisfied] (default: depends on,required status,current status,satisfied)
|
||||
|
||||
Reference in New Issue
Block a user