From f27de1c29e571cbbb9b8b8770cacbbc9e1ab9f6b Mon Sep 17 00:00:00 2001 From: Stanislav Pavlovichev Date: Sun, 7 Oct 2018 16:46:32 +0300 Subject: [PATCH 01/26] Exec Sh Feature --- menus.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/menus.go b/menus.go index 0c819c7..dbd5f70 100644 --- a/menus.go +++ b/menus.go @@ -2,6 +2,8 @@ package main import ( "fmt" + "os" + "os/exec" "time" "github.com/bcicen/ctop/config" @@ -134,6 +136,7 @@ func ContainerMenu() MenuFn { 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"}) + items = append(items, menu.Item{Val: "exec sh", Label: "exec sh"}) } if c.Meta["state"] == "exited" || c.Meta["state"] == "created" { items = append(items, menu.Item{Val: "start", Label: "start"}) @@ -156,6 +159,8 @@ func ContainerMenu() MenuFn { nextMenu = SingleView case "logs": nextMenu = LogMenu + case "exec sh": + nextMenu = ExecSh case "start": nextMenu = Confirm(confirmTxt("start", c.GetMeta("name")), c.Start) case "stop": @@ -207,6 +212,24 @@ func LogMenu() MenuFn { return nil } +func ExecSh() MenuFn { + c := cursor.Selected() + + if c == nil { + return nil + } + + // Reset colors && clear screen && run sh + cmdName := fmt.Sprintf("echo '\033[0m' && clear && docker exec -it %s sh", c.GetMeta("name")) + cmd := exec.Command("bash", "-c", cmdName) + cmd.Stdout = os.Stdout + cmd.Stdin = os.Stdin + cmd.Stderr = os.Stderr + cmd.Run() + + return nil +} + // Create a confirmation dialog with a given description string and // func to perform if confirmed func Confirm(txt string, fn func()) MenuFn { From e68f7ba96a37b174e83cfae2704a54c1515cebc9 Mon Sep 17 00:00:00 2001 From: Stanislav Pavlovichev Date: Fri, 12 Oct 2018 10:03:27 +0300 Subject: [PATCH 02/26] fix: handlers used to work after "exec sh" command feature: hot key for "exec sh" --- grid.go | 4 ++++ menus.go | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/grid.go b/grid.go index 501c216..c646c16 100644 --- a/grid.go +++ b/grid.go @@ -116,6 +116,10 @@ func Display() bool { menu = LogMenu ui.StopLoop() }) + ui.Handle("/sys/kbd/e", func(ui.Event) { + menu = ExecSh + ui.StopLoop() + }) ui.Handle("/sys/kbd/o", func(ui.Event) { menu = SingleView ui.StopLoop() diff --git a/menus.go b/menus.go index dbd5f70..aa445c3 100644 --- a/menus.go +++ b/menus.go @@ -27,6 +27,7 @@ var helpDialog = []menu.Item{ {"[r] - reverse container sort order", ""}, {"[o] - open single view", ""}, {"[l] - view container logs ([t] to toggle timestamp when open)", ""}, + {"[e] - exec sh", ""}, {"[S] - save current configuration to file", ""}, {"[q] - exit ctop", ""}, } @@ -219,6 +220,11 @@ func ExecSh() MenuFn { return nil } + ui.DefaultEvtStream.ResetHandlers() + defer ui.DefaultEvtStream.ResetHandlers() + ui.StopLoop() + defer ui.Loop() + // Reset colors && clear screen && run sh cmdName := fmt.Sprintf("echo '\033[0m' && clear && docker exec -it %s sh", c.GetMeta("name")) cmd := exec.Command("bash", "-c", cmdName) From 967a87a65f16c08994cc10944cc0d87e425df921 Mon Sep 17 00:00:00 2001 From: Stanislav Pavlovichev Date: Sat, 13 Oct 2018 08:33:53 +0300 Subject: [PATCH 03/26] Exec using API --- Gopkg.lock | 76 +++++++++++++++++++++++++++++++++---- connector/manager/docker.go | 23 +++++++++++ connector/manager/main.go | 1 + connector/manager/mock.go | 4 ++ connector/manager/runc.go | 4 ++ container/main.go | 4 ++ menus.go | 10 +---- 7 files changed, 105 insertions(+), 17 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 8d7a4ce..515b296 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,7 +3,10 @@ [[projects]] name = "github.com/Azure/go-ansiterm" - packages = [".","winterm"] + packages = [ + ".", + "winterm" + ] revision = "fa152c58bc15761d0200cb75fe958b89a9d4888e" [[projects]] @@ -36,13 +39,44 @@ [[projects]] name = "github.com/coreos/go-systemd" - packages = ["dbus","util"] + 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"] + 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" @@ -128,7 +162,24 @@ [[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"] + 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" @@ -144,22 +195,31 @@ [[projects]] name = "github.com/vishvananda/netlink" - packages = [".","nl"] + packages = [ + ".", + "nl" + ] revision = "1e2e08e8a2dcdacaae3f14ac44c5cfa31361f270" [[projects]] name = "golang.org/x/net" - packages = ["context","context/ctxhttp"] + packages = [ + "context", + "context/ctxhttp" + ] revision = "a6577fac2d73be281a500b310739095313165611" [[projects]] name = "golang.org/x/sys" - packages = ["unix","windows"] + packages = [ + "unix", + "windows" + ] revision = "99f16d856c9836c42d24e7ab64ea72916925fa97" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "a21d4c707f08f26de894adbdd00d5d6e82a54f5e0f52566229dd2da9b94e26ec" + inputs-digest = "f46f5c696ecb0b0c42a38dac512df21fc1f5fb2bfda888434e005e69d1b6273b" solver-name = "gps-cdcl" solver-version = 1 diff --git a/connector/manager/docker.go b/connector/manager/docker.go index 77dc987..0f66298 100644 --- a/connector/manager/docker.go +++ b/connector/manager/docker.go @@ -3,6 +3,7 @@ package manager import ( "fmt" api "github.com/fsouza/go-dockerclient" + "os" ) type Docker struct { @@ -17,6 +18,28 @@ func NewDocker(client *api.Client, id string) *Docker { } } +func (dc *Docker) Exec(cmd []string) error { + execCmd, err := dc.client.CreateExec(api.CreateExecOptions{ + AttachStdin: true, + AttachStdout: true, + AttachStderr: true, + Cmd: cmd, + Container: dc.id, + Tty: true, + }) + + if err != nil { + return err + } + + return dc.client.StartExec(execCmd.ID, api.StartExecOptions{ + InputStream: os.Stdin, + OutputStream: os.Stdout, + ErrorStream: os.Stderr, + RawTerminal: true, + }) +} + func (dc *Docker) Start() error { c, err := dc.client.InspectContainer(dc.id) if err != nil { diff --git a/connector/manager/main.go b/connector/manager/main.go index b6debaa..f65aad3 100644 --- a/connector/manager/main.go +++ b/connector/manager/main.go @@ -7,4 +7,5 @@ type Manager interface { Pause() error Unpause() error Restart() error + Exec(cmd []string) error } diff --git a/connector/manager/mock.go b/connector/manager/mock.go index f33fd77..f6fd62f 100644 --- a/connector/manager/mock.go +++ b/connector/manager/mock.go @@ -29,3 +29,7 @@ func (m *Mock) Unpause() error { func (m *Mock) Restart() error { return nil } + +func (m *Mock) Exec(cmd []string) error { + return nil +} diff --git a/connector/manager/runc.go b/connector/manager/runc.go index cf61f14..07a4b58 100644 --- a/connector/manager/runc.go +++ b/connector/manager/runc.go @@ -29,3 +29,7 @@ func (rc *Runc) Unpause() error { func (rc *Runc) Restart() error { return nil } + +func (rc *Runc) Exec(cmd []string) error { + return nil +} diff --git a/container/main.go b/container/main.go index 586dd94..735fff1 100644 --- a/container/main.go +++ b/container/main.go @@ -149,3 +149,7 @@ func (c *Container) Restart() { } } } + +func (c *Container) Exec(cmd []string) error { + return c.manager.Exec(cmd) +} diff --git a/menus.go b/menus.go index aa445c3..9ed7542 100644 --- a/menus.go +++ b/menus.go @@ -2,8 +2,6 @@ package main import ( "fmt" - "os" - "os/exec" "time" "github.com/bcicen/ctop/config" @@ -225,13 +223,7 @@ func ExecSh() MenuFn { ui.StopLoop() defer ui.Loop() - // Reset colors && clear screen && run sh - cmdName := fmt.Sprintf("echo '\033[0m' && clear && docker exec -it %s sh", c.GetMeta("name")) - cmd := exec.Command("bash", "-c", cmdName) - cmd.Stdout = os.Stdout - cmd.Stdin = os.Stdin - cmd.Stderr = os.Stderr - cmd.Run() + c.Exec([]string{"sh", "-c", "echo '\033[0m' && clear && sh"}) return nil } From a26fc9169c026a0a3464959c324bd86bf92f687b Mon Sep 17 00:00:00 2001 From: Stanislav Pavlovichev Date: Thu, 25 Oct 2018 21:52:59 +0300 Subject: [PATCH 04/26] Ability to change Shell --- README.md | 2 ++ config/param.go | 5 +++++ grid.go | 2 +- main.go | 5 +++++ menus.go | 15 +++++++++------ 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 99811cc..da732c5 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ Option | Description -s | select initial container sort field -scale-cpu | show cpu as % of system total -v | output version information and exit +-shell | specify shell (default: sh) ### Keybindings @@ -84,6 +85,7 @@ s | Select container sort field r | Reverse container sort order o | Open single view l | View container logs (`t` to toggle timestamp when open) +e | Exec Shell S | Save current configuration to file q | Quit ctop diff --git a/config/param.go b/config/param.go index 30dd036..f0b7ef1 100644 --- a/config/param.go +++ b/config/param.go @@ -12,6 +12,11 @@ var params = []*Param{ Val: "state", Label: "Container Sort Field", }, + &Param{ + Key: "shell", + Val: "sh", + Label: "Shell", + }, } type Param struct { diff --git a/grid.go b/grid.go index c646c16..871d3cb 100644 --- a/grid.go +++ b/grid.go @@ -117,7 +117,7 @@ func Display() bool { ui.StopLoop() }) ui.Handle("/sys/kbd/e", func(ui.Event) { - menu = ExecSh + menu = ExecShell ui.StopLoop() }) ui.Handle("/sys/kbd/o", func(ui.Event) { diff --git a/main.go b/main.go index 3a93a4c..f2eac66 100644 --- a/main.go +++ b/main.go @@ -45,6 +45,7 @@ func main() { 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") + defaultShell = flag.String("shell", "", "default shell") ) flag.Parse() @@ -87,6 +88,10 @@ func main() { config.Toggle("scaleCpu") } + if *defaultShell != "" { + config.Update("shell", *defaultShell) + } + // init ui if *invertFlag { InvertColorMap() diff --git a/menus.go b/menus.go index 9ed7542..c4f3ac8 100644 --- a/menus.go +++ b/menus.go @@ -25,7 +25,7 @@ var helpDialog = []menu.Item{ {"[r] - reverse container sort order", ""}, {"[o] - open single view", ""}, {"[l] - view container logs ([t] to toggle timestamp when open)", ""}, - {"[e] - exec sh", ""}, + {"[e] - exec shell", ""}, {"[S] - save current configuration to file", ""}, {"[q] - exit ctop", ""}, } @@ -135,7 +135,7 @@ func ContainerMenu() MenuFn { 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"}) - items = append(items, menu.Item{Val: "exec sh", Label: "exec sh"}) + items = append(items, menu.Item{Val: "exec shell", Label: "exec shell"}) } if c.Meta["state"] == "exited" || c.Meta["state"] == "created" { items = append(items, menu.Item{Val: "start", Label: "start"}) @@ -158,8 +158,8 @@ func ContainerMenu() MenuFn { nextMenu = SingleView case "logs": nextMenu = LogMenu - case "exec sh": - nextMenu = ExecSh + case "exec shell": + nextMenu = ExecShell case "start": nextMenu = Confirm(confirmTxt("start", c.GetMeta("name")), c.Start) case "stop": @@ -211,7 +211,7 @@ func LogMenu() MenuFn { return nil } -func ExecSh() MenuFn { +func ExecShell() MenuFn { c := cursor.Selected() if c == nil { @@ -223,7 +223,10 @@ func ExecSh() MenuFn { ui.StopLoop() defer ui.Loop() - c.Exec([]string{"sh", "-c", "echo '\033[0m' && clear && sh"}) + shell := config.Get("shell") + if err := c.Exec([]string{shell.Val, "-c", "echo '\033[0m' && clear && " + shell.Val}); err != nil { + log.Fatal(err) + } return nil } From d59c91a461827b853b09c0581c1dd5be0f69859f Mon Sep 17 00:00:00 2001 From: Stanislav Pavlovichev Date: Fri, 26 Oct 2018 17:08:33 +0300 Subject: [PATCH 05/26] Do not allow to close /dev/stdin --- connector/manager/docker.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/connector/manager/docker.go b/connector/manager/docker.go index 0f66298..7e32cc7 100644 --- a/connector/manager/docker.go +++ b/connector/manager/docker.go @@ -3,6 +3,7 @@ package manager import ( "fmt" api "github.com/fsouza/go-dockerclient" + "io" "os" ) @@ -18,6 +19,15 @@ func NewDocker(client *api.Client, id string) *Docker { } } +// Do not allow to close reader (i.e. /dev/stdin which docker client tries to close after command execution) +type noClosableReader struct { + wrappedReader io.Reader +} + +func (w *noClosableReader) Read(p []byte) (n int, err error) { + return w.wrappedReader.Read(p) +} + func (dc *Docker) Exec(cmd []string) error { execCmd, err := dc.client.CreateExec(api.CreateExecOptions{ AttachStdin: true, @@ -33,7 +43,7 @@ func (dc *Docker) Exec(cmd []string) error { } return dc.client.StartExec(execCmd.ID, api.StartExecOptions{ - InputStream: os.Stdin, + InputStream: &noClosableReader{os.Stdin}, OutputStream: os.Stdout, ErrorStream: os.Stderr, RawTerminal: true, From ca35ef2aab13b5e8bc3de5e8adbd87f17ca98d88 Mon Sep 17 00:00:00 2001 From: Stanislav Pavlovichev Date: Sun, 28 Oct 2018 12:07:43 +0200 Subject: [PATCH 06/26] Unnecessary loop stopping --- menus.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/menus.go b/menus.go index c4f3ac8..5084a46 100644 --- a/menus.go +++ b/menus.go @@ -220,8 +220,6 @@ func ExecShell() MenuFn { ui.DefaultEvtStream.ResetHandlers() defer ui.DefaultEvtStream.ResetHandlers() - ui.StopLoop() - defer ui.Loop() shell := config.Get("shell") if err := c.Exec([]string{shell.Val, "-c", "echo '\033[0m' && clear && " + shell.Val}); err != nil { From 101ddad6926f853eaa8650337bc8cf98fbc6ddf7 Mon Sep 17 00:00:00 2001 From: Stanislav Pavlovichev Date: Sun, 28 Oct 2018 13:42:15 +0200 Subject: [PATCH 07/26] Fixed a problem with rendering --- connector/manager/docker.go | 51 ++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/connector/manager/docker.go b/connector/manager/docker.go index 7e32cc7..1e8a91b 100644 --- a/connector/manager/docker.go +++ b/connector/manager/docker.go @@ -3,6 +3,7 @@ package manager import ( "fmt" api "github.com/fsouza/go-dockerclient" + "github.com/pkg/errors" "io" "os" ) @@ -28,6 +29,54 @@ func (w *noClosableReader) Read(p []byte) (n int, err error) { return w.wrappedReader.Read(p) } +const ( + STDIN = 0 + STDOUT = 1 + STDERR = 2 +) + +var ( + wrongFrameFormat = errors.New("Wrong frame format") +) + +// A frame has a Header and a Payload +// Header: [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} +// STREAM_TYPE can be: +// 0: stdin (is written on stdout) +// 1: stdout +// 2: stderr +// SIZE1, SIZE2, SIZE3, SIZE4 are the four bytes of the uint32 size encoded as big endian. +// But we don't use size, because we don't need to find the end of frame. +type frameWriter struct { + stdout io.Writer + stderr io.Writer + stdin io.Writer +} + +func (w *frameWriter) Write(p []byte) (n int, err error) { + if len(p) > 8 { + var targetWriter io.Writer + switch p[0] { + case STDIN: + targetWriter = w.stdin + break + case STDOUT: + targetWriter = w.stdout + break + case STDERR: + targetWriter = w.stderr + break + default: + return 0, wrongFrameFormat + } + + n, err := targetWriter.Write(p[8:]) + return n + 8, err + } + + return 0, wrongFrameFormat +} + func (dc *Docker) Exec(cmd []string) error { execCmd, err := dc.client.CreateExec(api.CreateExecOptions{ AttachStdin: true, @@ -44,7 +93,7 @@ func (dc *Docker) Exec(cmd []string) error { return dc.client.StartExec(execCmd.ID, api.StartExecOptions{ InputStream: &noClosableReader{os.Stdin}, - OutputStream: os.Stdout, + OutputStream: &frameWriter{os.Stdout, os.Stderr, os.Stdin}, ErrorStream: os.Stderr, RawTerminal: true, }) From 70bd2ae3a3476969cae3c7f921d38b130ceec648 Mon Sep 17 00:00:00 2001 From: Bradley Cicenas Date: Thu, 24 Jan 2019 11:50:49 +0000 Subject: [PATCH 08/26] v0.7.2 --- README.md | 4 ++-- VERSION | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 99811cc..dbcad36 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Fetch the [latest release](https://github.com/bcicen/ctop/releases) for your pla #### Linux ```bash -sudo wget https://github.com/bcicen/ctop/releases/download/v0.7.1/ctop-0.7.1-linux-amd64 -O /usr/local/bin/ctop +sudo wget https://github.com/bcicen/ctop/releases/download/v0.7.2/ctop-0.7.2-linux-amd64 -O /usr/local/bin/ctop sudo chmod +x /usr/local/bin/ctop ``` @@ -31,7 +31,7 @@ brew install ctop ``` or ```bash -sudo curl -Lo /usr/local/bin/ctop https://github.com/bcicen/ctop/releases/download/v0.7.1/ctop-0.7.1-darwin-amd64 +sudo curl -Lo /usr/local/bin/ctop https://github.com/bcicen/ctop/releases/download/v0.7.2/ctop-0.7.2-darwin-amd64 sudo chmod +x /usr/local/bin/ctop ``` diff --git a/VERSION b/VERSION index 39e898a..7486fdb 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.7.1 +0.7.2 From 7b4d4db049fd3dca6e305e559a04c2fb8cc65c2c Mon Sep 17 00:00:00 2001 From: Bradley Cicenas Date: Thu, 24 Jan 2019 12:50:59 +0000 Subject: [PATCH 09/26] add status indicator doc --- _docs/connectors.md | 2 +- _docs/img/status.png | Bin 0 -> 7550 bytes _docs/status.md | 18 ++++++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 _docs/img/status.png create mode 100644 _docs/status.md diff --git a/_docs/connectors.md b/_docs/connectors.md index 893b334..9b3247e 100644 --- a/_docs/connectors.md +++ b/_docs/connectors.md @@ -1,4 +1,4 @@ -# connectors +# Connectors `ctop` comes with the below native connectors, enabled via the `--connector` option. diff --git a/_docs/img/status.png b/_docs/img/status.png new file mode 100644 index 0000000000000000000000000000000000000000..3343d492219a0e6aeba3be5594cee0fc8ec8a6eb GIT binary patch literal 7550 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 Vkp0`>^*#k4C#5V|DQ+70e*mS!l#T!Z literal 0 HcmV?d00001 diff --git a/_docs/status.md b/_docs/status.md new file mode 100644 index 0000000..ce971fa --- /dev/null +++ b/_docs/status.md @@ -0,0 +1,18 @@ +# Status Indicator + +The `ctop` grid view provides a compact status indicator to convey container state +

