Skip to content

Commit 7eb526d

Browse files
committed
add stat subcommands
1 parent 69b1904 commit 7eb526d

13 files changed

+387
-18
lines changed

cli/stat.go

Lines changed: 128 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,40 @@ import (
55
"os"
66

77
"github.com/spf13/afero"
8+
"golang.org/x/xerrors"
89

910
"github.com/coder/coder/cli/clibase"
1011
"github.com/coder/coder/cli/clistat"
1112
"github.com/coder/coder/cli/cliui"
1213
)
1314

14-
func (*RootCmd) stat() *clibase.Cmd {
15+
func (r *RootCmd) stat() *clibase.Cmd {
1516
fs := afero.NewReadOnlyFs(afero.NewOsFs())
16-
defaultCols := []string{"host_cpu", "host_memory", "home_disk", "container_cpu", "container_memory"}
17+
defaultCols := []string{
18+
"host_cpu",
19+
"host_memory",
20+
"home_disk",
21+
"container_cpu",
22+
"container_memory",
23+
}
1724
formatter := cliui.NewOutputFormatter(
1825
cliui.TableFormat([]statsRow{}, defaultCols),
1926
cliui.JSONFormat(),
2027
)
28+
st, err := clistat.New(clistat.WithFS(fs))
29+
if err != nil {
30+
panic(xerrors.Errorf("initialize workspace stats collector: %w", err))
31+
}
2132

2233
cmd := &clibase.Cmd{
23-
Use: "stat",
24-
Short: "Show workspace resource usage.",
25-
Options: clibase.OptionSet{},
34+
Use: "stat",
35+
Short: "Show resource usage for the current workspace.",
36+
Children: []*clibase.Cmd{
37+
r.statCPU(st, fs),
38+
r.statMem(st, fs),
39+
r.statDisk(st, fs),
40+
},
2641
Handler: func(inv *clibase.Invocation) error {
27-
st, err := clistat.New(clistat.WithFS(fs))
28-
if err != nil {
29-
return err
30-
}
31-
3242
var sr statsRow
3343

3444
// Get CPU measurements first.
@@ -108,6 +118,114 @@ func (*RootCmd) stat() *clibase.Cmd {
108118
return cmd
109119
}
110120

121+
func (*RootCmd) statCPU(s *clistat.Statter, fs afero.Fs) *clibase.Cmd {
122+
var hostArg bool
123+
formatter := cliui.NewOutputFormatter(cliui.TextFormat(), cliui.JSONFormat())
124+
125+
cmd := &clibase.Cmd{
126+
Use: "cpu",
127+
Short: "Show CPU usage, in cores.",
128+
Options: clibase.OptionSet{
129+
{
130+
Flag: "host",
131+
Value: clibase.BoolOf(&hostArg),
132+
Description: "Force host CPU measurement.",
133+
},
134+
},
135+
Handler: func(inv *clibase.Invocation) error {
136+
var cs *clistat.Result
137+
var err error
138+
if ok, _ := clistat.IsContainerized(fs); ok && !hostArg {
139+
cs, err = s.ContainerCPU()
140+
} else {
141+
cs, err = s.HostCPU()
142+
}
143+
if err != nil {
144+
return err
145+
}
146+
out, err := formatter.Format(inv.Context(), cs)
147+
if err != nil {
148+
return err
149+
}
150+
_, err = fmt.Fprintln(inv.Stdout, out)
151+
return err
152+
},
153+
}
154+
formatter.AttachOptions(&cmd.Options)
155+
156+
return cmd
157+
}
158+
159+
func (*RootCmd) statMem(s *clistat.Statter, fs afero.Fs) *clibase.Cmd {
160+
var hostArg bool
161+
formatter := cliui.NewOutputFormatter(cliui.TextFormat(), cliui.JSONFormat())
162+
cmd := &clibase.Cmd{
163+
Use: "mem",
164+
Short: "Show memory usage, in gigabytes.",
165+
Options: clibase.OptionSet{
166+
{
167+
Flag: "host",
168+
Value: clibase.BoolOf(&hostArg),
169+
Description: "Force host memory measurement.",
170+
},
171+
},
172+
Handler: func(inv *clibase.Invocation) error {
173+
var ms *clistat.Result
174+
var err error
175+
if ok, _ := clistat.IsContainerized(fs); ok && !hostArg {
176+
ms, err = s.ContainerMemory()
177+
} else {
178+
ms, err = s.HostMemory()
179+
}
180+
if err != nil {
181+
return err
182+
}
183+
out, err := formatter.Format(inv.Context(), ms)
184+
if err != nil {
185+
return err
186+
}
187+
_, err = fmt.Fprintln(inv.Stdout, out)
188+
return err
189+
},
190+
}
191+
192+
formatter.AttachOptions(&cmd.Options)
193+
return cmd
194+
}
195+
196+
func (*RootCmd) statDisk(s *clistat.Statter, fs afero.Fs) *clibase.Cmd {
197+
var pathArg string
198+
formatter := cliui.NewOutputFormatter(cliui.TextFormat(), cliui.JSONFormat())
199+
cmd := &clibase.Cmd{
200+
Use: "disk",
201+
Short: "Show disk usage, in gigabytes.",
202+
Options: clibase.OptionSet{
203+
{
204+
Flag: "path",
205+
Value: clibase.StringOf(&pathArg),
206+
Description: "Path for which to check disk usage.",
207+
Default: "/",
208+
},
209+
},
210+
Handler: func(inv *clibase.Invocation) error {
211+
ds, err := s.Disk(pathArg)
212+
if err != nil {
213+
return err
214+
}
215+
216+
out, err := formatter.Format(inv.Context(), ds)
217+
if err != nil {
218+
return err
219+
}
220+
_, err = fmt.Fprintln(inv.Stdout, out)
221+
return err
222+
},
223+
}
224+
225+
formatter.AttachOptions(&cmd.Options)
226+
return cmd
227+
}
228+
111229
type statsRow struct {
112230
HostCPU *clistat.Result `json:"host_cpu" table:"host_cpu,default_sort"`
113231
HostMemory *clistat.Result `json:"host_memory" table:"host_memory"`

cli/stat_test.go

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func TestStatCmd(t *testing.T) {
2323
t.Parallel()
2424
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
2525
t.Cleanup(cancel)
26-
inv, _ := clitest.New(t, "stat", "--output=json")
26+
inv, _ := clitest.New(t, "stat", "all", "--output=json")
2727
buf := new(bytes.Buffer)
2828
inv.Stdout = buf
2929
err := inv.WithContext(ctx).Run()
@@ -38,7 +38,7 @@ func TestStatCmd(t *testing.T) {
3838
t.Parallel()
3939
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
4040
t.Cleanup(cancel)
41-
inv, _ := clitest.New(t, "stat", "--output=table")
41+
inv, _ := clitest.New(t, "stat", "all", "--output=table")
4242
buf := new(bytes.Buffer)
4343
inv.Stdout = buf
4444
err := inv.WithContext(ctx).Run()
@@ -53,7 +53,7 @@ func TestStatCmd(t *testing.T) {
5353
t.Parallel()
5454
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
5555
t.Cleanup(cancel)
56-
inv, _ := clitest.New(t, "stat")
56+
inv, _ := clitest.New(t, "stat", "all")
5757
buf := new(bytes.Buffer)
5858
inv.Stdout = buf
5959
err := inv.WithContext(ctx).Run()
@@ -65,3 +65,96 @@ func TestStatCmd(t *testing.T) {
6565
require.Contains(t, s, "HOME DISK")
6666
})
6767
}
68+
69+
func TestStatCPUCmd(t *testing.T) {
70+
t.Parallel()
71+
72+
t.Run("Text", func(t *testing.T) {
73+
t.Parallel()
74+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
75+
t.Cleanup(cancel)
76+
inv, _ := clitest.New(t, "stat", "cpu", "--output=text")
77+
buf := new(bytes.Buffer)
78+
inv.Stdout = buf
79+
err := inv.WithContext(ctx).Run()
80+
require.NoError(t, err)
81+
s := buf.String()
82+
require.NotEmpty(t, s)
83+
})
84+
85+
t.Run("JSON", func(t *testing.T) {
86+
t.Parallel()
87+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
88+
t.Cleanup(cancel)
89+
inv, _ := clitest.New(t, "stat", "cpu", "--output=json")
90+
buf := new(bytes.Buffer)
91+
inv.Stdout = buf
92+
err := inv.WithContext(ctx).Run()
93+
require.NoError(t, err)
94+
s := buf.String()
95+
tmp := struct{}{}
96+
require.NoError(t, json.NewDecoder(strings.NewReader(s)).Decode(&tmp))
97+
})
98+
}
99+
100+
func TestStatMemCmd(t *testing.T) {
101+
t.Parallel()
102+
103+
t.Run("Text", func(t *testing.T) {
104+
t.Parallel()
105+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
106+
t.Cleanup(cancel)
107+
inv, _ := clitest.New(t, "stat", "mem", "--output=text")
108+
buf := new(bytes.Buffer)
109+
inv.Stdout = buf
110+
err := inv.WithContext(ctx).Run()
111+
require.NoError(t, err)
112+
s := buf.String()
113+
require.NotEmpty(t, s)
114+
})
115+
116+
t.Run("JSON", func(t *testing.T) {
117+
t.Parallel()
118+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
119+
t.Cleanup(cancel)
120+
inv, _ := clitest.New(t, "stat", "mem", "--output=json")
121+
buf := new(bytes.Buffer)
122+
inv.Stdout = buf
123+
err := inv.WithContext(ctx).Run()
124+
require.NoError(t, err)
125+
s := buf.String()
126+
tmp := struct{}{}
127+
require.NoError(t, json.NewDecoder(strings.NewReader(s)).Decode(&tmp))
128+
})
129+
}
130+
131+
func TestStatDiskCmd(t *testing.T) {
132+
t.Parallel()
133+
134+
t.Run("Text", func(t *testing.T) {
135+
t.Parallel()
136+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
137+
t.Cleanup(cancel)
138+
inv, _ := clitest.New(t, "stat", "disk", "--output=text")
139+
buf := new(bytes.Buffer)
140+
inv.Stdout = buf
141+
err := inv.WithContext(ctx).Run()
142+
require.NoError(t, err)
143+
s := buf.String()
144+
require.NotEmpty(t, s)
145+
})
146+
147+
t.Run("JSON", func(t *testing.T) {
148+
t.Parallel()
149+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
150+
t.Cleanup(cancel)
151+
inv, _ := clitest.New(t, "stat", "disk", "--output=json")
152+
buf := new(bytes.Buffer)
153+
inv.Stdout = buf
154+
err := inv.WithContext(ctx).Run()
155+
require.NoError(t, err)
156+
s := buf.String()
157+
tmp := struct{}{}
158+
require.NoError(t, json.NewDecoder(strings.NewReader(s)).Decode(&tmp))
159+
})
160+
}

cli/testdata/coder_--help.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Coder v0.0.0-devel — A tool for provisioning self-hosted development environme
3434
workspace
3535
ssh Start a shell into a workspace
3636
start Start a workspace
37-
stat Show workspace resource usage.
37+
stat Show resource usage for the current workspace.
3838
state Manually manage Terraform state to fix broken workspaces
3939
stop Stop a workspace
4040
templates Manage templates

cli/testdata/coder_stat_--help.golden

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
Usage: coder stat [flags]
22

3-
Show workspace resource usage.
3+
Show resource usage for the current workspace.
4+
5+
Subcommands
6+
cpu Show CPU usage, in cores.
7+
disk Show disk usage, in gigabytes.
8+
mem Show memory usage, in gigabytes.
49

510
Options
611
-c, --column string-array (default: host_cpu,host_memory,home_disk,container_cpu,container_memory)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Usage: coder stat cpu [flags]
2+
3+
Show CPU usage, in cores.
4+
5+
Options
6+
--host bool
7+
Force host CPU measurement.
8+
9+
-o, --output string (default: text)
10+
Output format. Available formats: text, json.
11+
12+
---
13+
Run `coder --help` for a list of global options.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Usage: coder stat disk [flags]
2+
3+
Show disk usage, in gigabytes.
4+
5+
Options
6+
-o, --output string (default: text)
7+
Output format. Available formats: text, json.
8+
9+
--path string (default: /)
10+
Path for which to check disk usage.
11+
12+
---
13+
Run `coder --help` for a list of global options.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Usage: coder stat mem [flags]
2+
3+
Show memory usage, in gigabytes.
4+
5+
Options
6+
--host bool
7+
Force host memory measurement.
8+
9+
-o, --output string (default: text)
10+
Output format. Available formats: text, json.
11+
12+
---
13+
Run `coder --help` for a list of global options.

docs/cli.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Coder — A tool for provisioning self-hosted development environments with Terr
4949
| [<code>speedtest</code>](./cli/speedtest.md) | Run upload and download tests from your machine to a workspace |
5050
| [<code>ssh</code>](./cli/ssh.md) | Start a shell into a workspace |
5151
| [<code>start</code>](./cli/start.md) | Start a workspace |
52-
| [<code>stat</code>](./cli/stat.md) | Show workspace resource usage. |
52+
| [<code>stat</code>](./cli/stat.md) | Show resource usage for the current workspace. |
5353
| [<code>state</code>](./cli/state.md) | Manually manage Terraform state to fix broken workspaces |
5454
| [<code>stop</code>](./cli/stop.md) | Stop a workspace |
5555
| [<code>templates</code>](./cli/templates.md) | Manage templates |

docs/cli/stat.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,22 @@
22

33
# stat
44

5-
Show workspace resource usage.
5+
Show resource usage for the current workspace.
66

77
## Usage
88

99
```console
1010
coder stat [flags]
1111
```
1212

13+
## Subcommands
14+
15+
| Name | Purpose |
16+
| ----------------------------------- | -------------------------------- |
17+
| [<code>cpu</code>](./stat_cpu.md) | Show CPU usage, in cores. |
18+
| [<code>disk</code>](./stat_disk.md) | Show disk usage, in gigabytes. |
19+
| [<code>mem</code>](./stat_mem.md) | Show memory usage, in gigabytes. |
20+
1321
## Options
1422

1523
### -c, --column

0 commit comments

Comments
 (0)