mirror of
https://github.com/bcicen/ctop.git
synced 2024-08-30 18:23:19 +00:00
add ContainerSource interface, fix secondary sort method
This commit is contained in:
parent
4709624b17
commit
05b50af87b
@ -36,7 +36,7 @@ func (c *Container) Expand() {
|
|||||||
var curWidgets widgets.ContainerWidgets
|
var curWidgets widgets.ContainerWidgets
|
||||||
|
|
||||||
curWidgets = c.widgets
|
curWidgets = c.widgets
|
||||||
c.widgets = widgets.NewExpanded(c.ShortID(), c.ShortName())
|
c.widgets = widgets.NewExpanded(c.ShortID(), c.name)
|
||||||
c.widgets.Render(0, 0)
|
c.widgets.Render(0, 0)
|
||||||
c.widgets = curWidgets
|
c.widgets = curWidgets
|
||||||
}
|
}
|
||||||
|
@ -12,32 +12,37 @@ import (
|
|||||||
|
|
||||||
var lock = sync.RWMutex{}
|
var lock = sync.RWMutex{}
|
||||||
|
|
||||||
type ContainerMap struct {
|
type ContainerSource interface {
|
||||||
|
All() []*Container
|
||||||
|
Get(string) (*Container, bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DockerContainerSource struct {
|
||||||
client *docker.Client
|
client *docker.Client
|
||||||
containers Containers
|
containers Containers
|
||||||
collectors map[string]metrics.Collector
|
collectors map[string]metrics.Collector
|
||||||
needsRefresh map[string]int // container IDs requiring refresh
|
needsRefresh map[string]int // container IDs requiring refresh
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContainerMap() *ContainerMap {
|
func NewDockerContainerSource() *DockerContainerSource {
|
||||||
// init docker client
|
// init docker client
|
||||||
client, err := docker.NewClient(config.GetVal("dockerHost"))
|
client, err := docker.NewClient(config.GetVal("dockerHost"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
cm := &ContainerMap{
|
cm := &DockerContainerSource{
|
||||||
client: client,
|
client: client,
|
||||||
collectors: make(map[string]metrics.Collector),
|
collectors: make(map[string]metrics.Collector),
|
||||||
needsRefresh: make(map[string]int),
|
needsRefresh: make(map[string]int),
|
||||||
}
|
}
|
||||||
cm.refreshAll()
|
cm.refreshAll()
|
||||||
go cm.UpdateLoop()
|
go cm.Loop()
|
||||||
go cm.watchEvents()
|
go cm.watchEvents()
|
||||||
return cm
|
return cm
|
||||||
}
|
}
|
||||||
|
|
||||||
// Docker events watcher
|
// Docker events watcher
|
||||||
func (cm *ContainerMap) watchEvents() {
|
func (cm *DockerContainerSource) watchEvents() {
|
||||||
log.Info("docker event listener starting")
|
log.Info("docker event listener starting")
|
||||||
events := make(chan *docker.APIEvents)
|
events := make(chan *docker.APIEvents)
|
||||||
cm.client.AddEventListener(events)
|
cm.client.AddEventListener(events)
|
||||||
@ -52,16 +57,16 @@ func (cm *ContainerMap) watchEvents() {
|
|||||||
cm.needsRefresh[e.ID] = 1
|
cm.needsRefresh[e.ID] = 1
|
||||||
case "destroy":
|
case "destroy":
|
||||||
log.Debugf("handling docker event: action=%s id=%s", e.Action, e.ID)
|
log.Debugf("handling docker event: action=%s id=%s", e.Action, e.ID)
|
||||||
cm.DelByID(e.ID)
|
cm.delByID(e.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *ContainerMap) refresh(id string) {
|
func (cm *DockerContainerSource) refresh(id string) {
|
||||||
insp := cm.inspect(id)
|
insp := cm.inspect(id)
|
||||||
// remove container if no longer exists
|
// remove container if no longer exists
|
||||||
if insp == nil {
|
if insp == nil {
|
||||||
cm.DelByID(id)
|
cm.delByID(id)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +96,7 @@ func (cm *ContainerMap) refresh(id string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *ContainerMap) inspect(id string) *docker.Container {
|
func (cm *DockerContainerSource) inspect(id string) *docker.Container {
|
||||||
c, err := cm.client.InspectContainer(id)
|
c, err := cm.client.InspectContainer(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(*docker.NoSuchContainer); ok == false {
|
if _, ok := err.(*docker.NoSuchContainer); ok == false {
|
||||||
@ -101,7 +106,8 @@ func (cm *ContainerMap) inspect(id string) *docker.Container {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *ContainerMap) refreshAll() {
|
// Mark all container IDs for refresh
|
||||||
|
func (cm *DockerContainerSource) refreshAll() {
|
||||||
opts := docker.ListContainersOptions{All: true}
|
opts := docker.ListContainersOptions{All: true}
|
||||||
allContainers, err := cm.client.ListContainers(opts)
|
allContainers, err := cm.client.ListContainers(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -113,7 +119,7 @@ func (cm *ContainerMap) refreshAll() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *ContainerMap) UpdateLoop() {
|
func (cm *DockerContainerSource) Loop() {
|
||||||
for {
|
for {
|
||||||
switch {
|
switch {
|
||||||
case len(cm.needsRefresh) > 0:
|
case len(cm.needsRefresh) > 0:
|
||||||
@ -126,13 +132,13 @@ func (cm *ContainerMap) UpdateLoop() {
|
|||||||
delete(cm.needsRefresh, id)
|
delete(cm.needsRefresh, id)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(3 * time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a single container, by ID
|
// Get a single container, by ID
|
||||||
func (cm *ContainerMap) Get(id string) (*Container, bool) {
|
func (cm *DockerContainerSource) Get(id string) (*Container, bool) {
|
||||||
for _, c := range cm.containers {
|
for _, c := range cm.containers {
|
||||||
if c.id == id {
|
if c.id == id {
|
||||||
return c, true
|
return c, true
|
||||||
@ -142,17 +148,17 @@ func (cm *ContainerMap) Get(id string) (*Container, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove containers by ID
|
// Remove containers by ID
|
||||||
func (cm *ContainerMap) DelByID(id string) {
|
func (cm *DockerContainerSource) delByID(id string) {
|
||||||
for n, c := range cm.containers {
|
for n, c := range cm.containers {
|
||||||
if c.id == id {
|
if c.id == id {
|
||||||
cm.Del(n)
|
cm.del(n)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove one or more containers by index
|
// Remove one or more containers by index
|
||||||
func (cm *ContainerMap) Del(idx ...int) {
|
func (cm *DockerContainerSource) del(idx ...int) {
|
||||||
lock.Lock()
|
lock.Lock()
|
||||||
defer lock.Unlock()
|
defer lock.Unlock()
|
||||||
for _, i := range idx {
|
for _, i := range idx {
|
||||||
@ -162,7 +168,7 @@ func (cm *ContainerMap) Del(idx ...int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return array of all containers, sorted by field
|
// Return array of all containers, sorted by field
|
||||||
func (cm *ContainerMap) All() []*Container {
|
func (cm *DockerContainerSource) All() []*Container {
|
||||||
sort.Sort(cm.containers)
|
sort.Sort(cm.containers)
|
||||||
return cm.containers
|
return cm.containers
|
||||||
}
|
}
|
||||||
|
12
grid.go
12
grid.go
@ -16,16 +16,16 @@ func maxRows() int {
|
|||||||
|
|
||||||
type Grid struct {
|
type Grid struct {
|
||||||
cursorID string // id of currently selected container
|
cursorID string // id of currently selected container
|
||||||
cmap *ContainerMap
|
cSource ContainerSource
|
||||||
containers Containers // sorted slice of containers
|
containers Containers // sorted slice of containers
|
||||||
header *widgets.CTopHeader
|
header *widgets.CTopHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGrid() *Grid {
|
func NewGrid() *Grid {
|
||||||
cmap := NewContainerMap()
|
cs := NewDockerContainerSource()
|
||||||
g := &Grid{
|
g := &Grid{
|
||||||
cmap: cmap,
|
cSource: cs,
|
||||||
containers: cmap.All(),
|
containers: cs.All(),
|
||||||
header: widgets.NewCTopHeader(),
|
header: widgets.NewCTopHeader(),
|
||||||
}
|
}
|
||||||
return g
|
return g
|
||||||
@ -125,7 +125,7 @@ func (g *Grid) ExpandView() {
|
|||||||
ui.Clear()
|
ui.Clear()
|
||||||
ui.DefaultEvtStream.ResetHandlers()
|
ui.DefaultEvtStream.ResetHandlers()
|
||||||
defer ui.DefaultEvtStream.ResetHandlers()
|
defer ui.DefaultEvtStream.ResetHandlers()
|
||||||
container, _ := g.cmap.Get(g.cursorID)
|
container, _ := g.cSource.Get(g.cursorID)
|
||||||
container.Expand()
|
container.Expand()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +187,7 @@ func Display(g *Grid) bool {
|
|||||||
})
|
})
|
||||||
|
|
||||||
ui.Handle("/timer/1s", func(e ui.Event) {
|
ui.Handle("/timer/1s", func(e ui.Event) {
|
||||||
g.containers = g.cmap.All() // refresh containers for current sort order
|
g.containers = g.cSource.All() // refresh containers for current sort order
|
||||||
g.redrawRows()
|
g.redrawRows()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
16
sort.go
16
sort.go
@ -16,32 +16,40 @@ var Sorters = map[string]sortMethod{
|
|||||||
"id": idSorter,
|
"id": idSorter,
|
||||||
"name": nameSorter,
|
"name": nameSorter,
|
||||||
"cpu": func(c1, c2 *Container) bool {
|
"cpu": func(c1, c2 *Container) bool {
|
||||||
|
// Use secondary sort method if equal values
|
||||||
if c1.metrics.CPUUtil == c2.metrics.CPUUtil {
|
if c1.metrics.CPUUtil == c2.metrics.CPUUtil {
|
||||||
return nameSorter(c1, c2)
|
return nameSorter(c1, c2)
|
||||||
}
|
}
|
||||||
return c1.metrics.CPUUtil < c2.metrics.CPUUtil
|
return c1.metrics.CPUUtil > c2.metrics.CPUUtil
|
||||||
},
|
},
|
||||||
"mem": func(c1, c2 *Container) bool {
|
"mem": func(c1, c2 *Container) bool {
|
||||||
|
// Use secondary sort method if equal values
|
||||||
if c1.metrics.MemUsage == c2.metrics.MemUsage {
|
if c1.metrics.MemUsage == c2.metrics.MemUsage {
|
||||||
return nameSorter(c1, c2)
|
return nameSorter(c1, c2)
|
||||||
}
|
}
|
||||||
return c1.metrics.MemUsage < c2.metrics.MemUsage
|
return c1.metrics.MemUsage > c2.metrics.MemUsage
|
||||||
},
|
},
|
||||||
"mem %": func(c1, c2 *Container) bool {
|
"mem %": func(c1, c2 *Container) bool {
|
||||||
|
// Use secondary sort method if equal values
|
||||||
if c1.metrics.MemPercent == c2.metrics.MemPercent {
|
if c1.metrics.MemPercent == c2.metrics.MemPercent {
|
||||||
return nameSorter(c1, c2)
|
return nameSorter(c1, c2)
|
||||||
}
|
}
|
||||||
return c1.metrics.MemPercent < c2.metrics.MemPercent
|
return c1.metrics.MemPercent > c2.metrics.MemPercent
|
||||||
},
|
},
|
||||||
"net": func(c1, c2 *Container) bool {
|
"net": func(c1, c2 *Container) bool {
|
||||||
sum1 := sumNet(c1)
|
sum1 := sumNet(c1)
|
||||||
sum2 := sumNet(c2)
|
sum2 := sumNet(c2)
|
||||||
|
// Use secondary sort method if equal values
|
||||||
if sum1 == sum2 {
|
if sum1 == sum2 {
|
||||||
return nameSorter(c1, c2)
|
return nameSorter(c1, c2)
|
||||||
}
|
}
|
||||||
return sum1 < sum2
|
return sum1 > sum2
|
||||||
},
|
},
|
||||||
"state": func(c1, c2 *Container) bool {
|
"state": func(c1, c2 *Container) bool {
|
||||||
|
// Use secondary sort method if equal values
|
||||||
|
if c1.state == c2.state {
|
||||||
|
return nameSorter(c1, c2)
|
||||||
|
}
|
||||||
if c1.state == "running" {
|
if c1.state == "running" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -287,5 +287,6 @@ func slimGauge() *ui.Gauge {
|
|||||||
g.PaddingBottom = 0
|
g.PaddingBottom = 0
|
||||||
g.BarColor = ui.ColorGreen
|
g.BarColor = ui.ColorGreen
|
||||||
g.Label = "-"
|
g.Label = "-"
|
||||||
|
g.Bg = ui.ColorBlack
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user