Skip to content

Commit 11cfd67

Browse files
committed
fixup! [supervisor] add resource status endpoint
1 parent 8cb1a1a commit 11cfd67

File tree

6 files changed

+229
-174
lines changed

6 files changed

+229
-174
lines changed

components/supervisor/cmd/top.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,44 +16,54 @@ import (
1616

1717
"github.com/gitpod-io/gitpod/common-go/log"
1818
"github.com/gitpod-io/gitpod/supervisor/api"
19+
"github.com/gitpod-io/gitpod/supervisor/pkg/supervisor"
1920
)
2021

2122
var topOpts struct {
22-
Json bool
23+
Json bool
24+
Standalone bool
2325
}
2426

2527
var topCmd = &cobra.Command{
2628
Use: "top",
2729
Short: "Display resource (CPU/memory) usage of the workspace",
2830
Run: func(cmd *cobra.Command, args []string) {
29-
client := api.NewStatusServiceClient(dialSupervisor())
30-
3131
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
3232
defer cancel()
3333

34-
resp, err := client.ResourcesStatus(ctx, &api.ResourcesStatuRequest{})
34+
var (
35+
status *api.ResourcesStatusResponse
36+
err error
37+
)
38+
if topOpts.Standalone {
39+
status, err = supervisor.Top(ctx)
40+
} else {
41+
client := api.NewStatusServiceClient(dialSupervisor())
42+
status, err = client.ResourcesStatus(ctx, &api.ResourcesStatuRequest{})
43+
}
3544
if err != nil {
3645
log.WithError(err).Fatal("failed to resolve")
3746
}
3847

3948
if topOpts.Json {
40-
content, _ := json.Marshal(resp)
49+
content, _ := json.Marshal(status)
4150
fmt.Println(string(content))
4251
} else {
4352
tw := tabwriter.NewWriter(os.Stdout, 6, 4, 3, ' ', 0)
4453
defer tw.Flush()
4554

4655
fmt.Fprintf(tw, "CPU(millicores)\tMEMORY(bytes)\n")
4756

48-
cpuFraction := resp.Cpu.Used / resp.Cpu.Limit * 100
49-
memFraction := resp.Memory.Used / resp.Memory.Limit * 100
57+
cpuFraction := int64((float64(status.Cpu.Used) / float64(status.Cpu.Limit)) * 100)
58+
memFraction := int64((float64(status.Memory.Used) / float64(status.Memory.Limit)) * 100)
5059

51-
fmt.Fprintf(tw, "%dm/%dm (%d%%)\t%dMi/%dMi (%d%%)\n", resp.Cpu.Used, resp.Cpu.Limit, cpuFraction, resp.Memory.Used/(1024*1024), resp.Memory.Limit/(1024*1024), memFraction)
60+
fmt.Fprintf(tw, "%dm/%dm (%d%%)\t%dMi/%dMi (%d%%)\n", status.Cpu.Used, status.Cpu.Limit, cpuFraction, status.Memory.Used/(1024*1024), status.Memory.Limit/(1024*1024), memFraction)
5261
}
5362
},
5463
}
5564

5665
func init() {
5766
rootCmd.AddCommand(topCmd)
5867
topCmd.Flags().BoolVarP(&topOpts.Json, "json", "j", false, "print like json")
68+
topCmd.Flags().BoolVarP(&topOpts.Standalone, "standalone", "s", false, "standalone mode")
5969
}

