mirror of
https://github.com/bcicen/ctop.git
synced 2025-12-06 23:26:45 +08:00
Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae62c388bc | ||
|
|
523d6c7277 | ||
|
|
47a0d7e495 | ||
|
|
9dec6b4c67 | ||
|
|
7f6ff0b599 | ||
|
|
187adf0540 | ||
|
|
665e8fdd06 | ||
|
|
a39b7a3a3e | ||
|
|
77f5e6b735 | ||
|
|
3c83b7576b | ||
|
|
8a0bd3cf8a | ||
|
|
78caad2dbd | ||
|
|
8d8f1e72eb | ||
|
|
93556a1754 | ||
|
|
4d247f5272 | ||
|
|
db3d7e8927 | ||
|
|
efef345665 | ||
|
|
f158fa742f | ||
|
|
4d48245d7d | ||
|
|
6bee1b7f31 | ||
|
|
7118e45f3a | ||
|
|
3405d19be8 | ||
|
|
9a185b2388 | ||
|
|
caf6fc63c1 | ||
|
|
cf352f7c8a | ||
|
|
ac5bed210f | ||
|
|
a72d43526f | ||
|
|
9eb2457aa4 | ||
|
|
b83402b886 | ||
|
|
078564bd38 | ||
|
|
a2c08d312e | ||
|
|
f7a3d38d6b | ||
|
|
a3b8585697 | ||
|
|
2e526e9b86 | ||
|
|
541fe70b78 | ||
|
|
c786b697bf | ||
|
|
aa6c00b083 | ||
|
|
17855e3d8e | ||
|
|
842809bef5 | ||
|
|
4e567ee007 | ||
|
|
56700e120b | ||
|
|
5261444265 | ||
|
|
b3aa291182 | ||
|
|
051b474bf0 | ||
|
|
fac6632459 | ||
|
|
1c7cf98e58 | ||
|
|
44a54e070d | ||
|
|
10b9a6c013 | ||
|
|
a3b67e4607 | ||
|
|
ac1ce18143 | ||
|
|
01a305d326 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
ctop
|
ctop
|
||||||
.idea
|
.idea
|
||||||
|
/vendor/
|
||||||
11
Dockerfile
11
Dockerfile
@@ -1,16 +1,17 @@
|
|||||||
FROM quay.io/vektorcloud/go:1.9
|
FROM quay.io/vektorcloud/go:1.11
|
||||||
|
|
||||||
RUN apk add --no-cache make
|
RUN apk add --no-cache make
|
||||||
|
|
||||||
COPY Gopkg.* /go/src/github.com/bcicen/ctop/
|
WORKDIR /app
|
||||||
WORKDIR /go/src/github.com/bcicen/ctop/
|
COPY go.mod .
|
||||||
RUN dep ensure -vendor-only
|
RUN go mod download
|
||||||
|
|
||||||
COPY . /go/src/github.com/bcicen/ctop
|
COPY . .
|
||||||
RUN make build && \
|
RUN make build && \
|
||||||
mkdir -p /go/bin && \
|
mkdir -p /go/bin && \
|
||||||
mv -v ctop /go/bin/
|
mv -v ctop /go/bin/
|
||||||
|
|
||||||
FROM scratch
|
FROM scratch
|
||||||
|
ENV TERM=linux
|
||||||
COPY --from=0 /go/bin/ctop /ctop
|
COPY --from=0 /go/bin/ctop /ctop
|
||||||
ENTRYPOINT ["/ctop"]
|
ENTRYPOINT ["/ctop"]
|
||||||
|
|||||||
159
Gopkg.lock
generated
159
Gopkg.lock
generated
@@ -1,159 +0,0 @@
|
|||||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
|
||||||
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/Azure/go-ansiterm"
|
|
||||||
packages = [".","winterm"]
|
|
||||||
revision = "fa152c58bc15761d0200cb75fe958b89a9d4888e"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/Microsoft/go-winio"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "fff283ad5116362ca252298cfc9b95828956d85d"
|
|
||||||
version = "v0.3.8"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/Nvveen/Gotty"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "cd527374f1e5bff4938207604a14f2e38a9cf512"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/Sirupsen/logrus"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "26709e2714106fb8ad40b773b711ebce25b78914"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/c9s/goprocinfo"
|
|
||||||
packages = ["linux"]
|
|
||||||
revision = "b34328d6e0cd139894ea7347d2624ccf31fa3c58"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/coreos/go-systemd"
|
|
||||||
packages = ["dbus","util"]
|
|
||||||
revision = "b4a58d95188dd092ae20072bac14cece0e67c388"
|
|
||||||
version = "v4"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/docker/docker"
|
|
||||||
packages = ["api/types","api/types/blkiodev","api/types/container","api/types/filters","api/types/mount","api/types/network","api/types/registry","api/types/strslice","api/types/swarm","api/types/versions","opts","pkg/archive","pkg/fileutils","pkg/homedir","pkg/idtools","pkg/ioutils","pkg/jsonlog","pkg/jsonmessage","pkg/longpath","pkg/mount","pkg/pools","pkg/promise","pkg/stdcopy","pkg/symlink","pkg/system","pkg/term","pkg/term/windows"]
|
|
||||||
revision = "90d35abf7b3535c1c319c872900fbd76374e521c"
|
|
||||||
version = "v17.05.0-ce-rc3"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/docker/go-connections"
|
|
||||||
packages = ["nat"]
|
|
||||||
revision = "a2afab9802043837035592f1c24827fb70766de9"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/docker/go-units"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "0dadbb0345b35ec7ef35e228dabb8de89a65bf52"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/fsouza/go-dockerclient"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "318513eb1ab27495afbc67f671ba1080513d8aa0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "barchart-numfmt"
|
|
||||||
name = "github.com/gizak/termui"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "ea10e6ccee219e572ffad0ac1909f1a17f6db7d6"
|
|
||||||
source = "https://github.com/bcicen/termui"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/godbus/dbus"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f"
|
|
||||||
version = "v3"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/golang/protobuf"
|
|
||||||
packages = ["proto"]
|
|
||||||
revision = "0a4f71a498b7c4812f64969510bcb4eca251e33a"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/hashicorp/go-cleanhttp"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "3573b8b52aa7b37b9358d966a898feb387f62437"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/jgautheron/codename-generator"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "16d037c7cc3c9b552fe4af9828b7338d752dbaf9"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/maruel/panicparse"
|
|
||||||
packages = ["stack"]
|
|
||||||
revision = "25bcac0d793cf4109483505a0d66e066a3a90a80"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/mattn/go-runewidth"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "14207d285c6c197daabb5c9793d63e7af9ab2d50"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/mitchellh/go-wordwrap"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "ad45545899c7b13c020ea92b2072220eefad42b8"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/nsf/termbox-go"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "91bae1bb5fa9ee504905ecbe7043fa30e92feaa3"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/nu7hatch/gouuid"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "179d4d0c4d8d407a32af483c2354df1d2c91e6c3"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/op/go-logging"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "b2cb9fa56473e98db8caba80237377e83fe44db5"
|
|
||||||
version = "v1"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/opencontainers/runc"
|
|
||||||
packages = ["libcontainer","libcontainer/apparmor","libcontainer/cgroups","libcontainer/cgroups/fs","libcontainer/cgroups/systemd","libcontainer/configs","libcontainer/configs/validate","libcontainer/criurpc","libcontainer/keys","libcontainer/label","libcontainer/seccomp","libcontainer/selinux","libcontainer/stacktrace","libcontainer/system","libcontainer/user","libcontainer/utils"]
|
|
||||||
revision = "baf6536d6259209c3edfa2b22237af82942d3dfa"
|
|
||||||
version = "v0.1.1"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/seccomp/libseccomp-golang"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "1b506fc7c24eec5a3693cdcbed40d9c226cfc6a1"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/syndtr/gocapability"
|
|
||||||
packages = ["capability"]
|
|
||||||
revision = "2c00daeb6c3b45114c80ac44119e7b8801fdd852"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/vishvananda/netlink"
|
|
||||||
packages = [".","nl"]
|
|
||||||
revision = "1e2e08e8a2dcdacaae3f14ac44c5cfa31361f270"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "golang.org/x/net"
|
|
||||||
packages = ["context","context/ctxhttp"]
|
|
||||||
revision = "a6577fac2d73be281a500b310739095313165611"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "golang.org/x/sys"
|
|
||||||
packages = ["unix","windows"]
|
|
||||||
revision = "99f16d856c9836c42d24e7ab64ea72916925fa97"
|
|
||||||
|
|
||||||
[solve-meta]
|
|
||||||
analyzer-name = "dep"
|
|
||||||
analyzer-version = 1
|
|
||||||
inputs-digest = "cf4dacc32111b22d72ac23189b826c8316ec265e55bf987338c7a00633af788e"
|
|
||||||
solver-name = "gps-cdcl"
|
|
||||||
solver-version = 1
|
|
||||||
47
Gopkg.toml
47
Gopkg.toml
@@ -1,47 +0,0 @@
|
|||||||
|
|
||||||
# Gopkg.toml example
|
|
||||||
#
|
|
||||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
|
||||||
# for detailed Gopkg.toml documentation.
|
|
||||||
#
|
|
||||||
# required = ["github.com/user/thing/cmd/thing"]
|
|
||||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
|
||||||
#
|
|
||||||
# [[constraint]]
|
|
||||||
# name = "github.com/user/project"
|
|
||||||
# version = "1.0.0"
|
|
||||||
#
|
|
||||||
# [[constraint]]
|
|
||||||
# name = "github.com/user/project2"
|
|
||||||
# branch = "dev"
|
|
||||||
# source = "github.com/myfork/project2"
|
|
||||||
#
|
|
||||||
# [[override]]
|
|
||||||
# name = "github.com/x/y"
|
|
||||||
# version = "2.4.0"
|
|
||||||
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/fsouza/go-dockerclient"
|
|
||||||
revision = "318513eb1ab27495afbc67f671ba1080513d8aa0"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "barchart-numfmt"
|
|
||||||
name = "github.com/gizak/termui"
|
|
||||||
source = "https://github.com/bcicen/termui"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/jgautheron/codename-generator"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/nu7hatch/gouuid"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/op/go-logging"
|
|
||||||
version = "1.0.0"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/opencontainers/runc"
|
|
||||||
version = "0.1.1"
|
|
||||||
11
Makefile
11
Makefile
@@ -8,7 +8,7 @@ clean:
|
|||||||
rm -rf _build/ release/
|
rm -rf _build/ release/
|
||||||
|
|
||||||
build:
|
build:
|
||||||
dep ensure
|
go mod download
|
||||||
CGO_ENABLED=0 go build -tags release -ldflags $(LD_FLAGS) -o ctop
|
CGO_ENABLED=0 go build -tags release -ldflags $(LD_FLAGS) -o ctop
|
||||||
|
|
||||||
build-dev:
|
build-dev:
|
||||||
@@ -16,10 +16,11 @@ build-dev:
|
|||||||
|
|
||||||
build-all:
|
build-all:
|
||||||
mkdir -p _build
|
mkdir -p _build
|
||||||
GOOS=darwin GOARCH=amd64 go build -tags release -ldflags $(LD_FLAGS) -o _build/ctop-$(VERSION)-darwin-amd64
|
GOOS=darwin GOARCH=amd64 go build -tags release -ldflags $(LD_FLAGS) -o _build/ctop-$(VERSION)-darwin-amd64
|
||||||
GOOS=linux GOARCH=amd64 go build -tags release -ldflags $(LD_FLAGS) -o _build/ctop-$(VERSION)-linux-amd64
|
GOOS=linux GOARCH=amd64 go build -tags release -ldflags $(LD_FLAGS) -o _build/ctop-$(VERSION)-linux-amd64
|
||||||
GOOS=linux GOARCH=arm go build -tags release -ldflags $(LD_FLAGS) -o _build/ctop-$(VERSION)-linux-arm
|
GOOS=linux GOARCH=arm go build -tags release -ldflags $(LD_FLAGS) -o _build/ctop-$(VERSION)-linux-arm
|
||||||
GOOS=linux GOARCH=arm64 go build -tags release -ldflags $(LD_FLAGS) -o _build/ctop-$(VERSION)-linux-arm64
|
GOOS=linux GOARCH=arm64 go build -tags release -ldflags $(LD_FLAGS) -o _build/ctop-$(VERSION)-linux-arm64
|
||||||
|
GOOS=windows GOARCH=amd64 go build -tags release -ldflags $(LD_FLAGS) -o _build/ctop-$(VERSION)-windows-amd64
|
||||||
cd _build; sha256sum * > sha256sums.txt
|
cd _build; sha256sum * > sha256sums.txt
|
||||||
|
|
||||||
image:
|
image:
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Fetch the [latest release](https://github.com/bcicen/ctop/releases) for your pla
|
|||||||
#### Linux
|
#### Linux
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo wget https://github.com/bcicen/ctop/releases/download/v0.7/ctop-0.7-linux-amd64 -O /usr/local/bin/ctop
|
sudo wget https://github.com/bcicen/ctop/releases/download/v0.7.1/ctop-0.7.1-linux-amd64 -O /usr/local/bin/ctop
|
||||||
sudo chmod +x /usr/local/bin/ctop
|
sudo chmod +x /usr/local/bin/ctop
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ brew install ctop
|
|||||||
```
|
```
|
||||||
or
|
or
|
||||||
```bash
|
```bash
|
||||||
sudo curl -Lo /usr/local/bin/ctop https://github.com/bcicen/ctop/releases/download/v0.7/ctop-0.7-darwin-amd64
|
sudo curl -Lo /usr/local/bin/ctop https://github.com/bcicen/ctop/releases/download/v0.7.1/ctop-0.7.1-darwin-amd64
|
||||||
sudo chmod +x /usr/local/bin/ctop
|
sudo chmod +x /usr/local/bin/ctop
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ While running, use `S` to save the current filters, sort field, and other option
|
|||||||
Option | Description
|
Option | Description
|
||||||
--- | ---
|
--- | ---
|
||||||
-a | show active containers only
|
-a | show active containers only
|
||||||
-f <string> | set an initial filter string
|
-f \<string\> | set an initial filter string
|
||||||
-h | display help dialog
|
-h | display help dialog
|
||||||
-i | invert default colors
|
-i | invert default colors
|
||||||
-r | reverse container sort order
|
-r | reverse container sort order
|
||||||
@@ -75,7 +75,7 @@ Option | Description
|
|||||||
|
|
||||||
Key | Action
|
Key | Action
|
||||||
--- | ---
|
--- | ---
|
||||||
<enter> | Open container menu
|
\<enter\> | Open container menu
|
||||||
a | Toggle display of all (running and non-running) containers
|
a | Toggle display of all (running and non-running) containers
|
||||||
f | Filter displayed containers (`esc` to clear when open)
|
f | Filter displayed containers (`esc` to clear when open)
|
||||||
H | Toggle ctop header
|
H | Toggle ctop header
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ var ColorMap = map[string]ui.Attribute{
|
|||||||
|
|
||||||
func InvertColorMap() {
|
func InvertColorMap() {
|
||||||
re := regexp.MustCompile(".*.fg")
|
re := regexp.MustCompile(".*.fg")
|
||||||
for k, _ := range ColorMap {
|
for k := range ColorMap {
|
||||||
if re.FindAllString(k, 1) != nil {
|
if re.FindAllString(k, 1) != nil {
|
||||||
ColorMap[k] = ui.ColorBlack
|
ColorMap[k] = ui.ColorBlack
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ var (
|
|||||||
xdgRe = regexp.MustCompile("^XDG_*")
|
xdgRe = regexp.MustCompile("^XDG_*")
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigFile struct {
|
type File struct {
|
||||||
Options map[string]string `toml:"options"`
|
Options map[string]string `toml:"options"`
|
||||||
Toggles map[string]bool `toml:"toggles"`
|
Toggles map[string]bool `toml:"toggles"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func exportConfig() ConfigFile {
|
func exportConfig() File {
|
||||||
c := ConfigFile{
|
c := File{
|
||||||
Options: make(map[string]string),
|
Options: make(map[string]string),
|
||||||
Toggles: make(map[string]bool),
|
Toggles: make(map[string]bool),
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@ func exportConfig() ConfigFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Read() error {
|
func Read() error {
|
||||||
var config ConfigFile
|
var config File
|
||||||
|
|
||||||
path, err := getConfigPath()
|
path, err := getConfigPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ var params = []*Param{
|
|||||||
Val: "state",
|
Val: "state",
|
||||||
Label: "Container Sort Field",
|
Label: "Container Sort Field",
|
||||||
},
|
},
|
||||||
|
&Param{
|
||||||
|
Key: "namespace",
|
||||||
|
Val: "state",
|
||||||
|
Label: "Kubernetes namespace for monitoring",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
type Param struct {
|
type Param struct {
|
||||||
|
|||||||
@@ -5,17 +5,22 @@ var switches = []*Switch{
|
|||||||
&Switch{
|
&Switch{
|
||||||
Key: "sortReversed",
|
Key: "sortReversed",
|
||||||
Val: false,
|
Val: false,
|
||||||
Label: "Reverse Sort Order",
|
Label: "Reverse sort order",
|
||||||
},
|
},
|
||||||
&Switch{
|
&Switch{
|
||||||
Key: "allContainers",
|
Key: "allContainers",
|
||||||
Val: true,
|
Val: true,
|
||||||
Label: "Show All Containers",
|
Label: "Show all containers",
|
||||||
|
},
|
||||||
|
&Switch{
|
||||||
|
Key: "fullRowCursor",
|
||||||
|
Val: true,
|
||||||
|
Label: "Highlight entire cursor row (vs. name only)",
|
||||||
},
|
},
|
||||||
&Switch{
|
&Switch{
|
||||||
Key: "enableHeader",
|
Key: "enableHeader",
|
||||||
Val: true,
|
Val: true,
|
||||||
Label: "Enable Status Header",
|
Label: "Enable status header",
|
||||||
},
|
},
|
||||||
&Switch{
|
&Switch{
|
||||||
Key: "scaleCpu",
|
Key: "scaleCpu",
|
||||||
@@ -56,7 +61,7 @@ func UpdateSwitch(k string, val bool) {
|
|||||||
// Toggle a boolean switch
|
// Toggle a boolean switch
|
||||||
func Toggle(k string) {
|
func Toggle(k string) {
|
||||||
sw := GetSwitch(k)
|
sw := GetSwitch(k)
|
||||||
newVal := sw.Val != true
|
newVal := !sw.Val
|
||||||
log.Noticef("config change: %s: %t -> %t", k, sw.Val, newVal)
|
log.Noticef("config change: %s: %t -> %t", k, sw.Val, newVal)
|
||||||
sw.Val = newVal
|
sw.Val = newVal
|
||||||
//log.Errorf("ignoring toggle for non-existant switch: %s", k)
|
//log.Errorf("ignoring toggle for non-existant switch: %s", k)
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func (l *DockerLogs) Stream() chan models.Log {
|
|||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
parts := strings.Split(scanner.Text(), " ")
|
parts := strings.Split(scanner.Text(), " ")
|
||||||
ts := l.parseTime(parts[0])
|
ts := l.parseTime(parts[0])
|
||||||
logCh <- models.Log{ts, strings.Join(parts[1:], " ")}
|
logCh <- models.Log{Timestamp: ts, Message: strings.Join(parts[1:], " ")}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -62,10 +62,8 @@ func (l *DockerLogs) Stream() chan models.Log {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
select {
|
<-l.done
|
||||||
case <-l.done:
|
cancel()
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
log.Infof("log reader started for container: %s", l.id)
|
log.Infof("log reader started for container: %s", l.id)
|
||||||
|
|||||||
128
connector/collector/kubernetes.go
Normal file
128
connector/collector/kubernetes.go
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"k8s.io/metrics/pkg/apis/metrics/v1alpha1"
|
||||||
|
clientset "k8s.io/metrics/pkg/client/clientset/versioned"
|
||||||
|
|
||||||
|
"github.com/bcicen/ctop/config"
|
||||||
|
"github.com/bcicen/ctop/models"
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Kubernetes collector
|
||||||
|
type Kubernetes struct {
|
||||||
|
models.Metrics
|
||||||
|
name string
|
||||||
|
client clientset.Interface
|
||||||
|
clientset *kubernetes.Clientset
|
||||||
|
running bool
|
||||||
|
stream chan models.Metrics
|
||||||
|
done chan bool
|
||||||
|
lastCpu float64
|
||||||
|
lastSysCpu float64
|
||||||
|
scaleCpu bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKubernetes(client *kubernetes.Clientset, name string) *Kubernetes {
|
||||||
|
return &Kubernetes{
|
||||||
|
Metrics: models.Metrics{},
|
||||||
|
name: name,
|
||||||
|
client: clientset.New(client.RESTClient()),
|
||||||
|
clientset: client,
|
||||||
|
scaleCpu: config.GetSwitchVal("scaleCpu"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Kubernetes) Start() {
|
||||||
|
k.done = make(chan bool)
|
||||||
|
k.stream = make(chan models.Metrics)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
k.running = false
|
||||||
|
for {
|
||||||
|
|
||||||
|
result := &v1alpha1.PodMetrics{}
|
||||||
|
err := k.clientset.RESTClient().Get().AbsPath("/api/v1/namespaces/kube-system/services/http:heapster:/proxy/apis/metrics/v1alpha1/namespaces/" + config.GetVal("namespace") + "/pods/" + k.name).Do().Into(result)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("has error %s here %s", k.name, err.Error())
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
k.ReadCPU(result)
|
||||||
|
k.ReadMem(result)
|
||||||
|
k.stream <- k.Metrics
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
k.running = true
|
||||||
|
log.Infof("collector started for container: %s", k.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Kubernetes) Running() bool {
|
||||||
|
return c.running
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Kubernetes) Stream() chan models.Metrics {
|
||||||
|
return c.stream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Kubernetes) Logs() LogCollector {
|
||||||
|
return NewKubernetesLogs(c.name, c.clientset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop collector
|
||||||
|
func (c *Kubernetes) Stop() {
|
||||||
|
c.done <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Kubernetes) ReadCPU(metrics *v1alpha1.PodMetrics) {
|
||||||
|
all := int64(0)
|
||||||
|
for _, c := range metrics.Containers {
|
||||||
|
v := c.Usage[v1.ResourceCPU]
|
||||||
|
all += v.Value()
|
||||||
|
}
|
||||||
|
if all != 0 {
|
||||||
|
k.CPUUtil = round(float64(all))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Kubernetes) ReadMem(metrics *v1alpha1.PodMetrics) {
|
||||||
|
all := int64(0)
|
||||||
|
for _, c := range metrics.Containers {
|
||||||
|
v := c.Usage[v1.ResourceMemory]
|
||||||
|
a, ok := v.AsInt64()
|
||||||
|
if ok {
|
||||||
|
all += a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
k.MemUsage = all
|
||||||
|
k.MemLimit = int64(0)
|
||||||
|
//k.MemPercent = percent(float64(k.MemUsage), float64(k.MemLimit))
|
||||||
|
}
|
||||||
|
|
||||||
|
//func (c *Kubernetes) ReadNet(stats *api.Stats) {
|
||||||
|
// var rx, tx int64
|
||||||
|
// for _, network := range stats.Networks {
|
||||||
|
// rx += int64(network.RxBytes)
|
||||||
|
// tx += int64(network.TxBytes)
|
||||||
|
// }
|
||||||
|
// c.NetRx, c.NetTx = rx, tx
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func (c *Kubernetes) ReadIO(stats *api.Stats) {
|
||||||
|
// var read, write int64
|
||||||
|
// for _, blk := range stats.BlkioStats.IOServiceBytesRecursive {
|
||||||
|
// if blk.Op == "Read" {
|
||||||
|
// read = int64(blk.Value)
|
||||||
|
// }
|
||||||
|
// if blk.Op == "Write" {
|
||||||
|
// write = int64(blk.Value)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// c.IOBytesRead, c.IOBytesWrite = read, write
|
||||||
|
//}
|
||||||
78
connector/collector/kubernetes_logs.go
Normal file
78
connector/collector/kubernetes_logs.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bcicen/ctop/models"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KubernetesLogs struct {
|
||||||
|
id string
|
||||||
|
client *kubernetes.Clientset
|
||||||
|
done chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKubernetesLogs(id string, client *kubernetes.Clientset) *KubernetesLogs {
|
||||||
|
return &KubernetesLogs{
|
||||||
|
id: id,
|
||||||
|
client: client,
|
||||||
|
done: make(chan bool),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *KubernetesLogs) Stream() chan models.Log {
|
||||||
|
//r, w := io.Pipe()
|
||||||
|
logCh := make(chan models.Log)
|
||||||
|
//ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
//opts := api.LogsOptions{
|
||||||
|
// Context: ctx,
|
||||||
|
// Container: l.id,
|
||||||
|
// OutputStream: w,
|
||||||
|
// ErrorStream: w,
|
||||||
|
// Stdout: true,
|
||||||
|
// Stderr: true,
|
||||||
|
// Tail: "10",
|
||||||
|
// Follow: true,
|
||||||
|
// Timestamps: true,
|
||||||
|
//}
|
||||||
|
|
||||||
|
//// read io pipe into channel
|
||||||
|
//go func() {
|
||||||
|
// scanner := bufio.NewScanner(r)
|
||||||
|
// for scanner.Scan() {
|
||||||
|
// parts := strings.Split(scanner.Text(), " ")
|
||||||
|
// ts := l.parseTime(parts[0])
|
||||||
|
// logCh <- models.Log{Timestamp: ts, Message: strings.Join(parts[1:], " ")}
|
||||||
|
// }
|
||||||
|
//}()
|
||||||
|
|
||||||
|
//// connect to container log stream
|
||||||
|
//go func() {
|
||||||
|
// err := l.client.Logs(opts)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Errorf("error reading container logs: %s", err)
|
||||||
|
// }
|
||||||
|
// log.Infof("log reader stopped for container: %s", l.id)
|
||||||
|
//}()
|
||||||
|
|
||||||
|
//go func() {
|
||||||
|
// <-l.done
|
||||||
|
// cancel()
|
||||||
|
//}()
|
||||||
|
|
||||||
|
log.Infof("log reader started for container: %s", l.id)
|
||||||
|
return logCh
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *KubernetesLogs) Stop() { l.done <- true }
|
||||||
|
|
||||||
|
func (l *KubernetesLogs) parseTime(s string) time.Time {
|
||||||
|
ts, err := time.Parse("2006-01-02T15:04:05.000000000Z", s)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to parse container log: %s", err)
|
||||||
|
ts = time.Now()
|
||||||
|
}
|
||||||
|
return ts
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ func (l *MockLogs) Stream() chan models.Log {
|
|||||||
case <-l.done:
|
case <-l.done:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
logCh <- models.Log{time.Now(), mockLog}
|
logCh <- models.Log{Timestamp: time.Now(), Message: mockLog}
|
||||||
time.Sleep(250 * time.Millisecond)
|
time.Sleep(250 * time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build !darwin
|
// +build linux
|
||||||
|
|
||||||
package collector
|
package collector
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build !darwin
|
// +build linux
|
||||||
|
|
||||||
package collector
|
package collector
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import (
|
|||||||
api "github.com/fsouza/go-dockerclient"
|
api "github.com/fsouza/go-dockerclient"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() { enabled["docker"] = NewDocker }
|
||||||
|
|
||||||
type Docker struct {
|
type Docker struct {
|
||||||
client *api.Client
|
client *api.Client
|
||||||
containers map[string]*container.Container
|
containers map[string]*container.Container
|
||||||
@@ -78,6 +80,17 @@ func portsFormat(ports map[api.Port][]api.PortBinding) string {
|
|||||||
return strings.Join(append(exposed, published...), "\n")
|
return strings.Join(append(exposed, published...), "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ipsFormat(networks map[string]api.ContainerNetwork) string {
|
||||||
|
var ips []string
|
||||||
|
|
||||||
|
for k, v := range networks {
|
||||||
|
s := fmt.Sprintf("%s:%s", k, v.IPAddress)
|
||||||
|
ips = append(ips, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(ips, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
func (cm *Docker) refresh(c *container.Container) {
|
func (cm *Docker) refresh(c *container.Container) {
|
||||||
insp := cm.inspect(c.Id)
|
insp := cm.inspect(c.Id)
|
||||||
// remove container if no longer exists
|
// remove container if no longer exists
|
||||||
@@ -87,16 +100,20 @@ func (cm *Docker) refresh(c *container.Container) {
|
|||||||
}
|
}
|
||||||
c.SetMeta("name", shortName(insp.Name))
|
c.SetMeta("name", shortName(insp.Name))
|
||||||
c.SetMeta("image", insp.Config.Image)
|
c.SetMeta("image", insp.Config.Image)
|
||||||
|
c.SetMeta("IPs", ipsFormat(insp.NetworkSettings.Networks))
|
||||||
c.SetMeta("ports", portsFormat(insp.NetworkSettings.Ports))
|
c.SetMeta("ports", portsFormat(insp.NetworkSettings.Ports))
|
||||||
c.SetMeta("created", insp.Created.Format("Mon Jan 2 15:04:05 2006"))
|
c.SetMeta("created", insp.Created.Format("Mon Jan 2 15:04:05 2006"))
|
||||||
c.SetMeta("health", insp.State.Health.Status)
|
c.SetMeta("health", insp.State.Health.Status)
|
||||||
|
for _, env := range insp.Config.Env {
|
||||||
|
c.SetMeta("[ENV-VAR]", env)
|
||||||
|
}
|
||||||
c.SetState(insp.State.Status)
|
c.SetState(insp.State.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *Docker) inspect(id string) *api.Container {
|
func (cm *Docker) inspect(id string) *api.Container {
|
||||||
c, err := cm.client.InspectContainer(id)
|
c, err := cm.client.InspectContainer(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(*api.NoSuchContainer); ok == false {
|
if _, ok := err.(*api.NoSuchContainer); !ok {
|
||||||
log.Errorf(err.Error())
|
log.Errorf(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
// +build !linux
|
|
||||||
|
|
||||||
package connector
|
|
||||||
|
|
||||||
var enabled = map[string]func() Connector{
|
|
||||||
"docker": NewDocker,
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
// +build !darwin
|
|
||||||
|
|
||||||
package connector
|
|
||||||
|
|
||||||
var enabled = map[string]func() Connector{
|
|
||||||
"docker": NewDocker,
|
|
||||||
"runc": NewRunc,
|
|
||||||
}
|
|
||||||
218
connector/kubernetes.go
Normal file
218
connector/kubernetes.go
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
package connector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
bcfg "github.com/bcicen/ctop/config"
|
||||||
|
"github.com/bcicen/ctop/connector/collector"
|
||||||
|
"github.com/bcicen/ctop/connector/manager"
|
||||||
|
"github.com/bcicen/ctop/container"
|
||||||
|
api "github.com/fsouza/go-dockerclient"
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() { enabled["kubernetes"] = NewKubernetes }
|
||||||
|
|
||||||
|
type Kubernetes struct {
|
||||||
|
namespace string
|
||||||
|
clientset *kubernetes.Clientset
|
||||||
|
containers map[string]*container.Container
|
||||||
|
needsRefresh chan string // container IDs requiring refresh
|
||||||
|
lock sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKubernetes() Connector {
|
||||||
|
var kubeconfig string
|
||||||
|
//if home := homeDir(); home != "" {
|
||||||
|
// kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
|
||||||
|
//} else {
|
||||||
|
// kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
|
||||||
|
//}
|
||||||
|
//flag.Parse()
|
||||||
|
kubeconfig = filepath.Join(homeDir(), ".kube", "config")
|
||||||
|
|
||||||
|
// use the current context in kubeconfig
|
||||||
|
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the clientset
|
||||||
|
clientset, err := kubernetes.NewForConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// init docker client
|
||||||
|
k := &Kubernetes{
|
||||||
|
clientset: clientset,
|
||||||
|
containers: make(map[string]*container.Container),
|
||||||
|
needsRefresh: make(chan string, 60),
|
||||||
|
lock: sync.RWMutex{},
|
||||||
|
namespace: bcfg.GetVal("namespace"),
|
||||||
|
}
|
||||||
|
go k.Loop()
|
||||||
|
k.refreshAll()
|
||||||
|
go k.watchEvents()
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Kubernetes) watchEvents() {
|
||||||
|
for {
|
||||||
|
log.Info("kubernetes event listener starting")
|
||||||
|
allEvents, err := k.clientset.CoreV1().Events(k.namespace).List(metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range allEvents.Items {
|
||||||
|
if e.Kind != "pod" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
actionName := strings.Split(e.Action, ":")[0]
|
||||||
|
|
||||||
|
switch actionName {
|
||||||
|
case "start", "die", "pause", "unpause", "health_status":
|
||||||
|
log.Debugf("handling docker event: action=%s id=%s", e.Action, e.UID)
|
||||||
|
k.needsRefresh <- e.Name
|
||||||
|
case "destroy":
|
||||||
|
log.Debugf("handling docker event: action=%s id=%s", e.Action, e.UID)
|
||||||
|
k.delByID(e.Name)
|
||||||
|
default:
|
||||||
|
log.Debugf("handling docker event: %v", e)
|
||||||
|
k.needsRefresh <- e.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (k *Kubernetes) Loop() {
|
||||||
|
for id := range k.needsRefresh {
|
||||||
|
c := k.MustGet(id)
|
||||||
|
k.refresh(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a single container, creating one anew if not existing
|
||||||
|
func (k *Kubernetes) MustGet(name string) *container.Container {
|
||||||
|
c, ok := k.Get(name)
|
||||||
|
// append container struct for new containers
|
||||||
|
if !ok {
|
||||||
|
// create collector
|
||||||
|
collector := collector.NewKubernetes(k.clientset, name)
|
||||||
|
// create manager
|
||||||
|
manager := manager.NewKubernetes(k.clientset, name)
|
||||||
|
// create container
|
||||||
|
c = container.New(name, collector, manager)
|
||||||
|
k.lock.Lock()
|
||||||
|
k.containers[name] = c
|
||||||
|
k.lock.Unlock()
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Kubernetes) refresh(c *container.Container) {
|
||||||
|
insp := k.inspect(c.Id)
|
||||||
|
// remove container if no longer exists
|
||||||
|
if insp == nil {
|
||||||
|
k.delByID(c.Id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.SetMeta("name", insp.Name)
|
||||||
|
if len(insp.Spec.Containers) >= 1 {
|
||||||
|
c.SetMeta("image", insp.Spec.Containers[0].Image)
|
||||||
|
c.SetMeta("ports", k8sPort(insp.Spec.Containers[0].Ports))
|
||||||
|
for _, env := range insp.Spec.Containers[0].Env {
|
||||||
|
c.SetMeta("[ENV-VAR]", env.Name+"="+env.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.SetMeta("IPs", insp.Status.PodIP)
|
||||||
|
c.SetMeta("created", insp.CreationTimestamp.Format("Mon Jan 2 15:04:05 2006"))
|
||||||
|
c.SetMeta("health", string(insp.Status.Phase))
|
||||||
|
c.SetState("running")
|
||||||
|
}
|
||||||
|
|
||||||
|
func k8sPort(ports []v1.ContainerPort) string {
|
||||||
|
str := []string{}
|
||||||
|
for _, p := range ports {
|
||||||
|
str = append(str, fmt.Sprintf("%s:%d -> %d", p.HostIP, p.HostPort, p.ContainerPort))
|
||||||
|
}
|
||||||
|
return strings.Join(str, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Kubernetes) inspect(id string) *v1.Pod {
|
||||||
|
p, err := k.clientset.CoreV1().Pods(k.namespace).Get(id, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(*api.NoSuchContainer); !ok {
|
||||||
|
log.Errorf(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove containers by ID
|
||||||
|
func (k *Kubernetes) delByID(name string) {
|
||||||
|
k.lock.Lock()
|
||||||
|
delete(k.containers, name)
|
||||||
|
k.lock.Unlock()
|
||||||
|
log.Infof("removed dead container: %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Kubernetes) Get(name string) (c *container.Container, ok bool) {
|
||||||
|
k.lock.Lock()
|
||||||
|
c, ok = k.containers[name]
|
||||||
|
k.lock.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark all container IDs for refresh
|
||||||
|
func (k *Kubernetes) refreshAll() {
|
||||||
|
allPods, err := k.clientset.CoreV1().Pods(k.namespace).List(metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pod := range allPods.Items {
|
||||||
|
c := k.MustGet(pod.Name)
|
||||||
|
c.SetMeta("uid", string(pod.UID))
|
||||||
|
c.SetMeta("name", pod.Name)
|
||||||
|
if pod.Initializers != nil && pod.Initializers.Result != nil {
|
||||||
|
c.SetState(pod.Initializers.Result.Status)
|
||||||
|
} else {
|
||||||
|
c.SetState(string(pod.Status.Phase))
|
||||||
|
}
|
||||||
|
k.needsRefresh <- c.Id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Kubernetes) All() (containers container.Containers) {
|
||||||
|
k.lock.Lock()
|
||||||
|
for _, c := range k.containers {
|
||||||
|
containers = append(containers, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
containers.Sort()
|
||||||
|
containers.Filter()
|
||||||
|
k.lock.Unlock()
|
||||||
|
return containers
|
||||||
|
}
|
||||||
|
|
||||||
|
func homeDir() string {
|
||||||
|
if h := os.Getenv("HOME"); h != "" {
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
return os.Getenv("USERPROFILE") // windows
|
||||||
|
}
|
||||||
@@ -2,22 +2,31 @@ package connector
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/bcicen/ctop/container"
|
"github.com/bcicen/ctop/container"
|
||||||
"github.com/bcicen/ctop/logging"
|
"github.com/bcicen/ctop/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
var log = logging.Init()
|
var (
|
||||||
|
log = logging.Init()
|
||||||
|
enabled = make(map[string]func() Connector)
|
||||||
|
)
|
||||||
|
|
||||||
|
// return names for all enabled connectors on the current platform
|
||||||
|
func Enabled() (a []string) {
|
||||||
|
for k, _ := range enabled {
|
||||||
|
a = append(a, k)
|
||||||
|
}
|
||||||
|
sort.Strings(a)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
func ByName(s string) (Connector, error) {
|
func ByName(s string) (Connector, error) {
|
||||||
if _, ok := enabled[s]; !ok {
|
if cfn, ok := enabled[s]; ok {
|
||||||
msg := fmt.Sprintf("invalid connector type \"%s\"\nconnector must be one of:", s)
|
return cfn(), nil
|
||||||
for k, _ := range enabled {
|
|
||||||
msg += fmt.Sprintf("\n %s", k)
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf(msg)
|
|
||||||
}
|
}
|
||||||
return enabled[s](), nil
|
return nil, fmt.Errorf("invalid connector type \"%s\"", s)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Connector interface {
|
type Connector interface {
|
||||||
|
|||||||
@@ -42,3 +42,24 @@ func (dc *Docker) Remove() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dc *Docker) Pause() error {
|
||||||
|
if err := dc.client.PauseContainer(dc.id); err != nil {
|
||||||
|
return fmt.Errorf("cannot pause container: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *Docker) Unpause() error {
|
||||||
|
if err := dc.client.UnpauseContainer(dc.id); err != nil {
|
||||||
|
return fmt.Errorf("cannot unpause container: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *Docker) Restart() error {
|
||||||
|
if err := dc.client.RestartContainer(dc.id, 3); err != nil {
|
||||||
|
return fmt.Errorf("cannot restart container: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
64
connector/manager/kubernetes.go
Normal file
64
connector/manager/kubernetes.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package manager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Kubernetes struct {
|
||||||
|
id string
|
||||||
|
client *kubernetes.Clientset
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKubernetes(client *kubernetes.Clientset, id string) *Kubernetes {
|
||||||
|
return &Kubernetes{
|
||||||
|
id: id,
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *Kubernetes) Start() error {
|
||||||
|
//c, err := dc.client.InspectContainer(dc.id)
|
||||||
|
//if err != nil {
|
||||||
|
// return fmt.Errorf("cannot inspect container: %v", err)
|
||||||
|
//}
|
||||||
|
|
||||||
|
//if err := dc.client.StartContainer(c.ID, c.HostConfig); err != nil {
|
||||||
|
// return fmt.Errorf("cannot start container: %v", err)
|
||||||
|
//}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *Kubernetes) Stop() error {
|
||||||
|
//if err := dc.client.StopContainer(dc.id, 3); err != nil {
|
||||||
|
// return fmt.Errorf("cannot stop container: %v", err)
|
||||||
|
//}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *Kubernetes) Remove() error {
|
||||||
|
//if err := dc.client.RemoveContainer(api.RemoveContainerOptions{ID: dc.id}); err != nil {
|
||||||
|
// return fmt.Errorf("cannot remove container: %v", err)
|
||||||
|
//}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *Kubernetes) Pause() error {
|
||||||
|
//if err := dc.client.PauseContainer(dc.id); err != nil {
|
||||||
|
// return fmt.Errorf("cannot pause container: %v", err)
|
||||||
|
//}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *Kubernetes) Unpause() error {
|
||||||
|
//if err := dc.client.UnpauseContainer(dc.id); err != nil {
|
||||||
|
// return fmt.Errorf("cannot unpause container: %v", err)
|
||||||
|
//}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *Kubernetes) Restart() error {
|
||||||
|
//if err := dc.client.RestartContainer(dc.id, 3); err != nil {
|
||||||
|
// return fmt.Errorf("cannot restart container: %v", err)
|
||||||
|
//}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -4,4 +4,7 @@ type Manager interface {
|
|||||||
Start() error
|
Start() error
|
||||||
Stop() error
|
Stop() error
|
||||||
Remove() error
|
Remove() error
|
||||||
|
Pause() error
|
||||||
|
Unpause() error
|
||||||
|
Restart() error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,3 +17,15 @@ func (m *Mock) Stop() error {
|
|||||||
func (m *Mock) Remove() error {
|
func (m *Mock) Remove() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Mock) Pause() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mock) Unpause() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mock) Restart() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,3 +17,15 @@ func (rc *Runc) Stop() error {
|
|||||||
func (rc *Runc) Remove() error {
|
func (rc *Runc) Remove() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rc *Runc) Pause() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *Runc) Unpause() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *Runc) Restart() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,11 +14,13 @@ import (
|
|||||||
"github.com/nu7hatch/gouuid"
|
"github.com/nu7hatch/gouuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() { enabled["mock"] = NewMock }
|
||||||
|
|
||||||
type Mock struct {
|
type Mock struct {
|
||||||
containers container.Containers
|
containers container.Containers
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMock() *Mock {
|
func NewMock() Connector {
|
||||||
cs := &Mock{}
|
cs := &Mock{}
|
||||||
go cs.Init()
|
go cs.Init()
|
||||||
go cs.Loop()
|
go cs.Loop()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build !darwin
|
// +build linux
|
||||||
|
|
||||||
package connector
|
package connector
|
||||||
|
|
||||||
@@ -17,6 +17,8 @@ import (
|
|||||||
"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
|
"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() { enabled["runc"] = NewRunc }
|
||||||
|
|
||||||
type RuncOpts struct {
|
type RuncOpts struct {
|
||||||
root string // runc root path
|
root string // runc root path
|
||||||
systemdCgroups bool // use systemd cgroups
|
systemdCgroups bool // use systemd cgroups
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ var (
|
|||||||
log = logging.Init()
|
log = logging.Init()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
running = "running"
|
||||||
|
)
|
||||||
|
|
||||||
// Metrics and metadata representing a container
|
// Metrics and metadata representing a container
|
||||||
type Container struct {
|
type Container struct {
|
||||||
models.Metrics
|
models.Metrics
|
||||||
@@ -60,12 +64,12 @@ func (c *Container) GetMeta(k string) string {
|
|||||||
func (c *Container) SetState(s string) {
|
func (c *Container) SetState(s string) {
|
||||||
c.SetMeta("state", s)
|
c.SetMeta("state", s)
|
||||||
// start collector, if needed
|
// start collector, if needed
|
||||||
if s == "running" && !c.collector.Running() {
|
if s == running && !c.collector.Running() {
|
||||||
c.collector.Start()
|
c.collector.Start()
|
||||||
c.Read(c.collector.Stream())
|
c.Read(c.collector.Stream())
|
||||||
}
|
}
|
||||||
// stop collector, if needed
|
// stop collector, if needed
|
||||||
if s != "running" && c.collector.Running() {
|
if s != running && c.collector.Running() {
|
||||||
c.collector.Stop()
|
c.collector.Stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,18 +94,18 @@ func (c *Container) Read(stream chan models.Metrics) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) Start() {
|
func (c *Container) Start() {
|
||||||
if c.Meta["state"] != "running" {
|
if c.Meta["state"] != running {
|
||||||
if err := c.manager.Start(); err != nil {
|
if err := c.manager.Start(); err != nil {
|
||||||
log.Warningf("container %s: %v", c.Id, err)
|
log.Warningf("container %s: %v", c.Id, err)
|
||||||
log.StatusErr(err)
|
log.StatusErr(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.SetState("running")
|
c.SetState(running)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) Stop() {
|
func (c *Container) Stop() {
|
||||||
if c.Meta["state"] == "running" {
|
if c.Meta["state"] == running {
|
||||||
if err := c.manager.Stop(); err != nil {
|
if err := c.manager.Stop(); err != nil {
|
||||||
log.Warningf("container %s: %v", c.Id, err)
|
log.Warningf("container %s: %v", c.Id, err)
|
||||||
log.StatusErr(err)
|
log.StatusErr(err)
|
||||||
@@ -117,3 +121,35 @@ func (c *Container) Remove() {
|
|||||||
log.StatusErr(err)
|
log.StatusErr(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Container) Pause() {
|
||||||
|
if c.Meta["state"] == running {
|
||||||
|
if err := c.manager.Pause(); err != nil {
|
||||||
|
log.Warningf("container %s: %v", c.Id, err)
|
||||||
|
log.StatusErr(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.SetState("paused")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) Unpause() {
|
||||||
|
if c.Meta["state"] == "paused" {
|
||||||
|
if err := c.manager.Unpause(); err != nil {
|
||||||
|
log.Warningf("container %s: %v", c.Id, err)
|
||||||
|
log.StatusErr(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.SetState(running)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) Restart() {
|
||||||
|
if c.Meta["state"] == running {
|
||||||
|
if err := c.manager.Restart(); err != nil {
|
||||||
|
log.Warningf("container %s: %v", c.Id, err)
|
||||||
|
log.StatusErr(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
20
cursor.go
20
cursor.go
@@ -57,11 +57,11 @@ func (gc *GridCursor) RefreshContainers() (lenChanged bool) {
|
|||||||
// Set an initial cursor position, if possible
|
// Set an initial cursor position, if possible
|
||||||
func (gc *GridCursor) Reset() {
|
func (gc *GridCursor) Reset() {
|
||||||
for _, c := range gc.cSource.All() {
|
for _, c := range gc.cSource.All() {
|
||||||
c.Widgets.Name.UnHighlight()
|
c.Widgets.UnHighlight()
|
||||||
}
|
}
|
||||||
if gc.Len() > 0 {
|
if gc.Len() > 0 {
|
||||||
gc.selectedID = gc.filtered[0].Id
|
gc.selectedID = gc.filtered[0].Id
|
||||||
gc.filtered[0].Widgets.Name.Highlight()
|
gc.filtered[0].Widgets.Highlight()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,9 +109,9 @@ func (gc *GridCursor) Up() {
|
|||||||
active := gc.filtered[idx]
|
active := gc.filtered[idx]
|
||||||
next := gc.filtered[idx-1]
|
next := gc.filtered[idx-1]
|
||||||
|
|
||||||
active.Widgets.Name.UnHighlight()
|
active.Widgets.UnHighlight()
|
||||||
gc.selectedID = next.Id
|
gc.selectedID = next.Id
|
||||||
next.Widgets.Name.Highlight()
|
next.Widgets.Highlight()
|
||||||
|
|
||||||
gc.ScrollPage()
|
gc.ScrollPage()
|
||||||
ui.Render(cGrid)
|
ui.Render(cGrid)
|
||||||
@@ -128,9 +128,9 @@ func (gc *GridCursor) Down() {
|
|||||||
active := gc.filtered[idx]
|
active := gc.filtered[idx]
|
||||||
next := gc.filtered[idx+1]
|
next := gc.filtered[idx+1]
|
||||||
|
|
||||||
active.Widgets.Name.UnHighlight()
|
active.Widgets.UnHighlight()
|
||||||
gc.selectedID = next.Id
|
gc.selectedID = next.Id
|
||||||
next.Widgets.Name.Highlight()
|
next.Widgets.Highlight()
|
||||||
|
|
||||||
gc.ScrollPage()
|
gc.ScrollPage()
|
||||||
ui.Render(cGrid)
|
ui.Render(cGrid)
|
||||||
@@ -151,9 +151,9 @@ func (gc *GridCursor) PgUp() {
|
|||||||
active := gc.filtered[idx]
|
active := gc.filtered[idx]
|
||||||
next := gc.filtered[nextidx]
|
next := gc.filtered[nextidx]
|
||||||
|
|
||||||
active.Widgets.Name.UnHighlight()
|
active.Widgets.UnHighlight()
|
||||||
gc.selectedID = next.Id
|
gc.selectedID = next.Id
|
||||||
next.Widgets.Name.Highlight()
|
next.Widgets.Highlight()
|
||||||
|
|
||||||
cGrid.Align()
|
cGrid.Align()
|
||||||
ui.Render(cGrid)
|
ui.Render(cGrid)
|
||||||
@@ -174,9 +174,9 @@ func (gc *GridCursor) PgDown() {
|
|||||||
active := gc.filtered[idx]
|
active := gc.filtered[idx]
|
||||||
next := gc.filtered[nextidx]
|
next := gc.filtered[nextidx]
|
||||||
|
|
||||||
active.Widgets.Name.UnHighlight()
|
active.Widgets.UnHighlight()
|
||||||
gc.selectedID = next.Id
|
gc.selectedID = next.Id
|
||||||
next.Widgets.Name.Highlight()
|
next.Widgets.Highlight()
|
||||||
|
|
||||||
cGrid.Align()
|
cGrid.Align()
|
||||||
ui.Render(cGrid)
|
ui.Render(cGrid)
|
||||||
|
|||||||
@@ -23,6 +23,16 @@ func (w *GaugeCol) Reset() {
|
|||||||
w.Percent = 0
|
w.Percent = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *GaugeCol) Highlight() {
|
||||||
|
w.Bg = ui.ThemeAttr("par.text.fg")
|
||||||
|
w.PercentColor = ui.ThemeAttr("par.text.hi")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *GaugeCol) UnHighlight() {
|
||||||
|
w.Bg = ui.ThemeAttr("par.text.bg")
|
||||||
|
w.PercentColor = ui.ThemeAttr("par.text.bg")
|
||||||
|
}
|
||||||
|
|
||||||
func colorScale(n int) ui.Attribute {
|
func colorScale(n int) ui.Attribute {
|
||||||
if n > 70 {
|
if n > 70 {
|
||||||
return ui.ThemeAttr("status.danger")
|
return ui.ThemeAttr("status.danger")
|
||||||
|
|||||||
@@ -23,10 +23,7 @@ func NewCompactGrid() *CompactGrid {
|
|||||||
func (cg *CompactGrid) Align() {
|
func (cg *CompactGrid) Align() {
|
||||||
y := cg.Y
|
y := cg.Y
|
||||||
|
|
||||||
if cg.Offset >= len(cg.Rows) {
|
if cg.Offset >= len(cg.Rows) || cg.Offset < 0 {
|
||||||
cg.Offset = 0
|
|
||||||
}
|
|
||||||
if cg.Offset < 0 {
|
|
||||||
cg.Offset = 0
|
cg.Offset = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +57,5 @@ func (cg *CompactGrid) Buffer() ui.Buffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cg *CompactGrid) AddRows(rows ...ui.GridBufferer) {
|
func (cg *CompactGrid) AddRows(rows ...ui.GridBufferer) {
|
||||||
for _, r := range rows {
|
cg.Rows = append(cg.Rows, rows...)
|
||||||
cg.Rows = append(cg.Rows, r)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package compact
|
package compact
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/bcicen/ctop/config"
|
||||||
"github.com/bcicen/ctop/logging"
|
"github.com/bcicen/ctop/logging"
|
||||||
"github.com/bcicen/ctop/models"
|
"github.com/bcicen/ctop/models"
|
||||||
ui "github.com/gizak/termui"
|
ui "github.com/gizak/termui"
|
||||||
@@ -17,6 +18,7 @@ type Compact struct {
|
|||||||
Net *TextCol
|
Net *TextCol
|
||||||
IO *TextCol
|
IO *TextCol
|
||||||
Pids *TextCol
|
Pids *TextCol
|
||||||
|
Bg *RowBg
|
||||||
X, Y int
|
X, Y int
|
||||||
Width int
|
Width int
|
||||||
Height int
|
Height int
|
||||||
@@ -36,6 +38,7 @@ func NewCompact(id string) *Compact {
|
|||||||
Net: NewTextCol("-"),
|
Net: NewTextCol("-"),
|
||||||
IO: NewTextCol("-"),
|
IO: NewTextCol("-"),
|
||||||
Pids: NewTextCol("-"),
|
Pids: NewTextCol("-"),
|
||||||
|
Bg: NewRowBg(),
|
||||||
X: 1,
|
X: 1,
|
||||||
Height: 1,
|
Height: 1,
|
||||||
}
|
}
|
||||||
@@ -90,6 +93,8 @@ func (row *Compact) SetY(y int) {
|
|||||||
if y == row.Y {
|
if y == row.Y {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
row.Bg.Y = y
|
||||||
for _, col := range row.all() {
|
for _, col := range row.all() {
|
||||||
col.SetY(y)
|
col.SetY(y)
|
||||||
}
|
}
|
||||||
@@ -101,6 +106,10 @@ func (row *Compact) SetWidth(width int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
x := row.X
|
x := row.X
|
||||||
|
|
||||||
|
row.Bg.SetX(x + colWidths[0] + 1)
|
||||||
|
row.Bg.SetWidth(width)
|
||||||
|
|
||||||
autoWidth := calcWidth(width)
|
autoWidth := calcWidth(width)
|
||||||
for n, col := range row.all() {
|
for n, col := range row.all() {
|
||||||
if colWidths[n] != 0 {
|
if colWidths[n] != 0 {
|
||||||
@@ -119,6 +128,7 @@ func (row *Compact) SetWidth(width int) {
|
|||||||
func (row *Compact) Buffer() ui.Buffer {
|
func (row *Compact) Buffer() ui.Buffer {
|
||||||
buf := ui.NewBuffer()
|
buf := ui.NewBuffer()
|
||||||
|
|
||||||
|
buf.Merge(row.Bg.Buffer())
|
||||||
buf.Merge(row.Status.Buffer())
|
buf.Merge(row.Status.Buffer())
|
||||||
buf.Merge(row.Name.Buffer())
|
buf.Merge(row.Name.Buffer())
|
||||||
buf.Merge(row.Cid.Buffer())
|
buf.Merge(row.Cid.Buffer())
|
||||||
@@ -142,3 +152,44 @@ func (row *Compact) all() []ui.GridBufferer {
|
|||||||
row.Pids,
|
row.Pids,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (row *Compact) Highlight() {
|
||||||
|
row.Name.Highlight()
|
||||||
|
if config.GetSwitchVal("fullRowCursor") {
|
||||||
|
row.Bg.Highlight()
|
||||||
|
row.Cid.Highlight()
|
||||||
|
row.Cpu.Highlight()
|
||||||
|
row.Mem.Highlight()
|
||||||
|
row.Net.Highlight()
|
||||||
|
row.IO.Highlight()
|
||||||
|
row.Pids.Highlight()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (row *Compact) UnHighlight() {
|
||||||
|
row.Name.UnHighlight()
|
||||||
|
if config.GetSwitchVal("fullRowCursor") {
|
||||||
|
row.Bg.UnHighlight()
|
||||||
|
row.Cid.UnHighlight()
|
||||||
|
row.Cpu.UnHighlight()
|
||||||
|
row.Mem.UnHighlight()
|
||||||
|
row.Net.UnHighlight()
|
||||||
|
row.IO.UnHighlight()
|
||||||
|
row.Pids.UnHighlight()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RowBg struct {
|
||||||
|
*ui.Par
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRowBg() *RowBg {
|
||||||
|
bg := ui.NewPar("")
|
||||||
|
bg.Height = 1
|
||||||
|
bg.Border = false
|
||||||
|
bg.Bg = ui.ThemeAttr("par.text.bg")
|
||||||
|
return &RowBg{bg}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *RowBg) Highlight() { w.Bg = ui.ThemeAttr("par.text.fg") }
|
||||||
|
func (w *RowBg) UnHighlight() { w.Bg = ui.ThemeAttr("par.text.bg") }
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func (row *Compact) SetIO(read int64, write int64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (row *Compact) SetPids(val int) {
|
func (row *Compact) SetPids(val int) {
|
||||||
label := fmt.Sprintf("%s", strconv.Itoa(val))
|
label := strconv.Itoa(val)
|
||||||
row.Pids.Set(label)
|
row.Pids.Set(label)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,11 +67,11 @@ func (s *Status) SetHealth(val string) {
|
|||||||
color := ui.ColorDefault
|
color := ui.ColorDefault
|
||||||
|
|
||||||
switch val {
|
switch val {
|
||||||
case "healthy":
|
case "healthy", "Succeeded":
|
||||||
color = ui.ThemeAttr("status.ok")
|
color = ui.ThemeAttr("status.ok")
|
||||||
case "unhealthy":
|
case "unhealthy", "Failed", "Unknown":
|
||||||
color = ui.ThemeAttr("status.danger")
|
color = ui.ThemeAttr("status.danger")
|
||||||
case "starting":
|
case "starting", "Pending", "Running":
|
||||||
color = ui.ThemeAttr("status.warn")
|
color = ui.ThemeAttr("status.warn")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,11 +17,13 @@ func NewTextCol(s string) *TextCol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *TextCol) Highlight() {
|
func (w *TextCol) Highlight() {
|
||||||
|
w.Bg = ui.ThemeAttr("par.text.fg")
|
||||||
w.TextFgColor = ui.ThemeAttr("par.text.hi")
|
w.TextFgColor = ui.ThemeAttr("par.text.hi")
|
||||||
w.TextBgColor = ui.ThemeAttr("par.text.fg")
|
w.TextBgColor = ui.ThemeAttr("par.text.fg")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *TextCol) UnHighlight() {
|
func (w *TextCol) UnHighlight() {
|
||||||
|
w.Bg = ui.ThemeAttr("par.text.bg")
|
||||||
w.TextFgColor = ui.ThemeAttr("par.text.fg")
|
w.TextFgColor = ui.ThemeAttr("par.text.fg")
|
||||||
w.TextBgColor = ui.ThemeAttr("par.text.bg")
|
w.TextBgColor = ui.ThemeAttr("par.text.bg")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package compact
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
ui "github.com/gizak/termui"
|
ui "github.com/gizak/termui"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,7 +29,7 @@ func calcWidth(width int) int {
|
|||||||
for _, w := range colWidths {
|
for _, w := range colWidths {
|
||||||
width -= w
|
width -= w
|
||||||
if w == 0 {
|
if w == 0 {
|
||||||
staticCols += 1
|
staticCols++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (width - spacing) / staticCols
|
return (width - spacing) / staticCols
|
||||||
|
|||||||
36
cwidgets/single/env.go
Normal file
36
cwidgets/single/env.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package single
|
||||||
|
|
||||||
|
import (
|
||||||
|
ui "github.com/gizak/termui"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var envPattern = regexp.MustCompile(`(?P<KEY>[^=]+)=(?P<VALUJE>.*)`)
|
||||||
|
|
||||||
|
type Env struct {
|
||||||
|
*ui.Table
|
||||||
|
data map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEnv() *Env {
|
||||||
|
p := ui.NewTable()
|
||||||
|
p.Height = 4
|
||||||
|
p.Width = colWidth[0]
|
||||||
|
p.FgColor = ui.ThemeAttr("par.text.fg")
|
||||||
|
p.Separator = false
|
||||||
|
i := &Env{p, make(map[string]string)}
|
||||||
|
i.BorderLabel = "Env"
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Env) Set(k, v string) {
|
||||||
|
match := envPattern.FindStringSubmatch(v)
|
||||||
|
key := match[1]
|
||||||
|
value := match[2]
|
||||||
|
w.data[key] = value
|
||||||
|
|
||||||
|
w.Rows = [][]string{}
|
||||||
|
w.Rows = append(w.Rows, mkInfoRows(key, value)...)
|
||||||
|
|
||||||
|
w.Height = len(w.Rows) + 2
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
ui "github.com/gizak/termui"
|
ui "github.com/gizak/termui"
|
||||||
)
|
)
|
||||||
|
|
||||||
var displayInfo = []string{"id", "name", "image", "ports", "state", "created", "health"}
|
var displayInfo = []string{"id", "name", "image", "ports", "IPs", "state", "created", "health"}
|
||||||
|
|
||||||
type Info struct {
|
type Info struct {
|
||||||
*ui.Table
|
*ui.Table
|
||||||
@@ -45,7 +45,7 @@ func mkInfoRows(k, v string) (rows [][]string) {
|
|||||||
// initial row with field name
|
// initial row with field name
|
||||||
rows = append(rows, []string{k, lines[0]})
|
rows = append(rows, []string{k, lines[0]})
|
||||||
|
|
||||||
// append any additional lines in seperate row
|
// append any additional lines in separate row
|
||||||
if len(lines) > 1 {
|
if len(lines) > 1 {
|
||||||
for _, line := range lines[1:] {
|
for _, line := range lines[1:] {
|
||||||
if line != "" {
|
if line != "" {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ type Single struct {
|
|||||||
Cpu *Cpu
|
Cpu *Cpu
|
||||||
Mem *Mem
|
Mem *Mem
|
||||||
IO *IO
|
IO *IO
|
||||||
|
Env *Env
|
||||||
X, Y int
|
X, Y int
|
||||||
Width int
|
Width int
|
||||||
}
|
}
|
||||||
@@ -32,6 +33,7 @@ func NewSingle(id string) *Single {
|
|||||||
Cpu: NewCpu(),
|
Cpu: NewCpu(),
|
||||||
Mem: NewMem(),
|
Mem: NewMem(),
|
||||||
IO: NewIO(),
|
IO: NewIO(),
|
||||||
|
Env: NewEnv(),
|
||||||
Width: ui.TermWidth(),
|
Width: ui.TermWidth(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,8 +54,14 @@ func (e *Single) Down() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Single) SetWidth(w int) { e.Width = w }
|
func (e *Single) SetWidth(w int) { e.Width = w }
|
||||||
func (e *Single) SetMeta(k, v string) { e.Info.Set(k, v) }
|
func (e *Single) SetMeta(k, v string) {
|
||||||
|
if k == "[ENV-VAR]" {
|
||||||
|
e.Env.Set(k, v)
|
||||||
|
} else {
|
||||||
|
e.Info.Set(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Single) SetMetrics(m models.Metrics) {
|
func (e *Single) SetMetrics(m models.Metrics) {
|
||||||
e.Cpu.Update(m.CPUUtil)
|
e.Cpu.Update(m.CPUUtil)
|
||||||
@@ -69,6 +77,7 @@ func (e *Single) GetHeight() (h int) {
|
|||||||
h += e.Cpu.Height
|
h += e.Cpu.Height
|
||||||
h += e.Mem.Height
|
h += e.Mem.Height
|
||||||
h += e.IO.Height
|
h += e.IO.Height
|
||||||
|
h += e.Env.Height
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +112,7 @@ func (e *Single) Buffer() ui.Buffer {
|
|||||||
buf.Merge(e.Mem.Buffer())
|
buf.Merge(e.Mem.Buffer())
|
||||||
buf.Merge(e.Net.Buffer())
|
buf.Merge(e.Net.Buffer())
|
||||||
buf.Merge(e.IO.Buffer())
|
buf.Merge(e.IO.Buffer())
|
||||||
|
buf.Merge(e.Env.Buffer())
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +123,7 @@ func (e *Single) all() []ui.GridBufferer {
|
|||||||
e.Mem,
|
e.Mem,
|
||||||
e.Net,
|
e.Net,
|
||||||
e.IO,
|
e.IO,
|
||||||
|
e.Env,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
61
go.mod
Normal file
61
go.mod
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
module github.com/bcicen/ctop
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20160622173216-fa152c58bc15 // indirect
|
||||||
|
github.com/BurntSushi/toml v0.3.0
|
||||||
|
github.com/Microsoft/go-winio v0.3.8 // indirect
|
||||||
|
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
||||||
|
github.com/Sirupsen/logrus v0.0.0-20150423025312-26709e271410 // indirect
|
||||||
|
github.com/c9s/goprocinfo v0.0.0-20170609001544-b34328d6e0cd
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20151104194251-b4a58d95188d // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/docker/docker v0.0.0-20170502054910-90d35abf7b35 // indirect
|
||||||
|
github.com/docker/go-connections v0.0.0-20170301234100-a2afab980204 // indirect
|
||||||
|
github.com/docker/go-units v0.3.2 // indirect
|
||||||
|
github.com/fsouza/go-dockerclient v0.0.0-20170307141636-318513eb1ab2
|
||||||
|
github.com/ghodss/yaml v1.0.0 // indirect
|
||||||
|
github.com/gizak/termui v2.3.0+incompatible
|
||||||
|
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55 // indirect
|
||||||
|
github.com/gogo/protobuf v1.1.1 // indirect
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
|
||||||
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c // indirect
|
||||||
|
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect
|
||||||
|
github.com/googleapis/gnostic v0.2.0 // indirect
|
||||||
|
github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f // indirect
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.0.0-20170211013415-3573b8b52aa7 // indirect
|
||||||
|
github.com/imdario/mergo v0.3.6 // indirect
|
||||||
|
github.com/jgautheron/codename-generator v0.0.0-20150829203204-16d037c7cc3c
|
||||||
|
github.com/json-iterator/go v1.1.5 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.0-20170201023540-14207d285c6c // indirect
|
||||||
|
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||||
|
github.com/nsf/termbox-go v0.0.0-20180303152453-e2050e41c884
|
||||||
|
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d
|
||||||
|
github.com/op/go-logging v0.0.0-20160211212156-b2cb9fa56473
|
||||||
|
github.com/opencontainers/runc v0.1.1
|
||||||
|
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/seccomp/libseccomp-golang v0.0.0-20150813023252-1b506fc7c24e // indirect
|
||||||
|
github.com/spf13/pflag v1.0.3 // indirect
|
||||||
|
github.com/stretchr/testify v1.2.2 // indirect
|
||||||
|
github.com/syndtr/gocapability v0.0.0-20150716010906-2c00daeb6c3b // indirect
|
||||||
|
github.com/vishvananda/netlink v0.0.0-20150820014904-1e2e08e8a2dc // indirect
|
||||||
|
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 // indirect
|
||||||
|
golang.org/x/oauth2 v0.0.0-20181128211412-28207608b838 // indirect
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 // indirect
|
||||||
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c // indirect
|
||||||
|
google.golang.org/appengine v1.3.0 // indirect
|
||||||
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.2.2 // indirect
|
||||||
|
k8s.io/api v0.0.0-20181130031204-d04500c8c3dd
|
||||||
|
k8s.io/apimachinery v0.0.0-20181201231028-18a5ff3097b4
|
||||||
|
k8s.io/client-go v9.0.0+incompatible
|
||||||
|
k8s.io/klog v0.1.0 // indirect
|
||||||
|
k8s.io/metrics v0.0.0-20181121073115-d8618695b08f
|
||||||
|
sigs.k8s.io/yaml v1.1.0 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
replace github.com/gizak/termui => github.com/bcicen/termui v0.0.0-20180326052246-4eb80249d3f5
|
||||||
8
grid.go
8
grid.go
@@ -104,6 +104,14 @@ func Display() bool {
|
|||||||
menu = ContainerMenu
|
menu = ContainerMenu
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
|
ui.Handle("/sys/kbd/<left>", func(ui.Event) {
|
||||||
|
menu = LogMenu
|
||||||
|
ui.StopLoop()
|
||||||
|
})
|
||||||
|
ui.Handle("/sys/kbd/<right>", func(ui.Event) {
|
||||||
|
menu = SingleView
|
||||||
|
ui.StopLoop()
|
||||||
|
})
|
||||||
ui.Handle("/sys/kbd/l", func(ui.Event) {
|
ui.Handle("/sys/kbd/l", func(ui.Event) {
|
||||||
menu = LogMenu
|
menu = LogMenu
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
|
|||||||
26
install.sh
26
install.sh
@@ -5,6 +5,10 @@ KERNEL=$(uname -s)
|
|||||||
|
|
||||||
function output() { echo -e "\033[32mctop-install\033[0m $@"; }
|
function output() { echo -e "\033[32mctop-install\033[0m $@"; }
|
||||||
|
|
||||||
|
function command_exists() {
|
||||||
|
command -v "$@" > /dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
# extract github download url matching pattern
|
# extract github download url matching pattern
|
||||||
function extract_url() {
|
function extract_url() {
|
||||||
match=$1; shift
|
match=$1; shift
|
||||||
@@ -28,6 +32,26 @@ case $KERNEL in
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
for req in curl wget; do
|
||||||
|
command_exists $req || {
|
||||||
|
output "missing required $req binary"
|
||||||
|
req_failed=1
|
||||||
|
}
|
||||||
|
done
|
||||||
|
[ "$req_failed" == 1 ] && exit 1
|
||||||
|
|
||||||
|
sh_c='sh -c'
|
||||||
|
if [ "$CURRENT_USER" != 'root' ]; then
|
||||||
|
if command_exists sudo; then
|
||||||
|
sh_c='sudo -E sh -c'
|
||||||
|
elif command_exists su; then
|
||||||
|
sh_c='su -c'
|
||||||
|
else
|
||||||
|
output "Error: this installer needs the ability to run commands as root. We are unable to find either "sudo" or "su" available to make this happen."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
TMP=$(mktemp -d "${TMPDIR:-/tmp}/ctop.XXXXX")
|
TMP=$(mktemp -d "${TMPDIR:-/tmp}/ctop.XXXXX")
|
||||||
cd ${TMP}
|
cd ${TMP}
|
||||||
|
|
||||||
@@ -55,6 +79,6 @@ wget -q --show-progress $url
|
|||||||
|
|
||||||
output "installing to /usr/local/bin"
|
output "installing to /usr/local/bin"
|
||||||
chmod +x ctop-*
|
chmod +x ctop-*
|
||||||
sudo mv ctop-* /usr/local/bin/ctop
|
$sh_c "mv ctop-* /usr/local/bin/ctop"
|
||||||
|
|
||||||
output "done!"
|
output "done!"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package logging
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
@@ -56,13 +57,13 @@ func StopServer() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handler(conn net.Conn) {
|
func handler(wc io.WriteCloser) {
|
||||||
server.wg.Add(1)
|
server.wg.Add(1)
|
||||||
defer server.wg.Done()
|
defer server.wg.Done()
|
||||||
defer conn.Close()
|
defer wc.Close()
|
||||||
for msg := range Log.tail() {
|
for msg := range Log.tail() {
|
||||||
msg = fmt.Sprintf("%s\n", msg)
|
msg = fmt.Sprintf("%s\n", msg)
|
||||||
conn.Write([]byte(msg))
|
wc.Write([]byte(msg))
|
||||||
}
|
}
|
||||||
conn.Write([]byte("bye\n"))
|
wc.Write([]byte("bye\n"))
|
||||||
}
|
}
|
||||||
|
|||||||
35
main.go
35
main.go
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/bcicen/ctop/config"
|
"github.com/bcicen/ctop/config"
|
||||||
"github.com/bcicen/ctop/connector"
|
"github.com/bcicen/ctop/connector"
|
||||||
@@ -28,24 +29,24 @@ var (
|
|||||||
status *widgets.StatusLine
|
status *widgets.StatusLine
|
||||||
|
|
||||||
versionStr = fmt.Sprintf("ctop version %v, build %v %v", version, build, goVersion)
|
versionStr = fmt.Sprintf("ctop version %v, build %v %v", version, build, goVersion)
|
||||||
|
|
||||||
|
fs = flag.NewFlagSet("ctop", flag.ExitOnError)
|
||||||
|
versionFlag = fs.Bool("version", false, "output version information and exit")
|
||||||
|
helpFlag = fs.Bool("h", false, "display this help dialog")
|
||||||
|
filterFlag = fs.String("f", "", "filter containers")
|
||||||
|
activeOnlyFlag = fs.Bool("a", false, "show active containers only")
|
||||||
|
sortFieldFlag = fs.String("s", "", "select container sort field")
|
||||||
|
reverseSortFlag = fs.Bool("r", false, "reverse container sort order")
|
||||||
|
invertFlag = fs.Bool("i", false, "invert default colors")
|
||||||
|
scaleCpu = fs.Bool("scale-cpu", false, "show cpu as % of system total")
|
||||||
|
connectorFlag = fs.String("connector", "docker", "container connector to use")
|
||||||
|
namespaceFlag = fs.String("n", "default", "Kubernetes namespace for monitoring")
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
defer panicExit()
|
defer panicExit()
|
||||||
|
|
||||||
// parse command line arguments
|
fs.Parse(os.Args[1:])
|
||||||
var (
|
|
||||||
versionFlag = flag.Bool("v", false, "output version information and exit")
|
|
||||||
helpFlag = flag.Bool("h", false, "display this help dialog")
|
|
||||||
filterFlag = flag.String("f", "", "filter containers")
|
|
||||||
activeOnlyFlag = flag.Bool("a", false, "show active containers only")
|
|
||||||
sortFieldFlag = flag.String("s", "", "select container sort field")
|
|
||||||
reverseSortFlag = flag.Bool("r", false, "reverse container sort order")
|
|
||||||
invertFlag = flag.Bool("i", false, "invert default colors")
|
|
||||||
scaleCpu = flag.Bool("scale-cpu", false, "show cpu as % of system total")
|
|
||||||
connectorFlag = flag.String("connector", "docker", "container connector to use")
|
|
||||||
)
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if *versionFlag {
|
if *versionFlag {
|
||||||
fmt.Println(versionStr)
|
fmt.Println(versionStr)
|
||||||
@@ -90,10 +91,12 @@ func main() {
|
|||||||
if *invertFlag {
|
if *invertFlag {
|
||||||
InvertColorMap()
|
InvertColorMap()
|
||||||
}
|
}
|
||||||
|
config.Update("namespace", *namespaceFlag)
|
||||||
ui.ColorMap = ColorMap // override default colormap
|
ui.ColorMap = ColorMap // override default colormap
|
||||||
if err := ui.Init(); err != nil {
|
if err := ui.Init(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
tm.SetInputMode(tm.InputAlt)
|
||||||
|
|
||||||
defer Shutdown()
|
defer Shutdown()
|
||||||
// init grid, cursor, header
|
// init grid, cursor, header
|
||||||
@@ -138,7 +141,7 @@ func panicExit() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var helpMsg = `ctop - container metric viewer
|
var helpMsg = `ctop - interactive container viewer
|
||||||
|
|
||||||
usage: ctop [options]
|
usage: ctop [options]
|
||||||
|
|
||||||
@@ -147,5 +150,7 @@ options:
|
|||||||
|
|
||||||
func printHelp() {
|
func printHelp() {
|
||||||
fmt.Println(helpMsg)
|
fmt.Println(helpMsg)
|
||||||
flag.PrintDefaults()
|
fs.PrintDefaults()
|
||||||
|
fmt.Printf("\navailable connectors: ")
|
||||||
|
fmt.Println(strings.Join(connector.Enabled(), ", "))
|
||||||
}
|
}
|
||||||
|
|||||||
18
menus.go
18
menus.go
@@ -37,7 +37,10 @@ func HelpMenu() MenuFn {
|
|||||||
m := menu.NewMenu()
|
m := menu.NewMenu()
|
||||||
m.BorderLabel = "Help"
|
m.BorderLabel = "Help"
|
||||||
m.AddItems(helpDialog...)
|
m.AddItems(helpDialog...)
|
||||||
ui.Render(m)
|
ui.Handle("/sys/wnd/resize", func(e ui.Event) {
|
||||||
|
ui.Clear()
|
||||||
|
ui.Render(m)
|
||||||
|
})
|
||||||
ui.Handle("/sys/kbd/", func(ui.Event) {
|
ui.Handle("/sys/kbd/", func(ui.Event) {
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
@@ -129,11 +132,16 @@ func ContainerMenu() MenuFn {
|
|||||||
|
|
||||||
if c.Meta["state"] == "running" {
|
if c.Meta["state"] == "running" {
|
||||||
items = append(items, menu.Item{Val: "stop", Label: "stop"})
|
items = append(items, menu.Item{Val: "stop", Label: "stop"})
|
||||||
|
items = append(items, menu.Item{Val: "pause", Label: "pause"})
|
||||||
|
items = append(items, menu.Item{Val: "restart", Label: "restart"})
|
||||||
}
|
}
|
||||||
if c.Meta["state"] == "exited" || c.Meta["state"] == "created" {
|
if c.Meta["state"] == "exited" || c.Meta["state"] == "created" {
|
||||||
items = append(items, menu.Item{Val: "start", Label: "start"})
|
items = append(items, menu.Item{Val: "start", Label: "start"})
|
||||||
items = append(items, menu.Item{Val: "remove", Label: "remove"})
|
items = append(items, menu.Item{Val: "remove", Label: "remove"})
|
||||||
}
|
}
|
||||||
|
if c.Meta["state"] == "paused" {
|
||||||
|
items = append(items, menu.Item{Val: "unpause", Label: "unpause"})
|
||||||
|
}
|
||||||
items = append(items, menu.Item{Val: "cancel", Label: "cancel"})
|
items = append(items, menu.Item{Val: "cancel", Label: "cancel"})
|
||||||
|
|
||||||
m.AddItems(items...)
|
m.AddItems(items...)
|
||||||
@@ -154,6 +162,12 @@ func ContainerMenu() MenuFn {
|
|||||||
nextMenu = Confirm(confirmTxt("stop", c.GetMeta("name")), c.Stop)
|
nextMenu = Confirm(confirmTxt("stop", c.GetMeta("name")), c.Stop)
|
||||||
case "remove":
|
case "remove":
|
||||||
nextMenu = Confirm(confirmTxt("remove", c.GetMeta("name")), c.Remove)
|
nextMenu = Confirm(confirmTxt("remove", c.GetMeta("name")), c.Remove)
|
||||||
|
case "pause":
|
||||||
|
nextMenu = Confirm(confirmTxt("pause", c.GetMeta("name")), c.Pause)
|
||||||
|
case "unpause":
|
||||||
|
nextMenu = Confirm(confirmTxt("unpause", c.GetMeta("name")), c.Unpause)
|
||||||
|
case "restart":
|
||||||
|
nextMenu = Confirm(confirmTxt("restart", c.GetMeta("name")), c.Restart)
|
||||||
}
|
}
|
||||||
ui.StopLoop()
|
ui.StopLoop()
|
||||||
})
|
})
|
||||||
@@ -176,7 +190,7 @@ func LogMenu() MenuFn {
|
|||||||
|
|
||||||
logs, quit := logReader(c)
|
logs, quit := logReader(c)
|
||||||
m := widgets.NewTextView(logs)
|
m := widgets.NewTextView(logs)
|
||||||
m.BorderLabel = "Logs"
|
m.BorderLabel = fmt.Sprintf("Logs [%s]", c.GetMeta("name"))
|
||||||
ui.Render(m)
|
ui.Render(m)
|
||||||
|
|
||||||
ui.Handle("/sys/wnd/resize", func(e ui.Event) {
|
ui.Handle("/sys/wnd/resize", func(e ui.Event) {
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ func (i *Input) KeyPress(e ui.Event) {
|
|||||||
if len(i.Data) >= i.MaxLen {
|
if len(i.Data) >= i.MaxLen {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if strings.Index(input_chars, ch) > -1 {
|
if strings.Contains(input_chars, ch) {
|
||||||
i.Data += ch
|
i.Data += ch
|
||||||
i.stream <- i.Data
|
i.stream <- i.Data
|
||||||
ui.Render(i)
|
ui.Render(i)
|
||||||
|
|||||||
Reference in New Issue
Block a user