diff --git a/container.go b/container.go index 0e0369e..be3b85d 100644 --- a/container.go +++ b/container.go @@ -8,6 +8,7 @@ import ( type Container struct { id string + name string done chan bool stats chan *docker.Stats widgets *Widgets diff --git a/containermap.go b/containermap.go index ffff2f4..8227e45 100644 --- a/containermap.go +++ b/containermap.go @@ -1,23 +1,57 @@ package main import ( + "os" "strings" "github.com/fsouza/go-dockerclient" ) +var filters = map[string][]string{ + "status": []string{"running"}, +} + func NewContainerMap() *ContainerMap { - return &ContainerMap{ - containers: make(map[string]*Container), - sortField: "cpu", + // init docker client + host := os.Getenv("DOCKER_HOST") + if host == "" { + host = "unix:///var/run/docker.sock" } + client, err := docker.NewClient(host) + if err != nil { + panic(err) + } + + cm := &ContainerMap{ + client: client, + containers: make(map[string]*Container), + sortField: SortFields[0], + } + cm.Refresh() + return cm } type ContainerMap struct { + client *docker.Client containers map[string]*Container sortField string } +func (cm *ContainerMap) Refresh() { + opts := docker.ListContainersOptions{ + Filters: filters, + } + containers, err := cm.client.ListContainers(opts) + if err != nil { + panic(err) + } + for _, c := range containers { + if _, ok := cm.containers[c.ID[:12]]; ok == false { + cm.Add(c) + } + } +} + // Return number of containers/rows func (cm *ContainerMap) Len() uint { return uint(len(cm.containers)) @@ -28,11 +62,13 @@ func (cm *ContainerMap) Add(c docker.APIContainers) { name := strings.Replace(c.Names[0], "/", "", 1) // use primary container name cm.containers[id] = &Container{ id: id, + name: name, done: make(chan bool), stats: make(chan *docker.Stats), - widgets: NewWidgets(cid, name), + widgets: NewWidgets(id, name), reader: &StatReader{}, } + cm.containers[id].Collect(cm.client) } // Get a single container, by ID diff --git a/grid.go b/grid.go index eae3a4f..f547b45 100644 --- a/grid.go +++ b/grid.go @@ -3,6 +3,7 @@ package main import ( "fmt" + "github.com/bcicen/ctop/views" ui "github.com/gizak/termui" ) @@ -58,7 +59,7 @@ func (g *Grid) Redraw() { func header() *ui.Row { return ui.NewRow( ui.NewCol(2, 0, headerPar("NAME")), - ui.NewCol(1, 0, headerPar("CID")), + ui.NewCol(2, 0, headerPar("CID")), ui.NewCol(2, 0, headerPar("CPU")), ui.NewCol(2, 0, headerPar("MEM")), ui.NewCol(2, 0, headerPar("NET RX/TX")), @@ -74,12 +75,26 @@ func headerPar(s string) *ui.Par { return p } -func Display(g *Grid) { +type View func() + +func ResetView() { + ui.DefaultEvtStream.ResetHandlers() + ui.Clear() +} + +func OpenView(v View) { + ResetView() + defer ResetView() + v() +} + +func Display(g *Grid) bool { + var newView View + if err := ui.Init(); err != nil { panic(err) } defer ui.Close() - // calculate layout ui.Body.Align() g.Cursor() @@ -97,6 +112,10 @@ func Display(g *Grid) { g.Cursor() } }) + ui.Handle("/sys/kbd/h", func(ui.Event) { + newView = views.Help + ui.StopLoop() + }) ui.Handle("/sys/kbd/q", func(ui.Event) { ui.StopLoop() }) @@ -112,4 +131,9 @@ func Display(g *Grid) { }) ui.Loop() + if newView != nil { + OpenView(newView) + return false + } + return true } diff --git a/main.go b/main.go index 50f1ced..82f59f5 100644 --- a/main.go +++ b/main.go @@ -2,43 +2,14 @@ package main import ( "os" - - "github.com/fsouza/go-dockerclient" ) -func getContainers(client *docker.Client) []docker.APIContainers { - filters := make(map[string][]string) - filters["status"] = []string{"running"} - opts := docker.ListContainersOptions{ - Filters: filters, - } - containers, err := client.ListContainers(opts) - if err != nil { - panic(err) - } - return containers -} - func main() { - dockerhost := os.Getenv("DOCKER_HOST") - if dockerhost == "" { - dockerhost = "unix:///var/run/docker.sock" - } - - client, err := docker.NewClient(dockerhost) - if err != nil { - panic(err) - } - g := NewGrid() - for _, c := range getContainers(client) { - g.containers.Add(c) + for { + exit := Display(g) + if exit { + os.Exit(0) + } } - - for _, c := range g.containers.All() { - c.Collect(client) - } - - Display(g) - } diff --git a/sort.go b/sort.go index 7237d75..af75ad5 100644 --- a/sort.go +++ b/sort.go @@ -4,6 +4,8 @@ import ( "sort" ) +var SortFields = []string{"id", "name", "cpu", "mem"} + type ByID []*Container func (a ByID) Len() int { return len(a) } @@ -14,7 +16,7 @@ type ByName []*Container func (a ByName) Len() int { return len(a) } func (a ByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a ByName) Less(i, j int) bool { return a[i].id < a[j].id } +func (a ByName) Less(i, j int) bool { return a[i].name < a[j].name } type ByCPU []*Container diff --git a/views/help.go b/views/help.go new file mode 100644 index 0000000..d56df9a --- /dev/null +++ b/views/help.go @@ -0,0 +1,26 @@ +package views + +import ( + "strings" + + ui "github.com/gizak/termui" +) + +var helpDialog = []string{ + "[h] - open this help dialog", + "[q] - exit ctop", +} + +func Help() { + p := ui.NewPar(strings.Join(helpDialog, "\n")) + p.Height = 10 + p.Width = 50 + p.TextFgColor = ui.ColorWhite + p.BorderLabel = "Help" + p.BorderFg = ui.ColorCyan + ui.Render(p) + ui.Handle("/sys/kbd/q", func(ui.Event) { + ui.StopLoop() + }) + ui.Loop() +} diff --git a/widgets.go b/widgets.go index a92e728..43ec344 100644 --- a/widgets.go +++ b/widgets.go @@ -18,7 +18,7 @@ type Widgets struct { func (w *Widgets) MakeRow() *ui.Row { return ui.NewRow( ui.NewCol(2, 0, w.name), - ui.NewCol(1, 0, w.cid), + ui.NewCol(2, 0, w.cid), ui.NewCol(2, 0, w.cpu), ui.NewCol(2, 0, w.memory), ui.NewCol(2, 0, w.net),