components/supervisor/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ require (
4343
cloud.google.com/go/storage v1.15.0 // indirect
4444
github.com/Microsoft/hcsshim v0.8.17 // indirect
4545
github.com/beorn7/perks v1.0.1 // indirect
46+
github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8
4647
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
4748
github.com/cespare/xxhash/v2 v2.1.2 // indirect
4849
github.com/docker/go-units v0.4.0 // indirect

components/supervisor/go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7
117117
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
118118
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
119119
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
120+
github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 h1:SjZ2GvvOononHOpK84APFuMvxqsk3tEIaKH/z4Rpu3g=
121+
github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8/go.mod h1:uEyr4WpAH4hio6LFriaPkL938XnrvLpNPmQHBdrmbIE=
120122
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
121123
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
122124
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=

components/supervisor/pkg/supervisor/services.go

Lines changed: 1 addition & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@ import (
99
"context"
1010
"errors"
1111
"io"
12-
"io/ioutil"
1312
"os"
1413
"path/filepath"
15-
"strconv"
1614
"strings"
1715
"sync"
1816
"time"
@@ -934,145 +932,5 @@ func (s *portService) RetryAutoExpose(ctx context.Context, req *api.RetryAutoExp
934932

935933
// ResourcesStatus provides workspace resources status information.
936934
func (s *statusService) ResourcesStatus(ctx context.Context, in *api.ResourcesStatuRequest) (*api.ResourcesStatusResponse, error) {
937-
memory, err := resolveMemoryStatus()
938-
if err != nil {
939-
return nil, err
940-
}
941-
cpu, err := resolveCPUStatus()
942-
if err != nil {
943-
return nil, err
944-
}
945-
return &api.ResourcesStatusResponse{
946-
Memory: memory,
947-
Cpu: cpu,
948-
}, nil
949-
}
950-
951-
func resolveMemoryStatus() (*api.ResourceStatus, error) {
952-
content, err := ioutil.ReadFile("/sys/fs/cgroup/memory/memory.usage_in_bytes")
953-
if err != nil {
954-
return nil, xerrors.Errorf("failed to read memory.usage_in_bytes: %w", err)
955-
}
956-
used, err := strconv.Atoi(strings.TrimSpace(string(content)))
957-
if err != nil {
958-
return nil, xerrors.Errorf("failed to parse memory.usage_in_bytes: %w", err)
959-
}
960-
content, err = ioutil.ReadFile("/sys/fs/cgroup/memory/memory.limit_in_bytes")
961-
if err != nil {
962-
return nil, xerrors.Errorf("failed to read memory.limit_in_bytes: %w", err)
963-
}
964-
limit, err := strconv.Atoi(strings.TrimSpace(string(content)))
965-
if err != nil {
966-
return nil, xerrors.Errorf("failed to parse memory.limit_in_bytes: %w", err)
967-
}
968-
content, err = ioutil.ReadFile("/sys/fs/cgroup/memory/memory.stat")
969-
if err != nil {
970-
return nil, xerrors.Errorf("failed to read memory.stat: %w", err)
971-
}
972-
statLines := strings.Split(strings.TrimSpace(string(content)), "\n")
973-
stat := make(map[string]string, len(statLines))
974-
for _, line := range statLines {
975-
tokens := strings.Split(line, " ")
976-
stat[tokens[0]] = tokens[1]
977-
}
978-
// substract evictable memory
979-
value, ok := stat["total_inactive_file"]
980-
if ok {
981-
totalInactiveFile, err := strconv.Atoi(value)
982-
if err != nil {
983-
return nil, xerrors.Errorf("failed to parse total_inactive_file: %w", err)
984-
}
985-
if used < totalInactiveFile {
986-
used = 0
987-
} else {
988-
used -= totalInactiveFile
989-
}
990-
}
991-
return &api.ResourceStatus{
992-
Limit: int64(limit),
993-
Used: int64(used),
994-
}, nil
995-
}
996-
997-
func resolveCPUStatus() (*api.ResourceStatus, error) {
998-
t, err := resolveCPUStat()
999-
if err != nil {
1000-
return nil, err
1001-
}
1002-
1003-
time.Sleep(time.Second)
1004-
1005-
t2, err := resolveCPUStat()
1006-
if err != nil {
1007-
return nil, err
1008-
}
1009-
1010-
cpuUsage := t2.usage - t.usage
1011-
totalTime := t2.uptime - t.uptime
1012-
used := cpuUsage / totalTime * 1000
1013-
1014-
content, err := ioutil.ReadFile("/sys/fs/cgroup/cpu/cpu.cfs_quota_us")
1015-
if err != nil {
1016-
return nil, xerrors.Errorf("failed to read cpu.cfs_quota_us: %w", err)
1017-
}
1018-
quota, err := strconv.Atoi(strings.TrimSpace(string(content)))
1019-
if err != nil {
1020-
return nil, xerrors.Errorf("failed to parse cpu.cfs_quota_us: %w", err)
1021-
}
1022-
1023-
var limit int
1024-
if quota > 0 {
1025-
content, err = ioutil.ReadFile("/sys/fs/cgroup/cpu/cpu.cfs_period_us")
1026-
if err != nil {
1027-
return nil, xerrors.Errorf("failed to read cpu.cfs_period_us: %w", err)
1028-
}
1029-
period, err := strconv.Atoi(strings.TrimSpace(string(content)))
1030-
if err != nil {
1031-
return nil, xerrors.Errorf("failed to parse cpu.cfs_period_us: %w", err)
1032-
}
1033-
1034-
limit = quota / period * 1000
1035-
} else {
1036-
content, err = ioutil.ReadFile("/sys/fs/cgroup/cpu/cpuacct.usage_percpu")
1037-
if err != nil {
1038-
return nil, xerrors.Errorf("failed to read cpuacct.usage_percpu: %w", err)
1039-
}
1040-
limit = len(strings.Split(strings.TrimSpace(string(content)), " ")) * 1000
1041-
}
1042-
1043-
return &api.ResourceStatus{
1044-
Limit: int64(limit),
1045-
Used: int64(used),
1046-
}, nil
1047-
}
1048-
1049-
type cpuStat struct {
1050-
usage float64
1051-
uptime float64
1052-
}
1053-
1054-
func resolveCPUStat() (*cpuStat, error) {
1055-
content, err := ioutil.ReadFile("/sys/fs/cgroup/cpu/cpuacct.usage")
1056-
if err != nil {
1057-
return nil, xerrors.Errorf("failed to read cpuacct.usage: %w", err)
1058-
}
1059-
usage, err := strconv.ParseFloat(strings.TrimSpace(string(content)), 64)
1060-
if err != nil {
1061-
return nil, xerrors.Errorf("failed to parse cpuacct.usage: %w", err)
1062-
}
1063-
// convert from nanoseconds to seconds
1064-
usage *= 1e-9
1065-
content, err = ioutil.ReadFile("/proc/uptime")
1066-
if err != nil {
1067-
return nil, xerrors.Errorf("failed to read uptime: %w", err)
1068-
}
1069-
values := strings.Split(strings.TrimSpace(string(content)), " ")
1070-
uptime, err := strconv.ParseFloat(values[0], 64)
1071-
if err != nil {
1072-
return nil, xerrors.Errorf("failed to parse uptime: %w", err)
1073-
}
1074-
return &cpuStat{
1075-
usage: usage,
1076-
uptime: uptime,
1077-
}, nil
935+
return Top(ctx)
1078936
}

0 commit comments

Comments
 (0)