ctop

+ +Appearance | Description +--- | --- +red | container is stopped +green | container is running +two vertical bars (▮▮) | container is paused + +If the container is configured with a health check, a `+` will appear next to the indicator + +Appearance | Description +--- | --- +red | health check in failed state +yellow | health check in starting state +green | health check in OK state From 2f7bc2a17201815589d464f5626b330b273fd394 Mon Sep 17 00:00:00 2001 From: bradley Date: Thu, 24 Jan 2019 08:00:51 -0500 Subject: [PATCH 10/26] Update status.md --- _docs/status.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/_docs/status.md b/_docs/status.md index ce971fa..0b45bc7 100644 --- a/_docs/status.md +++ b/_docs/status.md @@ -1,18 +1,27 @@ # Status Indicator The `ctop` grid view provides a compact status indicator to convey container state -

ctop

+ +ctop + + Appearance | Description --- | --- red | container is stopped green | container is running -two vertical bars (▮▮) | container is paused +▮▮ | container is paused + + If the container is configured with a health check, a `+` will appear next to the indicator + + Appearance | Description --- | --- red | health check in failed state yellow | health check in starting state green | health check in OK state + + From c49939f9659d49530f9b6415e38a23021761c4cd Mon Sep 17 00:00:00 2001 From: bradley Date: Thu, 24 Jan 2019 08:02:13 -0500 Subject: [PATCH 11/26] Update status.md --- _docs/status.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/_docs/status.md b/_docs/status.md index 0b45bc7..16dac88 100644 --- a/_docs/status.md +++ b/_docs/status.md @@ -4,6 +4,8 @@ The `ctop` grid view provides a compact status indicator to convey container sta ctop +#### Status + Appearance | Description @@ -14,6 +16,7 @@ green | container is running +#### Health If the container is configured with a health check, a `+` will appear next to the indicator From 29fa8cf3e784c64090b5e40aad28e959bb2561dd Mon Sep 17 00:00:00 2001 From: bradley Date: Thu, 24 Jan 2019 08:02:52 -0500 Subject: [PATCH 12/26] Update status.md --- _docs/status.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_docs/status.md b/_docs/status.md index 16dac88..082a20c 100644 --- a/_docs/status.md +++ b/_docs/status.md @@ -4,7 +4,7 @@ The `ctop` grid view provides a compact status indicator to convey container sta ctop -#### Status +### Status @@ -16,7 +16,7 @@ green | container is running -#### Health +### Health If the container is configured with a health check, a `+` will appear next to the indicator From 9592de82a0e5b6dcabe6d32ccef21af9b6af5262 Mon Sep 17 00:00:00 2001 From: Bradley Cicenas Date: Thu, 24 Jan 2019 14:06:40 +0000 Subject: [PATCH 13/26] add keyboard shortcuts to container menu --- menus.go | 105 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 77 insertions(+), 28 deletions(-) diff --git a/menus.go b/menus.go index 0c819c7..496c726 100644 --- a/menus.go +++ b/menus.go @@ -126,55 +126,104 @@ func ContainerMenu() MenuFn { m.BorderLabel = "Menu" items := []menu.Item{ - menu.Item{Val: "single", Label: "single view"}, - menu.Item{Val: "logs", Label: "log view"}, + menu.Item{Val: "single", Label: "[o] single view"}, + menu.Item{Val: "logs", Label: "[l] log view"}, } if c.Meta["state"] == "running" { - 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"}) + items = append(items, menu.Item{Val: "stop", Label: "[s] stop"}) + items = append(items, menu.Item{Val: "pause", Label: "[p] pause"}) + items = append(items, menu.Item{Val: "restart", Label: "[r] restart"}) } if c.Meta["state"] == "exited" || c.Meta["state"] == "created" { - items = append(items, menu.Item{Val: "start", Label: "start"}) - items = append(items, menu.Item{Val: "remove", Label: "remove"}) + items = append(items, menu.Item{Val: "start", Label: "[s] start"}) + items = append(items, menu.Item{Val: "remove", Label: "[R] remove"}) } if c.Meta["state"] == "paused" { - items = append(items, menu.Item{Val: "unpause", Label: "unpause"}) + items = append(items, menu.Item{Val: "unpause", Label: "[p] unpause"}) } - items = append(items, menu.Item{Val: "cancel", Label: "cancel"}) + items = append(items, menu.Item{Val: "cancel", Label: "[c] cancel"}) m.AddItems(items...) ui.Render(m) - var nextMenu MenuFn HandleKeys("up", m.Up) HandleKeys("down", m.Down) + + var selected string + + // shortcuts + ui.Handle("/sys/kbd/o", func(ui.Event) { + selected = "single" + ui.StopLoop() + }) + ui.Handle("/sys/kbd/l", func(ui.Event) { + selected = "logs" + ui.StopLoop() + }) + if c.Meta["state"] != "paused" { + ui.Handle("/sys/kbd/s", func(ui.Event) { + if c.Meta["state"] == "running" { + selected = "stop" + } else { + selected = "start" + } + ui.StopLoop() + }) + } + if c.Meta["state"] != "exited" || c.Meta["state"] != "created" { + ui.Handle("/sys/kbd/p", func(ui.Event) { + if c.Meta["state"] == "paused" { + selected = "unpause" + } else { + selected = "pause" + } + ui.StopLoop() + }) + } + if c.Meta["state"] == "running" { + ui.Handle("/sys/kbd/r", func(ui.Event) { + selected = "restart" + ui.StopLoop() + }) + } + ui.Handle("/sys/kbd/R", func(ui.Event) { + selected = "remove" + ui.StopLoop() + }) + ui.Handle("/sys/kbd/c", func(ui.Event) { + ui.StopLoop() + }) + ui.Handle("/sys/kbd/", func(ui.Event) { - switch m.SelectedItem().Val { - case "single": - nextMenu = SingleView - case "logs": - nextMenu = LogMenu - case "start": - nextMenu = Confirm(confirmTxt("start", c.GetMeta("name")), c.Start) - case "stop": - nextMenu = Confirm(confirmTxt("stop", c.GetMeta("name")), c.Stop) - case "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) - } + selected = m.SelectedItem().Val ui.StopLoop() }) ui.Handle("/sys/kbd/", func(ui.Event) { ui.StopLoop() }) ui.Loop() + + var nextMenu MenuFn + switch selected { + case "single": + nextMenu = SingleView + case "logs": + nextMenu = LogMenu + case "start": + nextMenu = Confirm(confirmTxt("start", c.GetMeta("name")), c.Start) + case "stop": + nextMenu = Confirm(confirmTxt("stop", c.GetMeta("name")), c.Stop) + case "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) + } + return nextMenu } From b401e7b17e3acde4979650f0bd9aa5fb306071ff Mon Sep 17 00:00:00 2001 From: CodeLingo Bot Date: Thu, 7 Mar 2019 02:33:29 +0000 Subject: [PATCH 14/26] Fix function comments based on best practices from Effective Go Signed-off-by: CodeLingo Bot --- config/param.go | 2 +- config/switch.go | 4 ++-- connector/docker.go | 4 ++-- connector/main.go | 2 +- connector/mock.go | 2 +- connector/runc.go | 4 ++-- container/main.go | 2 +- cursor.go | 2 +- cwidgets/single/main.go | 2 +- widgets/menu/main.go | 2 +- 10 files changed, 13 insertions(+), 13 deletions(-) diff --git a/config/param.go b/config/param.go index 30dd036..23a7670 100644 --- a/config/param.go +++ b/config/param.go @@ -30,7 +30,7 @@ func Get(k string) *Param { return &Param{} // default } -// Get Param value by key +// GetVal gets Param value by key func GetVal(k string) string { return Get(k).Val } diff --git a/config/switch.go b/config/switch.go index e209d79..ab3d7e0 100644 --- a/config/switch.go +++ b/config/switch.go @@ -35,7 +35,7 @@ type Switch struct { Label string } -// Return Switch by key +// GetSwitch returns Switch by key func GetSwitch(k string) *Switch { for _, sw := range GlobalSwitches { if sw.Key == k { @@ -45,7 +45,7 @@ func GetSwitch(k string) *Switch { return &Switch{} // default } -// Return Switch value by key +// GetSwitchVal returns Switch value by key func GetSwitchVal(k string) bool { return GetSwitch(k).Val } diff --git a/connector/docker.go b/connector/docker.go index 73ea10d..d80f729 100644 --- a/connector/docker.go +++ b/connector/docker.go @@ -143,7 +143,7 @@ func (cm *Docker) Loop() { } } -// Get a single container, creating one anew if not existing +// MustGet gets a single container, creating one anew if not existing func (cm *Docker) MustGet(id string) *container.Container { c, ok := cm.Get(id) // append container struct for new containers @@ -177,7 +177,7 @@ func (cm *Docker) delByID(id string) { log.Infof("removed dead container: %s", id) } -// Return array of all containers, sorted by field +// All returns array of all containers, sorted by field func (cm *Docker) All() (containers container.Containers) { cm.lock.Lock() for _, c := range cm.containers { diff --git a/connector/main.go b/connector/main.go index 9162d77..809a501 100644 --- a/connector/main.go +++ b/connector/main.go @@ -13,7 +13,7 @@ var ( enabled = make(map[string]func() Connector) ) -// return names for all enabled connectors on the current platform +// Enabled returns names for all enabled connectors on the current platform func Enabled() (a []string) { for k, _ := range enabled { a = append(a, k) diff --git a/connector/mock.go b/connector/mock.go index bfa30d1..59fb15b 100644 --- a/connector/mock.go +++ b/connector/mock.go @@ -73,7 +73,7 @@ func (cs *Mock) Get(id string) (*container.Container, bool) { return nil, false } -// Return array of all containers, sorted by field +// All returns array of all containers, sorted by field func (cs *Mock) All() container.Containers { cs.containers.Sort() cs.containers.Filter() diff --git a/connector/runc.go b/connector/runc.go index 796e2f0..e1b7638 100644 --- a/connector/runc.go +++ b/connector/runc.go @@ -168,7 +168,7 @@ func (cm *Runc) Loop() { } } -// Get a single ctop container in the map matching libc container, creating one anew if not existing +// MustGet gets a single ctop container in the map matching libc container, creating one anew if not existing func (cm *Runc) MustGet(id string) *container.Container { c, ok := cm.Get(id) if !ok { @@ -216,7 +216,7 @@ func (cm *Runc) delByID(id string) { log.Infof("removed dead container: %s", id) } -// Return array of all containers, sorted by field +// All returns array of all containers, sorted by field func (cm *Runc) All() (containers container.Containers) { cm.lock.Lock() for _, c := range cm.containers { diff --git a/container/main.go b/container/main.go index b4bd0b3..258f9d1 100644 --- a/container/main.go +++ b/container/main.go @@ -74,7 +74,7 @@ func (c *Container) SetState(s string) { } } -// Return container log collector +// Logs returns container log collector func (c *Container) Logs() collector.LogCollector { return c.collector.Logs() } diff --git a/cursor.go b/cursor.go index aaa1ec9..1001074 100644 --- a/cursor.go +++ b/cursor.go @@ -65,7 +65,7 @@ func (gc *GridCursor) Reset() { } } -// Return current cursor index +// Idx returns current cursor index func (gc *GridCursor) Idx() int { for n, c := range gc.filtered { if c.Id == gc.selectedID { diff --git a/cwidgets/single/main.go b/cwidgets/single/main.go index 934e212..8140b53 100644 --- a/cwidgets/single/main.go +++ b/cwidgets/single/main.go @@ -70,7 +70,7 @@ func (e *Single) SetMetrics(m models.Metrics) { e.IO.Update(m.IOBytesRead, m.IOBytesWrite) } -// Return total column height +// GetHeight returns total column height func (e *Single) GetHeight() (h int) { h += e.Info.Height h += e.Net.Height diff --git a/widgets/menu/main.go b/widgets/menu/main.go index a2e6f2a..4e4e83b 100644 --- a/widgets/menu/main.go +++ b/widgets/menu/main.go @@ -42,7 +42,7 @@ func (m *Menu) AddItems(items ...Item) { m.refresh() } -// Remove menu item by value or label +// DelItem removes menu item by value or label func (m *Menu) DelItem(s string) (success bool) { for n, i := range m.items { if i.Val == s || i.Label == s { From 1b441db189aa7e825793cd1dafab5dc9069b7fb5 Mon Sep 17 00:00:00 2001 From: Nemo Date: Wed, 17 Apr 2019 00:43:15 +0530 Subject: [PATCH 15/26] Switches to a read-only socket mount --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dbcad36..a3af009 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ sudo chmod +x /usr/local/bin/ctop ```bash docker run --rm -ti \ --name=ctop \ - -v /var/run/docker.sock:/var/run/docker.sock \ + --volume /var/run/docker.sock:/var/run/docker.sock:ro \ quay.io/vektorlab/ctop:latest ``` From b8c38d09efd54cf27547fc6b2697cbdd866d8498 Mon Sep 17 00:00:00 2001 From: Bradley Cicenas Date: Sun, 12 May 2019 20:23:29 +0000 Subject: [PATCH 16/26] add exec shortcut key to container menu --- menus.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/menus.go b/menus.go index 52d5caf..5c893a6 100644 --- a/menus.go +++ b/menus.go @@ -135,7 +135,7 @@ func ContainerMenu() MenuFn { items = append(items, menu.Item{Val: "stop", Label: "[s] stop"}) items = append(items, menu.Item{Val: "pause", Label: "[p] pause"}) items = append(items, menu.Item{Val: "restart", Label: "[r] restart"}) - items = append(items, menu.Item{Val: "exec shell", Label: "[e]xec shell"}) + items = append(items, menu.Item{Val: "exec", Label: "[e] exec shell"}) } if c.Meta["state"] == "exited" || c.Meta["state"] == "created" { items = append(items, menu.Item{Val: "start", Label: "[s] start"}) @@ -184,6 +184,10 @@ func ContainerMenu() MenuFn { }) } if c.Meta["state"] == "running" { + ui.Handle("/sys/kbd/e", func(ui.Event) { + selected = "exec" + ui.StopLoop() + }) ui.Handle("/sys/kbd/r", func(ui.Event) { selected = "restart" ui.StopLoop() @@ -212,7 +216,7 @@ func ContainerMenu() MenuFn { nextMenu = SingleView case "logs": nextMenu = LogMenu - case "exec shell": + case "exec": nextMenu = ExecShell case "start": nextMenu = Confirm(confirmTxt("start", c.GetMeta("name")), c.Start) From d187e8c623fea0006b4eb5d7c163f8b4b813c1eb Mon Sep 17 00:00:00 2001 From: Bradley Cicenas Date: Sun, 12 May 2019 20:23:54 +0000 Subject: [PATCH 17/26] drop potentially empty initial frames during exec attach --- connector/manager/docker.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/connector/manager/docker.go b/connector/manager/docker.go index 1e8a91b..5b683fc 100644 --- a/connector/manager/docker.go +++ b/connector/manager/docker.go @@ -22,11 +22,11 @@ func NewDocker(client *api.Client, id string) *Docker { // Do not allow to close reader (i.e. /dev/stdin which docker client tries to close after command execution) type noClosableReader struct { - wrappedReader io.Reader + io.Reader } func (w *noClosableReader) Read(p []byte) (n int, err error) { - return w.wrappedReader.Read(p) + return w.Reader.Read(p) } const ( @@ -35,9 +35,7 @@ const ( STDERR = 2 ) -var ( - wrongFrameFormat = errors.New("Wrong frame format") -) +var wrongFrameFormat = errors.New("Wrong frame format") // A frame has a Header and a Payload // Header: [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} @@ -54,6 +52,11 @@ type frameWriter struct { } func (w *frameWriter) Write(p []byte) (n int, err error) { + // drop initial empty frames + if len(p) == 0 { + return 0, nil + } + if len(p) > 8 { var targetWriter io.Writer switch p[0] { From 73986d273285c8b75fffb80d29e9d5b6c29101b1 Mon Sep 17 00:00:00 2001 From: Stanislav Pavlovichev Date: Mon, 13 May 2019 15:47:22 +0300 Subject: [PATCH 18/26] Enable cursor --- menus.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menus.go b/menus.go index 5c893a6..16c18b7 100644 --- a/menus.go +++ b/menus.go @@ -275,7 +275,7 @@ func ExecShell() MenuFn { defer ui.DefaultEvtStream.ResetHandlers() shell := config.Get("shell") - if err := c.Exec([]string{shell.Val, "-c", "echo '\033[0m' && clear && " + shell.Val}); err != nil { + if err := c.Exec([]string{shell.Val, "-c", "printf '\\e[0m\\e[?25h' && clear && " + shell.Val}); err != nil { log.Fatal(err) } From 98fcfe8b6f3317e04339745a77a02bc13bba1240 Mon Sep 17 00:00:00 2001 From: Bradley Cicenas Date: Wed, 22 May 2019 16:58:55 +0000 Subject: [PATCH 19/26] refactor connectors for retry logic, add error view --- connector/docker.go | 46 ++++++++++++++++++------ connector/main.go | 85 ++++++++++++++++++++++++++++++++++++++++----- connector/mock.go | 13 +++++-- connector/runc.go | 56 +++++++++++++++++------------ cursor.go | 37 +++++++++++--------- go.mod | 1 + grid.go | 61 +++++++++++++++++++++++++++++--- main.go | 17 +++++---- widgets/error.go | 38 ++++++++++++++++++++ widgets/header.go | 2 +- 10 files changed, 284 insertions(+), 72 deletions(-) create mode 100644 widgets/error.go diff --git a/connector/docker.go b/connector/docker.go index d80f729..6595059 100644 --- a/connector/docker.go +++ b/connector/docker.go @@ -17,27 +17,45 @@ type Docker struct { client *api.Client containers map[string]*container.Container needsRefresh chan string // container IDs requiring refresh + closed chan struct{} lock sync.RWMutex } -func NewDocker() Connector { +func NewDocker() (Connector, error) { // init docker client client, err := api.NewClientFromEnv() if err != nil { - panic(err) + return nil, err } cm := &Docker{ client: client, containers: make(map[string]*container.Container), needsRefresh: make(chan string, 60), + closed: make(chan struct{}), lock: sync.RWMutex{}, } + + // query info as pre-flight healthcheck + info, err := client.Info() + if err != nil { + return nil, err + } + + log.Debugf("docker-connector ID: %s", info.ID) + log.Debugf("docker-connector Driver: %s", info.Driver) + log.Debugf("docker-connector Images: %d", info.Images) + log.Debugf("docker-connector Name: %s", info.Name) + log.Debugf("docker-connector ServerVersion: %s", info.ServerVersion) + go cm.Loop() cm.refreshAll() go cm.watchEvents() - return cm + return cm, nil } +// Docker implements Connector +func (cm *Docker) Wait() struct{} { return <-cm.closed } + // Docker events watcher func (cm *Docker) watchEvents() { log.Info("docker event listener starting") @@ -60,6 +78,8 @@ func (cm *Docker) watchEvents() { cm.delByID(e.ID) } } + log.Info("docker event listener exited") + close(cm.closed) } func portsFormat(ports map[api.Port][]api.PortBinding) string { @@ -114,7 +134,7 @@ func (cm *Docker) inspect(id string) *api.Container { c, err := cm.client.InspectContainer(id) if err != nil { if _, ok := err.(*api.NoSuchContainer); !ok { - log.Errorf(err.Error()) + log.Errorf("%s (%T)", err.Error(), err) } } return c @@ -125,7 +145,8 @@ func (cm *Docker) refreshAll() { opts := api.ListContainersOptions{All: true} allContainers, err := cm.client.ListContainers(opts) if err != nil { - panic(err) + log.Errorf("%s (%T)", err.Error(), err) + return } for _, i := range allContainers { @@ -137,9 +158,14 @@ func (cm *Docker) refreshAll() { } func (cm *Docker) Loop() { - for id := range cm.needsRefresh { - c := cm.MustGet(id) - cm.refresh(c) + for { + select { + case id := <-cm.needsRefresh: + c := cm.MustGet(id) + cm.refresh(c) + case <-cm.closed: + return + } } } @@ -161,7 +187,7 @@ func (cm *Docker) MustGet(id string) *container.Container { return c } -// Get a single container, by ID +// Docker implements Connector func (cm *Docker) Get(id string) (*container.Container, bool) { cm.lock.Lock() c, ok := cm.containers[id] @@ -177,7 +203,7 @@ func (cm *Docker) delByID(id string) { log.Infof("removed dead container: %s", id) } -// All returns array of all containers, sorted by field +// Docker implements Connector func (cm *Docker) All() (containers container.Containers) { cm.lock.Lock() for _, c := range cm.containers { diff --git a/connector/main.go b/connector/main.go index 809a501..61e3a13 100644 --- a/connector/main.go +++ b/connector/main.go @@ -3,6 +3,8 @@ package connector import ( "fmt" "sort" + "sync" + "time" "github.com/bcicen/ctop/container" "github.com/bcicen/ctop/logging" @@ -10,9 +12,79 @@ import ( var ( log = logging.Init() - enabled = make(map[string]func() Connector) + enabled = make(map[string]ConnectorFn) ) +type ConnectorFn func() (Connector, error) + +type Connector interface { + // All returns a pre-sorted container.Containers of all discovered containers + All() container.Containers + // Get returns a single container.Container by ID + Get(string) (*container.Container, bool) + // Wait waits for the underlying connection to be lost before returning + Wait() struct{} +} + +// ConnectorSuper provides initial connection and retry on failure for +// an undlerying Connector type +type ConnectorSuper struct { + conn Connector + connFn ConnectorFn + err error + lock sync.RWMutex +} + +func NewConnectorSuper(connFn ConnectorFn) *ConnectorSuper { + cs := &ConnectorSuper{ + connFn: connFn, + err: fmt.Errorf("connecting..."), + } + go cs.loop() + return cs +} + +// Get returns the underlying Connector, or nil and an error +// if the Connector is not yet initialized or is disconnected. +func (cs *ConnectorSuper) Get() (Connector, error) { + cs.lock.RLock() + defer cs.lock.RUnlock() + if cs.err != nil { + return nil, cs.err + } + return cs.conn, nil +} + +func (cs *ConnectorSuper) setError(err error) { + cs.lock.Lock() + defer cs.lock.Unlock() + cs.err = err +} + +func (cs *ConnectorSuper) loop() { + const interval = 3 + for { + log.Infof("initializing connector") + + conn, err := cs.connFn() + if err != nil { + cs.setError(fmt.Errorf("%s\n\nattempting to reconnect...", err)) + log.Errorf("failed to initialize connector: %s (%T)", err, err) + log.Errorf("retrying in %ds", interval) + time.Sleep(interval * time.Second) + } else { + cs.conn = conn + cs.setError(nil) + log.Infof("successfully initialized connector") + + // wait until connection closed + cs.conn.Wait() + cs.setError(fmt.Errorf("attempting to reconnect...")) + log.Infof("connector closed") + } + } +} + // Enabled returns names for all enabled connectors on the current platform func Enabled() (a []string) { for k, _ := range enabled { @@ -22,14 +94,11 @@ func Enabled() (a []string) { return a } -func ByName(s string) (Connector, error) { +// ByName returns a ConnectorSuper for a given name, or error if the connector +// does not exists on the current platform +func ByName(s string) (*ConnectorSuper, error) { if cfn, ok := enabled[s]; ok { - return cfn(), nil + return NewConnectorSuper(cfn), nil } return nil, fmt.Errorf("invalid connector type \"%s\"", s) } - -type Connector interface { - All() container.Containers - Get(string) (*container.Container, bool) -} diff --git a/connector/mock.go b/connector/mock.go index 59fb15b..d96496b 100644 --- a/connector/mock.go +++ b/connector/mock.go @@ -20,11 +20,11 @@ type Mock struct { containers container.Containers } -func NewMock() Connector { +func NewMock() (Connector, error) { cs := &Mock{} go cs.Init() go cs.Loop() - return cs + return cs, nil } // Create Mock containers @@ -41,6 +41,15 @@ func (cs *Mock) Init() { } +func (cs *Mock) Wait() struct{} { + ch := make(chan struct{}) + go func() { + time.Sleep(30 * time.Second) + close(ch) + }() + return <-ch +} + func (cs *Mock) makeContainer(aggression int64) { collector := collector.NewMock(aggression) manager := manager.NewMock() diff --git a/connector/runc.go b/connector/runc.go index e1b7638..c9f7c87 100644 --- a/connector/runc.go +++ b/connector/runc.go @@ -54,35 +54,44 @@ type Runc struct { factory libcontainer.Factory containers map[string]*container.Container libContainers map[string]libcontainer.Container + closed chan struct{} needsRefresh chan string // container IDs requiring refresh lock sync.RWMutex } -func NewRunc() Connector { +func NewRunc() (Connector, error) { opts, err := NewRuncOpts() - runcFailOnErr(err) + if err != nil { + return nil, err + } factory, err := getFactory(opts) - runcFailOnErr(err) + if err != nil { + return nil, err + } cm := &Runc{ opts: opts, factory: factory, containers: make(map[string]*container.Container), libContainers: make(map[string]libcontainer.Container), - needsRefresh: make(chan string, 60), + closed: make(chan struct{}), lock: sync.RWMutex{}, } go func() { for { - cm.refreshAll() - time.Sleep(5 * time.Second) + select { + case <-cm.closed: + return + case <-time.After(5 * time.Second): + cm.refreshAll() + } } }() go cm.Loop() - return cm + return cm, nil } func (cm *Runc) GetLibc(id string) libcontainer.Container { @@ -141,7 +150,11 @@ func (cm *Runc) refresh(id string) { // Read runc root, creating any new containers func (cm *Runc) refreshAll() { list, err := ioutil.ReadDir(cm.opts.root) - runcFailOnErr(err) + if err != nil { + log.Errorf("%s (%T)", err.Error(), err) + close(cm.closed) + return + } for _, i := range list { if i.IsDir() { @@ -199,14 +212,6 @@ func (cm *Runc) MustGet(id string) *container.Container { return c } -// Get a single container, by ID -func (cm *Runc) Get(id string) (*container.Container, bool) { - cm.lock.Lock() - defer cm.lock.Unlock() - c, ok := cm.containers[id] - return c, ok -} - // Remove containers by ID func (cm *Runc) delByID(id string) { cm.lock.Lock() @@ -216,7 +221,18 @@ func (cm *Runc) delByID(id string) { log.Infof("removed dead container: %s", id) } -// All returns array of all containers, sorted by field +// Runc implements Connector +func (cm *Runc) Wait() struct{} { return <-cm.closed } + +// Runc implements Connector +func (cm *Runc) Get(id string) (*container.Container, bool) { + cm.lock.Lock() + defer cm.lock.Unlock() + c, ok := cm.containers[id] + return c, ok +} + +// Runc implements Connector func (cm *Runc) All() (containers container.Containers) { cm.lock.Lock() for _, c := range cm.containers { @@ -239,9 +255,3 @@ func getFactory(opts RuncOpts) (libcontainer.Factory, error) { } return libcontainer.New(opts.root, cgroupManager) } - -func runcFailOnErr(err error) { - if err != nil { - panic(fmt.Errorf("fatal runc error: %s", err)) - } -} diff --git a/cursor.go b/cursor.go index 1001074..6c5a6e8 100644 --- a/cursor.go +++ b/cursor.go @@ -11,7 +11,7 @@ import ( type GridCursor struct { selectedID string // id of currently selected container filtered container.Containers - cSource connector.Connector + cSuper *connector.ConnectorSuper isScrolling bool // toggled when actively scrolling } @@ -25,14 +25,20 @@ func (gc *GridCursor) Selected() *container.Container { return nil } -// Refresh containers from source -func (gc *GridCursor) RefreshContainers() (lenChanged bool) { +// Refresh containers from source, returning whether the quantity of +// containers has changed and any error +func (gc *GridCursor) RefreshContainers() (bool, error) { oldLen := gc.Len() - - // Containers filtered by display bool gc.filtered = container.Containers{} + + cSource, err := gc.cSuper.Get() + if err != nil { + return true, err + } + + // filter Containers by display bool var cursorVisible bool - for _, c := range gc.cSource.All() { + for _, c := range cSource.All() { if c.Display { if c.Id == gc.selectedID { cursorVisible = true @@ -41,22 +47,21 @@ func (gc *GridCursor) RefreshContainers() (lenChanged bool) { } } - if oldLen != gc.Len() { - lenChanged = true - } - - if !cursorVisible { + if !cursorVisible || gc.selectedID == "" { gc.Reset() } - if gc.selectedID == "" { - gc.Reset() - } - return lenChanged + + return oldLen != gc.Len(), nil } // Set an initial cursor position, if possible func (gc *GridCursor) Reset() { - for _, c := range gc.cSource.All() { + cSource, err := gc.cSuper.Get() + if err != nil { + return + } + + for _, c := range cSource.All() { c.Widgets.UnHighlight() } if gc.Len() > 0 { diff --git a/go.mod b/go.mod index 4921cba..9afb509 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( 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/pkg/errors v0.8.1 github.com/pmezard/go-difflib v1.0.0 // indirect github.com/seccomp/libseccomp-golang v0.0.0-20150813023252-1b506fc7c24e // indirect github.com/stretchr/testify v1.2.2 // indirect diff --git a/grid.go b/grid.go index 871d3cb..882d730 100644 --- a/grid.go +++ b/grid.go @@ -6,6 +6,43 @@ import ( ui "github.com/gizak/termui" ) +func ShowConnError(err error) (exit bool) { + ui.Clear() + ui.DefaultEvtStream.ResetHandlers() + defer ui.DefaultEvtStream.ResetHandlers() + + setErr := func(err error) { + errView.Text = err.Error() + ui.Render(errView) + } + + HandleKeys("exit", func() { + exit = true + ui.StopLoop() + }) + + ui.Handle("/timer/1s", func(ui.Event) { + _, err := cursor.RefreshContainers() + if err == nil { + ui.StopLoop() + return + } + setErr(err) + }) + + ui.Handle("/sys/wnd/resize", func(e ui.Event) { + errView.Resize() + ui.Clear() + ui.Render(errView) + log.Infof("RESIZE") + }) + + errView.Resize() + setErr(err) + ui.Loop() + return exit +} + func RedrawRows(clr bool) { // reinit body rows cGrid.Clear() @@ -33,7 +70,6 @@ func RedrawRows(clr bool) { } cGrid.Align() ui.Render(cGrid) - } func SingleView() MenuFn { @@ -68,16 +104,21 @@ func SingleView() MenuFn { return nil } -func RefreshDisplay() { +func RefreshDisplay() error { // skip display refresh during scroll if !cursor.isScrolling { - needsClear := cursor.RefreshContainers() + needsClear, err := cursor.RefreshContainers() + if err != nil { + return err + } RedrawRows(needsClear) } + return nil } func Display() bool { var menu MenuFn + var connErr error cGrid.SetWidth(ui.TermWidth()) ui.DefaultEvtStream.Hook(logEvent) @@ -126,7 +167,10 @@ func Display() bool { }) ui.Handle("/sys/kbd/a", func(ui.Event) { config.Toggle("allContainers") - RefreshDisplay() + connErr = RefreshDisplay() + if connErr != nil { + ui.StopLoop() + } }) ui.Handle("/sys/kbd/D", func(ui.Event) { dumpContainer(cursor.Selected()) @@ -160,7 +204,10 @@ func Display() bool { if log.StatusQueued() { ui.StopLoop() } - RefreshDisplay() + connErr = RefreshDisplay() + if connErr != nil { + ui.StopLoop() + } }) ui.Handle("/sys/wnd/resize", func(e ui.Event) { @@ -174,6 +221,10 @@ func Display() bool { ui.Loop() + if connErr != nil { + return ShowConnError(connErr) + } + if log.StatusQueued() { for sm := range log.FlushStatus() { if sm.IsError { diff --git a/main.go b/main.go index f2eac66..cd6a184 100644 --- a/main.go +++ b/main.go @@ -22,11 +22,12 @@ var ( version = "dev-build" goVersion = runtime.Version() - log *logging.CTopLogger - cursor *GridCursor - cGrid *compact.CompactGrid - header *widgets.CTopHeader - status *widgets.StatusLine + log *logging.CTopLogger + cursor *GridCursor + cGrid *compact.CompactGrid + header *widgets.CTopHeader + status *widgets.StatusLine + errView *widgets.ErrorView versionStr = fmt.Sprintf("ctop version %v, build %v %v", version, build, goVersion) ) @@ -104,14 +105,15 @@ func main() { defer Shutdown() // init grid, cursor, header - conn, err := connector.ByName(*connectorFlag) + cSuper, err := connector.ByName(*connectorFlag) if err != nil { panic(err) } - cursor = &GridCursor{cSource: conn} + cursor = &GridCursor{cSuper: cSuper} cGrid = compact.NewCompactGrid() header = widgets.NewCTopHeader() status = widgets.NewStatusLine() + errView = widgets.NewErrorView() for { exit := Display() @@ -140,6 +142,7 @@ func validSort(s string) { func panicExit() { if r := recover(); r != nil { Shutdown() + panic(r) fmt.Printf("error: %s\n", r) os.Exit(1) } diff --git a/widgets/error.go b/widgets/error.go new file mode 100644 index 0000000..458ccaa --- /dev/null +++ b/widgets/error.go @@ -0,0 +1,38 @@ +package widgets + +import ( + "fmt" + ui "github.com/gizak/termui" +) + +type ErrorView struct { + *ui.Par +} + +func NewErrorView() *ErrorView { + p := ui.NewPar("") + p.Border = true + p.Height = 10 + p.Width = 20 + p.PaddingTop = 1 + p.PaddingBottom = 1 + p.PaddingLeft = 2 + p.PaddingRight = 2 + p.Bg = ui.ThemeAttr("bg") + p.TextFgColor = ui.ThemeAttr("status.warn") + p.TextBgColor = ui.ThemeAttr("menu.text.bg") + p.BorderFg = ui.ThemeAttr("status.warn") + p.BorderLabelFg = ui.ThemeAttr("status.warn") + return &ErrorView{p} +} + +func (w *ErrorView) Buffer() ui.Buffer { + w.BorderLabel = fmt.Sprintf(" %s ", timeStr()) + return w.Par.Buffer() +} + +func (w *ErrorView) Resize() { + w.SetX(ui.TermWidth() / 12) + w.SetY(ui.TermHeight() / 3) + w.SetWidth(w.X * 10) +} diff --git a/widgets/header.go b/widgets/header.go index 4a96f8f..a7ab786 100644 --- a/widgets/header.go +++ b/widgets/header.go @@ -16,7 +16,7 @@ type CTopHeader struct { func NewCTopHeader() *CTopHeader { return &CTopHeader{ - Time: headerPar(2, timeStr()), + Time: headerPar(2, ""), Count: headerPar(24, "-"), Filter: headerPar(40, ""), bg: headerBg(), From 0a5a4c9062ebfb005e492f73189818a6ecd3f8ed Mon Sep 17 00:00:00 2001 From: Bradley Cicenas Date: Wed, 22 May 2019 17:38:01 +0000 Subject: [PATCH 20/26] add multi-line scrolling support, timestamps to error view --- connector/main.go | 2 +- grid.go | 5 +++-- widgets/error.go | 40 +++++++++++++++++++++++++++++++--------- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/connector/main.go b/connector/main.go index 61e3a13..0907a77 100644 --- a/connector/main.go +++ b/connector/main.go @@ -68,7 +68,7 @@ func (cs *ConnectorSuper) loop() { conn, err := cs.connFn() if err != nil { - cs.setError(fmt.Errorf("%s\n\nattempting to reconnect...", err)) + cs.setError(err) log.Errorf("failed to initialize connector: %s (%T)", err, err) log.Errorf("retrying in %ds", interval) time.Sleep(interval * time.Second) diff --git a/grid.go b/grid.go index 882d730..2e48c8b 100644 --- a/grid.go +++ b/grid.go @@ -12,7 +12,8 @@ func ShowConnError(err error) (exit bool) { defer ui.DefaultEvtStream.ResetHandlers() setErr := func(err error) { - errView.Text = err.Error() + errView.Append(err.Error()) + errView.Append("attempting to reconnect...") ui.Render(errView) } @@ -21,7 +22,7 @@ func ShowConnError(err error) (exit bool) { ui.StopLoop() }) - ui.Handle("/timer/1s", func(ui.Event) { + ui.Handle("/timer/2s", func(ui.Event) { _, err := cursor.RefreshContainers() if err == nil { ui.StopLoop() diff --git a/widgets/error.go b/widgets/error.go index 458ccaa..7f6a4a9 100644 --- a/widgets/error.go +++ b/widgets/error.go @@ -2,37 +2,59 @@ package widgets import ( "fmt" + "strings" + "time" + ui "github.com/gizak/termui" ) type ErrorView struct { *ui.Par + lines []string } func NewErrorView() *ErrorView { + const yPad = 1 + const xPad = 2 + p := ui.NewPar("") + p.X = xPad + p.Y = yPad p.Border = true p.Height = 10 p.Width = 20 - p.PaddingTop = 1 - p.PaddingBottom = 1 - p.PaddingLeft = 2 - p.PaddingRight = 2 + p.PaddingTop = yPad + p.PaddingBottom = yPad + p.PaddingLeft = xPad + p.PaddingRight = xPad + p.BorderLabel = " ctop - error " p.Bg = ui.ThemeAttr("bg") p.TextFgColor = ui.ThemeAttr("status.warn") p.TextBgColor = ui.ThemeAttr("menu.text.bg") p.BorderFg = ui.ThemeAttr("status.warn") p.BorderLabelFg = ui.ThemeAttr("status.warn") - return &ErrorView{p} + return &ErrorView{p, make([]string, 0, 50)} +} + +func (w *ErrorView) Append(s string) { + if len(w.lines)+2 >= cap(w.lines) { + w.lines = append(w.lines[:0], w.lines[2:]...) + } + ts := time.Now().Local().Format("15:04:05 MST") + w.lines = append(w.lines, fmt.Sprintf("[%s] %s", ts, s)) + w.lines = append(w.lines, "") } func (w *ErrorView) Buffer() ui.Buffer { - w.BorderLabel = fmt.Sprintf(" %s ", timeStr()) + offset := len(w.lines) - w.InnerHeight() + if offset < 0 { + offset = 0 + } + w.Text = strings.Join(w.lines[offset:len(w.lines)], "\n") return w.Par.Buffer() } func (w *ErrorView) Resize() { - w.SetX(ui.TermWidth() / 12) - w.SetY(ui.TermHeight() / 3) - w.SetWidth(w.X * 10) + w.Height = ui.TermHeight() - (w.PaddingTop + w.PaddingBottom) + w.SetWidth(ui.TermWidth() - (w.PaddingLeft + w.PaddingRight)) } From c8ac3316525398371f4bcebda6f6dba438259a5b Mon Sep 17 00:00:00 2001 From: Bradley Cicenas Date: Wed, 22 May 2019 17:39:37 +0000 Subject: [PATCH 21/26] fix timer --- grid.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grid.go b/grid.go index 2e48c8b..655e000 100644 --- a/grid.go +++ b/grid.go @@ -22,7 +22,7 @@ func ShowConnError(err error) (exit bool) { ui.StopLoop() }) - ui.Handle("/timer/2s", func(ui.Event) { + ui.Handle("/timer/1s", func(ui.Event) { _, err := cursor.RefreshContainers() if err == nil { ui.StopLoop() From 4c4f041b4063898cc7f4b75402f6abc65265e52b Mon Sep 17 00:00:00 2001 From: Bradley Cicenas Date: Sat, 22 Jun 2019 18:42:48 +0000 Subject: [PATCH 22/26] improve health check visibility --- cwidgets/compact/status.go | 28 +++++++++++++--------------- cwidgets/compact/util.go | 2 +- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/cwidgets/compact/status.go b/cwidgets/compact/status.go index eec4c96..d6b920f 100644 --- a/cwidgets/compact/status.go +++ b/cwidgets/compact/status.go @@ -5,8 +5,8 @@ import ( ) const ( - mark = string('\u25C9') - healthMark = string('\u207A') + mark = "◉" + healthMark = "✚" vBar = string('\u25AE') + string('\u25AE') ) @@ -18,7 +18,10 @@ type Status struct { } func NewStatus() *Status { - s := &Status{Block: ui.NewBlock()} + s := &Status{ + Block: ui.NewBlock(), + health: []ui.Cell{{Ch: ' '}}, + } s.Height = 1 s.Border = false s.Set("") @@ -28,11 +31,12 @@ func NewStatus() *Status { func (s *Status) Buffer() ui.Buffer { buf := s.Block.Buffer() x := 0 - for _, c := range s.status { + for _, c := range s.health { buf.Set(s.InnerX()+x, s.InnerY(), c) x += c.Width() } - for _, c := range s.health { + x += 1 + for _, c := range s.status { buf.Set(s.InnerX()+x, s.InnerY(), c) x += c.Width() } @@ -53,18 +57,16 @@ func (s *Status) Set(val string) { text = vBar } - var cells []ui.Cell - for _, ch := range text { - cells = append(cells, ui.Cell{Ch: ch, Fg: color}) - } - s.status = cells + s.status = ui.TextCells(text, color, ui.ColorDefault) } func (s *Status) SetHealth(val string) { if val == "" { return } + color := ui.ColorDefault + mark := healthMark switch val { case "healthy": @@ -75,9 +77,5 @@ func (s *Status) SetHealth(val string) { color = ui.ThemeAttr("status.warn") } - var cells []ui.Cell - for _, ch := range healthMark { - cells = append(cells, ui.Cell{Ch: ch, Fg: color}) - } - s.health = cells + s.health = ui.TextCells(mark, color, ui.ColorDefault) } diff --git a/cwidgets/compact/util.go b/cwidgets/compact/util.go index e634ed9..9bd8f66 100644 --- a/cwidgets/compact/util.go +++ b/cwidgets/compact/util.go @@ -12,7 +12,7 @@ const colSpacing = 1 // per-column width. 0 == auto width var colWidths = []int{ - 3, // status + 5, // status 0, // name 0, // cid 0, // cpu From 331f50f03e9cf7f4586d4f31c5475fe1763f631b Mon Sep 17 00:00:00 2001 From: Bradley Cicenas Date: Sat, 22 Jun 2019 18:44:07 +0000 Subject: [PATCH 23/26] update status doc image --- _docs/img/status.png | Bin 7550 -> 6615 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/_docs/img/status.png b/_docs/img/status.png index 3343d492219a0e6aeba3be5594cee0fc8ec8a6eb..86d03e83f9688d5bde38dce030bb6497b391df19 100644 GIT binary patch literal 6615 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 literal 7550 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 Vkp0`>^*#k4C#5V|DQ+70e*mS!l#T!Z From b7d81485f97fb517accc4a589492e713fc483410 Mon Sep 17 00:00:00 2001 From: Bradley Cicenas Date: Sun, 23 Jun 2019 09:04:27 +0000 Subject: [PATCH 24/26] update dockerclient, runc deps --- go.mod | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 9afb509..cb8db3f 100644 --- a/go.mod +++ b/go.mod @@ -1,42 +1,30 @@ 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/checkpoint-restore/go-criu v0.0.0-20190109184317-bdb7599cd87b // indirect + github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50 // indirect 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/cyphar/filepath-securejoin v0.2.2 // indirect + github.com/fsouza/go-dockerclient v1.4.1 github.com/gizak/termui v2.3.0+incompatible github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55 // indirect - github.com/golang/protobuf v0.0.0-20170712042213-0a4f71a498b7 // indirect - github.com/hashicorp/go-cleanhttp v0.0.0-20170211013415-3573b8b52aa7 // indirect github.com/jgautheron/codename-generator v0.0.0-20150829203204-16d037c7cc3c - github.com/kr/pretty v0.1.0 // indirect - github.com/maruel/panicparse v0.0.0-20170227222818-25bcac0d793c // indirect - github.com/maruel/ut v1.0.0 // 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/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618 // 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/opencontainers/runc v1.0.0-rc8 + github.com/opencontainers/runtime-spec v1.0.1 // indirect + github.com/opencontainers/selinux v1.2.2 // indirect github.com/pkg/errors v0.8.1 - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/seccomp/libseccomp-golang v0.0.0-20150813023252-1b506fc7c24e // indirect - github.com/stretchr/testify v1.2.2 // indirect - github.com/syndtr/gocapability v0.0.0-20150716010906-2c00daeb6c3b // indirect + github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 // indirect github.com/vishvananda/netlink v0.0.0-20150820014904-1e2e08e8a2dc // indirect github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect - golang.org/x/net v0.0.0-20170308210134-a6577fac2d73 // indirect - golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect - golang.org/x/sys v0.0.0-20170308153327-99f16d856c98 // indirect ) replace github.com/gizak/termui => github.com/bcicen/termui v0.0.0-20180326052246-4eb80249d3f5 From d8c7dd4c5c55ae6b279b97f03c122c0dea692e5c Mon Sep 17 00:00:00 2001 From: Bradley Cicenas Date: Mon, 24 Jun 2019 17:34:04 +0000 Subject: [PATCH 25/26] move image to go 1.12 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 4617766..fab2a93 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM quay.io/vektorcloud/go:1.11 +FROM quay.io/vektorcloud/go:1.12 RUN apk add --no-cache make From ddd80b4af92fc3fe3716f7f2706db7d9387a40fb Mon Sep 17 00:00:00 2001 From: Alexandr Kozlenkov Date: Fri, 12 Jul 2019 23:18:10 +0300 Subject: [PATCH 26/26] Added script for instaling minikube --- prepare-minikube.sh | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 prepare-minikube.sh diff --git a/prepare-minikube.sh b/prepare-minikube.sh new file mode 100644 index 0000000..ae64a59 --- /dev/null +++ b/prepare-minikube.sh @@ -0,0 +1,6 @@ +#/bin/sh + +curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 \ + && chmod +x minikube + +sudo install minikube /usr/local/bin