-
Notifications
You must be signed in to change notification settings - Fork 887
feat: add coder stat
command
#6938
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
Closed
Closed
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
package cli | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"fmt" | ||
"os" | ||
"strconv" | ||
"strings" | ||
"time" | ||
|
||
"github.com/shirou/gopsutil/cpu" | ||
"golang.org/x/xerrors" | ||
|
||
"github.com/coder/coder/cli/clibase" | ||
"github.com/coder/coder/cli/cliui" | ||
) | ||
|
||
type statCmd struct { | ||
*RootCmd | ||
watch time.Duration | ||
} | ||
|
||
func (r *RootCmd) stat() *clibase.Cmd { | ||
c := &clibase.Cmd{ | ||
Use: "stat <type> [flags...]", | ||
Short: "Display local system resource usage statistics", | ||
Long: "stat calls can be used as the script for agent metadata blocks.", | ||
} | ||
sc := statCmd{RootCmd: r} | ||
c.Options.Add( | ||
clibase.Option{ | ||
Flag: "watch", | ||
FlagShorthand: "w", | ||
Description: "Continuously display the statistic on the given interval.", | ||
Value: clibase.DurationOf(&sc.watch), | ||
}, | ||
) | ||
c.AddSubcommands( | ||
sc.cpu(), | ||
) | ||
sc.setWatchLoops(c) | ||
return c | ||
} | ||
|
||
func (sc *statCmd) setWatchLoops(c *clibase.Cmd) { | ||
for _, cmd := range c.Children { | ||
innerHandler := cmd.Handler | ||
cmd.Handler = func(inv *clibase.Invocation) error { | ||
if sc.watch == 0 { | ||
return innerHandler(inv) | ||
} | ||
|
||
ticker := time.NewTicker(sc.watch) | ||
defer ticker.Stop() | ||
|
||
for range ticker.C { | ||
if err := innerHandler(inv); err != nil { | ||
_, _ = fmt.Fprintf(inv.Stderr, "error: %v", err) | ||
} | ||
} | ||
panic("unreachable") | ||
} | ||
} | ||
} | ||
|
||
func cpuUsageFromCgroup(interval time.Duration) (float64, error) { | ||
cgroup, err := os.OpenFile("/proc/self/cgroup", os.O_RDONLY, 0) | ||
if err != nil { | ||
return 0, err | ||
} | ||
defer cgroup.Close() | ||
sc := bufio.NewScanner(cgroup) | ||
|
||
var groupDir string | ||
for sc.Scan() { | ||
fields := strings.Split(sc.Text(), ":") | ||
if len(fields) != 3 { | ||
continue | ||
} | ||
if fields[1] != "cpu,cpuacct" { | ||
continue | ||
} | ||
groupDir = fields[2] | ||
break | ||
} | ||
|
||
if groupDir == "" { | ||
return 0, xerrors.New("no cpu cgroup found") | ||
} | ||
|
||
cpuAcct := func() (int64, error) { | ||
path := fmt.Sprintf("/sys/fs/cgroup/cpu,cpuacct/%s/cpuacct.usage", groupDir) | ||
|
||
byt, err := os.ReadFile( | ||
path, | ||
) | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
return strconv.ParseInt(string(bytes.TrimSpace(byt)), 10, 64) | ||
} | ||
|
||
stat1, err := cpuAcct() | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
time.Sleep(interval) | ||
|
||
stat2, err := cpuAcct() | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
var ( | ||
cpuTime = time.Duration(stat2 - stat1) | ||
realTime = interval | ||
) | ||
|
||
ncpu, err := cpu.Counts(true) | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
return (cpuTime.Seconds() / realTime.Seconds()) * 100 / float64(ncpu), nil | ||
} | ||
|
||
//nolint:revive | ||
func (sc *statCmd) cpu() *clibase.Cmd { | ||
var interval time.Duration | ||
c := &clibase.Cmd{ | ||
Use: "cpu-usage", | ||
Aliases: []string{"cu"}, | ||
Short: "Display the system's cpu usage", | ||
Long: "If inside a cgroup (e.g. docker container), the cpu usage is ", | ||
Handler: func(inv *clibase.Invocation) error { | ||
if sc.watch != 0 { | ||
interval = sc.watch | ||
} | ||
|
||
r, err := cpuUsageFromCgroup(interval) | ||
if err != nil { | ||
cliui.Infof(sc.verboseStderr(inv), "cgroup error: %+v", err) | ||
|
||
// Use standard methods if cgroup method fails. | ||
rs, err := cpu.Percent(interval, false) | ||
if err != nil { | ||
return err | ||
} | ||
r = rs[0] | ||
} | ||
|
||
_, _ = fmt.Fprintf(inv.Stdout, "%02.0f\n", r) | ||
|
||
return nil | ||
}, | ||
Options: []clibase.Option{ | ||
{ | ||
Flag: "interval", | ||
FlagShorthand: "i", | ||
Description: `The sample collection interval. If --watch is set, it overrides this value.`, | ||
Default: "0s", | ||
Value: clibase.DurationOf(&interval), | ||
}, | ||
}, | ||
} | ||
return c | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package cli_test | ||
|
||
import ( | ||
"bytes" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/coder/coder/cli/clitest" | ||
) | ||
|
||
func TestStat(t *testing.T) { | ||
t.Parallel() | ||
|
||
t.Run("cpu", func(t *testing.T) { | ||
t.Parallel() | ||
inv, _ := clitest.New(t, "stat", "cpu") | ||
var out bytes.Buffer | ||
inv.Stdout = &out | ||
clitest.Run(t, inv) | ||
|
||
require.Regexp(t, `^[\d]{2}\n$`, out.String()) | ||
}) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
Usage: coder stat [flags] <type> [flags...] | ||
|
||
Display local system resource usage statistics | ||
|
||
stat calls can be used as the script for agent metadata blocks. | ||
|
||
[1mSubcommands[0m | ||
cpu Display the system's cpu usage | ||
|
||
[1mOptions[0m | ||
-w, --watch duration | ||
Continuously display the statistic on the given interval. | ||
|
||
--- | ||
Run `coder --help` for a list of global options. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
Usage: coder stat cpu [flags] | ||
|
||
Display the system's cpu usage | ||
|
||
If inside a cgroup (e.g. docker container), the cpu usage is | ||
|
||
[1mOptions[0m | ||
-i, --interval duration (default: 0s) | ||
The sample collection interval. If --watch is set, it overrides this | ||
value. | ||
|
||
--- | ||
Run `coder --help` for a list of global options. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<!-- DO NOT EDIT | GENERATED CONTENT --> | ||
|
||
# stat | ||
|
||
Display local system resource usage statistics | ||
|
||
## Usage | ||
|
||
```console | ||
coder stat [flags] <type> [flags...] | ||
``` | ||
|
||
## Description | ||
|
||
```console | ||
stat calls can be used as the script for agent metadata blocks. | ||
``` | ||
|
||
## Subcommands | ||
|
||
| Name | Purpose | | ||
| ------------------------------ | ------------------------------ | | ||
| [<code>cpu</code>](./stat_cpu) | Display the system's cpu usage | | ||
|
||
## Options | ||
|
||
### -w, --watch | ||
|
||
| | | | ||
| ---- | --------------------- | | ||
| Type | <code>duration</code> | | ||
|
||
Continuously display the statistic on the given interval. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<!-- DO NOT EDIT | GENERATED CONTENT --> | ||
|
||
# stat cpu | ||
|
||
Display the system's cpu usage | ||
|
||
## Usage | ||
|
||
```console | ||
coder stat cpu [flags] | ||
``` | ||
|
||
## Description | ||
|
||
```console | ||
If inside a cgroup (e.g. docker container), the cpu usage is | ||
``` | ||
|
||
## Options | ||
|
||
### -i, --interval | ||
|
||
| | | | ||
| ------- | --------------------- | | ||
| Type | <code>duration</code> | | ||
| Default | <code>0s</code> | | ||
|
||
The sample collection interval. If --watch is set, it overrides this value. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bpmct go pkg doesn't handle this on its own, and we need to specify the path of the cgroup, which is kernel/release dependent.