From 71ab2c9721408f23f6cfe2a38ebd355e491be7df Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Thu, 29 Oct 2020 15:16:48 +0200 Subject: [PATCH] Inspect container --- connector/manager/docker.go | 14 +++++++++ connector/manager/main.go | 1 + connector/manager/mock.go | 4 +++ connector/manager/runc.go | 4 +++ container/main.go | 4 +++ grid.go | 4 +++ menus.go | 61 +++++++++++++++++++++++++++++++++++++ 7 files changed, 92 insertions(+) diff --git a/connector/manager/docker.go b/connector/manager/docker.go index 5b683fc..471d428 100644 --- a/connector/manager/docker.go +++ b/connector/manager/docker.go @@ -1,6 +1,7 @@ package manager import ( + "encoding/json" "fmt" api "github.com/fsouza/go-dockerclient" "github.com/pkg/errors" @@ -102,6 +103,19 @@ func (dc *Docker) Exec(cmd []string) error { }) } +func (dc *Docker) Inspect() string { + i, err := dc.client.InspectContainer(dc.id) + if err != nil { + return err.Error() + } + // Convert Container struct back to JSON but pretty print + out, err := json.MarshalIndent(i, "", " ") + if err != nil { + return err.Error() + } + return string(out) +} + 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 1fcae1d..68c54d9 100644 --- a/connector/manager/main.go +++ b/connector/manager/main.go @@ -12,4 +12,5 @@ type Manager interface { Unpause() error Restart() error Exec(cmd []string) error + Inspect() string } diff --git a/connector/manager/mock.go b/connector/manager/mock.go index 0438f86..c8b40d3 100644 --- a/connector/manager/mock.go +++ b/connector/manager/mock.go @@ -33,3 +33,7 @@ func (m *Mock) Restart() error { func (m *Mock) Exec(cmd []string) error { return ActionNotImplErr } + +func (m *Mock) Inspect() string { + return "" +} diff --git a/connector/manager/runc.go b/connector/manager/runc.go index c4dd277..38b2722 100644 --- a/connector/manager/runc.go +++ b/connector/manager/runc.go @@ -33,3 +33,7 @@ func (rc *Runc) Restart() error { func (rc *Runc) Exec(cmd []string) error { return ActionNotImplErr } + +func (rc *Runc) Inspect() string { + return "" +} diff --git a/container/main.go b/container/main.go index e0b0c3f..c60b2e2 100644 --- a/container/main.go +++ b/container/main.go @@ -158,3 +158,7 @@ func (c *Container) Restart() { func (c *Container) Exec(cmd []string) error { return c.manager.Exec(cmd) } + +func (c *Container) Inspect() string { + return c.manager.Inspect() +} diff --git a/grid.go b/grid.go index 9efced3..a6cc62b 100644 --- a/grid.go +++ b/grid.go @@ -162,6 +162,10 @@ func Display() bool { menu = ExecShell ui.StopLoop() }) + ui.Handle("/sys/kbd/i", func(ui.Event) { + menu = InspectView + ui.StopLoop() + }) ui.Handle("/sys/kbd/o", func(ui.Event) { menu = SingleView ui.StopLoop() diff --git a/menus.go b/menus.go index c3ca2af..4915698 100644 --- a/menus.go +++ b/menus.go @@ -1,6 +1,7 @@ package main import ( + "bufio" "fmt" "strings" "time" @@ -27,6 +28,7 @@ var helpDialog = []menu.Item{ {"[o] - open single view", ""}, {"[l] - view container logs ([t] to toggle timestamp when open)", ""}, {"[e] - exec shell", ""}, + {"[i] - inspect", ""}, {"[c] - configure columns", ""}, {"[S] - save current configuration to file", ""}, {"[q] - exit ctop", ""}, @@ -214,6 +216,7 @@ func ContainerMenu() MenuFn { items := []menu.Item{ menu.Item{Val: "single", Label: "[o] single view"}, menu.Item{Val: "logs", Label: "[l] log view"}, + menu.Item{Val: "inspect", Label: "[i] inspect"}, } if c.Meta["state"] == "running" { @@ -303,6 +306,8 @@ func ContainerMenu() MenuFn { nextMenu = LogMenu case "exec": nextMenu = ExecShell + case "inspect": + nextMenu = InspectView case "start": nextMenu = Confirm(confirmTxt("start", c.GetMeta("name")), c.Start) case "stop": @@ -367,6 +372,34 @@ func ExecShell() MenuFn { return nil } +func InspectView() MenuFn { + c := cursor.Selected() + if c == nil { + return nil + } + + ui.DefaultEvtStream.ResetHandlers() + defer ui.DefaultEvtStream.ResetHandlers() + + inspectLines, quit := inspectReader(c) + m := widgets.NewTextView(inspectLines) + m.BorderLabel = fmt.Sprintf("Inspect [%s]", c.GetMeta("name")) + ui.Render(m) + + ui.Handle("/sys/wnd/resize", func(e ui.Event) { + m.Resize() + }) + ui.Handle("/sys/kbd/t", func(ui.Event) { + m.Toggle() + }) + ui.Handle("/sys/kbd/q", func(ui.Event) { + quit <- true + ui.StopLoop() + }) + ui.Loop() + return nil +} + // Create a confirmation dialog with a given description string and // func to perform if confirmed func Confirm(txt string, fn func()) MenuFn { @@ -457,4 +490,32 @@ func logReader(container *container.Container) (logs chan widgets.ToggleText, qu return } +type toggleInspect struct { + json string +} + +func (t *toggleInspect) Toggle(on bool) string { + return t.json +} + +func inspectReader(container *container.Container) (lines chan widgets.ToggleText, quit chan bool) { + inspectLines := container.Inspect() + lines = make(chan widgets.ToggleText) + quit = make(chan bool) + go func() { + // Split inspectLines to lines + scanner := bufio.NewScanner(strings.NewReader(inspectLines)) + for scanner.Scan() { + lines <- &toggleInspect{json: scanner.Text()} + } + for { + select { + case <-quit: + return + } + } + }() + return +} + func confirmTxt(a, n string) string { return fmt.Sprintf("%s container %s?", a, n) }