Compare commits

..

25 Commits
v0.3 ... v0.4

Author SHA1 Message Date
Bradley Cicenas
ecc37a2f99 ensure Loop() is started before feeding refresh chan 2017-03-09 18:39:11 +11:00
Bradley Cicenas
2f17a9d689 add ctrl+c exit handler 2017-03-09 16:29:34 +11:00
Bradley Cicenas
8a6808c804 trim grid screencap 2017-03-09 12:22:37 +11:00
Bradley Cicenas
3ca94b50cd add LICENSE 2017-03-09 10:41:32 +11:00
Bradley Cicenas
0e3fe88bb4 use consistent case for ctop name 2017-03-09 10:40:35 +11:00
Bradley Cicenas
b9b904626c update readme 2017-03-09 10:37:02 +11:00
bradley
e195828f92 resize grid screencap 2017-03-09 10:28:02 +11:00
Bradley Cicenas
1d176d46c4 remove sleep from mocksource container creation 2017-03-09 10:26:29 +11:00
Bradley Cicenas
7026193f8e add expanded view screen cap, docpage 2017-03-09 10:25:29 +11:00
Bradley Cicenas
d9b4295176 update grid screencap 2017-03-09 10:13:57 +11:00
Bradley Cicenas
92cc7bc849 add aggression multiplier to mock collector 2017-03-09 09:33:44 +11:00
Bradley Cicenas
70790e88ae v0.4 2017-03-08 22:20:30 +11:00
Bradley Cicenas
bcf05b7f42 fix panic on row removal 2017-03-08 22:19:43 +11:00
bradley
9df3ff2aa0 set static logo width 2017-03-08 19:19:38 +11:00
Bradley Cicenas
2b80832a36 add pagination support for compact view 2017-03-08 18:45:31 +11:00
Bradley Cicenas
a6ee6edb1d move MaxRows() method into cgrid 2017-03-08 11:52:31 +11:00
Bradley Cicenas
d7f9f715bb fix cursor highlighting for newly filtered containers 2017-03-08 11:46:39 +11:00
Bradley Cicenas
2d2d58d47f filter Containers in place 2017-03-08 11:26:22 +11:00
Bradley Cicenas
bf4d59c251 clear screen conditionally 2017-03-08 11:10:38 +11:00
Bradley Cicenas
b8eb386360 add global default ColorMap 2017-03-08 10:40:03 +11:00
Bradley Cicenas
02610c59da move logo to docs folder
fix logo path
2017-03-08 08:41:20 +11:00
Bradley Cicenas
71768b498c update release url 2017-03-07 20:22:44 +11:00
Bradley Cicenas
57e49ea2c6 add logo to readme 2017-03-07 20:10:19 +11:00
bradley
5b25f931df center screencap in readme 2017-03-07 14:52:28 +11:00
Bradley Cicenas
4af33fdf12 add screencaps to readme 2017-03-07 14:48:44 +11:00
29 changed files with 254 additions and 137 deletions

22
LICENSE Normal file
View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2017 VektorLab
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,7 +1,15 @@
# ctop
<p align="center"><img width="200px" src="/_docs/img/logo.png" alt="ctop"/></p>
#
Top-like interface for container metrics
ctop provides a concise and condensed overview of real-time metrics for multiple containers:
<p align="center"><img src="_docs/img/grid.gif" alt="ctop"/></p>
as well as an [expanded view][expanded_view] for inspecting a specific container.
ctop currently comes with built-in support for Docker; connectors for other container and cluster systems are planned for future releases.
## Install
Fetch the [latest release](https://github.com/bcicen/ctop/releases) for your platform:
@@ -9,7 +17,7 @@ Fetch the [latest release](https://github.com/bcicen/ctop/releases) for your pla
#### Linux
```bash
wget https://github.com/bcicen/ctop/releases/download/v0.1/ctop-0.1-linux-amd64 -O ctop
wget https://github.com/bcicen/ctop/releases/download/v0.4/ctop-0.4-linux-amd64 -O ctop
sudo mv ctop /usr/local/bin/
sudo chmod +x /usr/local/bin/ctop
```
@@ -17,14 +25,14 @@ sudo chmod +x /usr/local/bin/ctop
#### OS X
```bash
curl -Lo ctop https://github.com/bcicen/ctop/releases/download/v0.1/ctop-0.1-darwin-amd64
curl -Lo ctop https://github.com/bcicen/ctop/releases/download/v0.4/ctop-0.4-darwin-amd64
sudo mv ctop /usr/local/bin/
sudo chmod +x /usr/local/bin/ctop
```
## Usage
cTop requires no arguments and will configure itself using the `DOCKER_HOST` environment variable
ctop requires no arguments and will configure itself using the `DOCKER_HOST` environment variable
```bash
export DOCKER_HOST=tcp://127.0.0.1:4243
ctop
@@ -36,8 +44,10 @@ Key | Action
--- | ---
a | Toggle display of all (running and non-running) containers
f | Filter displayed containers
H | Toggle cTop header
H | Toggle ctop header
h | Open help dialog
s | Select container sort field
r | Reverse container sort order
q | Quit cTop
q | Quit ctop
[expanded_view]: _docs/expanded.md

View File

@@ -1 +1 @@
0.3
0.4

4
_docs/expanded.md Normal file
View File

@@ -0,0 +1,4 @@
# Expanded View
ctop provides an expanded, rolling view for following container metrics
<p align="center"><img width="80%" src="img/expanded.gif" alt="ctop"/></p>

BIN
_docs/img/expanded.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 KiB

BIN
_docs/img/grid.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 KiB

BIN
_docs/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

43
colors.go Normal file
View File

@@ -0,0 +1,43 @@
package main
import ui "github.com/gizak/termui"
/*
Valid colors:
ui.ColorDefault
ui.ColorBlack
ui.ColorRed
ui.ColorGreen
ui.ColorYellow
ui.ColorBlue
ui.ColorMagenta
ui.ColorCyan
ui.ColorWhite
*/
var ColorMap = map[string]ui.Attribute{
"fg": ui.ColorWhite,
"bg": ui.ColorDefault,
"block.bg": ui.ColorDefault,
"border.bg": ui.ColorDefault,
"border.fg": ui.ColorWhite,
"label.bg": ui.ColorDefault,
"label.fg": ui.ColorGreen,
"menu.text.fg": ui.ColorWhite,
"menu.text.bg": ui.ColorDefault,
"menu.border.fg": ui.ColorCyan,
"menu.label.fg": ui.ColorGreen,
"header.fg": ui.ColorBlack,
"header.bg": ui.ColorWhite,
"gauge.bar.bg": ui.ColorGreen,
"gauge.percent.fg": ui.ColorWhite,
"linechart.axes.fg": ui.ColorDefault,
"linechart.line.fg": ui.ColorGreen,
"mbarchart.bar.bg": ui.ColorGreen,
"mbarchart.num.fg": ui.ColorWhite,
"mbarchart.text.fg": ui.ColorWhite,
"par.text.fg": ui.ColorWhite,
"par.text.bg": ui.ColorDefault,
"sparkline.line.fg": ui.ColorGreen,
"sparkline.title.fg": ui.ColorWhite,
}

View File

@@ -15,7 +15,7 @@ var switches = []*Switch{
&Switch{
Key: "enableHeader",
Val: true,
Label: "Enable cTop Status Line",
Label: "Enable Status Header",
},
&Switch{
Key: "loggingEnabled",

View File

@@ -14,6 +14,7 @@ type Container struct {
Widgets *compact.Compact
updater cwidgets.WidgetUpdater
collector metrics.Collector
display bool // display this container in compact view
}
func NewContainer(id string, collector metrics.Collector) *Container {

View File

@@ -6,7 +6,7 @@ import (
type GridCursor struct {
selectedID string // id of currently selected container
containers Containers
filtered Containers
cSource ContainerSource
}
@@ -16,64 +16,110 @@ func NewGridCursor() *GridCursor {
}
}
func (gc *GridCursor) Len() int { return len(gc.containers) }
func (gc *GridCursor) Selected() *Container { return gc.containers[gc.Idx()] }
func (gc *GridCursor) Len() int { return len(gc.filtered) }
func (gc *GridCursor) Selected() *Container { return gc.filtered[gc.Idx()] }
func (gc *GridCursor) RefreshContainers() {
gc.containers = gc.cSource.All().Filter()
// Refresh containers from source
func (gc *GridCursor) RefreshContainers() (lenChanged bool) {
oldLen := gc.Len()
// Containers filtered by display bool
gc.filtered = Containers{}
var cursorVisible bool
for _, c := range gc.cSource.All() {
if c.display {
if c.Id == gc.selectedID {
cursorVisible = true
}
gc.filtered = append(gc.filtered, c)
}
}
if oldLen != gc.Len() {
lenChanged = true
}
if !cursorVisible {
gc.Reset()
}
if gc.selectedID == "" {
gc.Reset()
}
return lenChanged
}
// Set an initial cursor position, if possible
func (gc *GridCursor) Reset() {
for _, c := range gc.cSource.All() {
c.Widgets.Name.UnHighlight()
}
if gc.Len() > 0 {
gc.selectedID = gc.containers[0].Id
gc.containers[0].Widgets.Name.Highlight()
gc.selectedID = gc.filtered[0].Id
gc.filtered[0].Widgets.Name.Highlight()
}
}
// Return current cursor index
func (gc *GridCursor) Idx() int {
for n, c := range gc.containers {
for n, c := range gc.filtered {
if c.Id == gc.selectedID {
return n
}
}
gc.Reset()
return 0
}
func (gc *GridCursor) ScrollPage() {
// skip scroll if no need to page
if gc.Len() < cGrid.MaxRows() {
cGrid.Offset = 0
return
}
idx := gc.Idx()
// page down
if idx >= cGrid.Offset+cGrid.MaxRows() {
cGrid.Offset++
cGrid.Align()
}
// page up
if idx < cGrid.Offset {
cGrid.Offset--
cGrid.Align()
}
}
func (gc *GridCursor) Up() {
idx := gc.Idx()
// decrement if possible
if idx <= 0 {
if idx <= 0 { // already at top
return
}
active := gc.containers[idx]
next := gc.containers[idx-1]
active := gc.filtered[idx]
next := gc.filtered[idx-1]
active.Widgets.Name.UnHighlight()
gc.selectedID = next.Id
next.Widgets.Name.Highlight()
gc.ScrollPage()
ui.Render(cGrid)
}
func (gc *GridCursor) Down() {
idx := gc.Idx()
// increment if possible
if idx >= (gc.Len() - 1) {
if idx >= gc.Len()-1 { // already at bottom
return
}
if idx >= maxRows()-1 {
return
}
active := gc.containers[idx]
next := gc.containers[idx+1]
active := gc.filtered[idx]
next := gc.filtered[idx+1]
active.Widgets.Name.UnHighlight()
gc.selectedID = next.Id
next.Widgets.Name.Highlight()
gc.ScrollPage()
ui.Render(cGrid)
}

View File

@@ -14,7 +14,6 @@ func NewGaugeCol() *GaugeCol {
g.Border = false
g.Percent = 0
g.PaddingBottom = 0
g.BarColor = ui.ColorGreen
g.Label = "-"
return &GaugeCol{g}
}

View File

@@ -12,7 +12,7 @@ type CompactGrid struct {
X, Y int
Width int
Height int
cursorID string
Offset int // starting row offset
}
func NewCompactGrid() *CompactGrid {
@@ -20,28 +20,34 @@ func NewCompactGrid() *CompactGrid {
}
func (cg *CompactGrid) Align() {
// update row y pos recursively
y := cg.Y
for _, r := range cg.Rows {
if cg.Offset >= len(cg.Rows) {
cg.Offset = 0
}
// update row ypos, width recursively
for _, r := range cg.pageRows() {
r.SetY(y)
y += r.GetHeight()
}
// update row width recursively
for _, r := range cg.Rows {
r.SetWidth(cg.Width)
}
}
func (cg *CompactGrid) Clear() { cg.Rows = []ui.GridBufferer{header} }
func (cg *CompactGrid) GetHeight() int { return len(cg.Rows) }
func (cg *CompactGrid) Clear() { cg.Rows = []ui.GridBufferer{} }
func (cg *CompactGrid) GetHeight() int { return len(cg.Rows) + header.Height }
func (cg *CompactGrid) SetX(x int) { cg.X = x }
func (cg *CompactGrid) SetY(y int) { cg.Y = y }
func (cg *CompactGrid) SetWidth(w int) { cg.Width = w }
func (cg *CompactGrid) MaxRows() int { return ui.TermHeight() - header.Height - cg.Y }
func (cg *CompactGrid) pageRows() (rows []ui.GridBufferer) {
rows = append(rows, header)
rows = append(rows, cg.Rows[cg.Offset:]...)
return rows
}
func (cg *CompactGrid) Buffer() ui.Buffer {
buf := ui.NewBuffer()
for _, r := range cg.Rows {
for _, r := range cg.pageRows() {
buf.Merge(r.Buffer())
}
return buf

View File

@@ -18,7 +18,7 @@ func (row *Compact) SetCPU(val int) {
row.Cpu.Label = fmt.Sprintf("%s%%", strconv.Itoa(val))
if val < 5 {
val = 5
row.Cpu.BarColor = ui.ColorBlack
row.Cpu.BarColor = ui.ThemeAttr("gauge.bar.bg")
}
row.Cpu.Percent = val
}
@@ -29,7 +29,7 @@ func (row *Compact) SetMem(val int64, limit int64, percent int) {
percent = 5
row.Memory.BarColor = ui.ColorBlack
} else {
row.Memory.BarColor = ui.ColorGreen
row.Memory.BarColor = ui.ThemeAttr("gauge.bar.bg")
}
row.Memory.Percent = percent
}

View File

@@ -17,7 +17,7 @@ func NewTextCol(s string) *TextCol {
}
func (w *TextCol) Highlight() {
w.TextFgColor = ui.ThemeAttr("par.text.bg")
w.TextFgColor = ui.ColorBlack
w.TextBgColor = ui.ThemeAttr("par.text.fg")
}

View File

@@ -17,8 +17,6 @@ func NewCpu() *Cpu {
cpu.Width = colWidth[0]
cpu.X = 0
cpu.DataLabels = cpu.hist.Labels
cpu.AxesColor = ui.ColorDefault
cpu.LineColor = ui.ColorGreen
// hack to force the default minY scale to 0
tmpData := []float64{20}

View File

@@ -15,7 +15,7 @@ func NewInfo(id string) *Info {
p := ui.NewTable()
p.Height = 4
p.Width = colWidth[0]
p.FgColor = ui.ColorWhite
p.FgColor = ui.ThemeAttr("par.text.fg")
p.Seperator = false
i := &Info{p, make(map[string]string)}
i.Set("id", id)

View File

@@ -57,7 +57,6 @@ func newMemLabel() *ui.Par {
p.Border = false
p.Height = 1
p.Width = 20
p.TextFgColor = ui.ColorDefault
return p
}
@@ -67,9 +66,6 @@ func newMemChart() *ui.MBarChart {
mbar.Border = false
mbar.BarGap = 1
mbar.BarWidth = 6
mbar.TextColor = ui.ColorDefault
mbar.BarColor[0] = ui.ColorGreen
mbar.BarColor[1] = ui.ColorBlack
mbar.NumColor[1] = ui.ColorBlack

View File

@@ -26,14 +26,12 @@ func NewNet() *Net {
rx.Title = "RX"
rx.Height = 1
rx.Data = net.rxHist.Data
rx.TitleColor = ui.ColorDefault
rx.LineColor = ui.ColorGreen
tx := ui.NewSparkline()
tx.Title = "TX"
tx.Height = 1
tx.Data = net.txHist.Data
tx.TitleColor = ui.ColorDefault
tx.LineColor = ui.ColorYellow
net.Lines = []ui.Sparkline{rx, tx}

View File

@@ -31,8 +31,8 @@ func NewDockerContainerSource() *DockerContainerSource {
containers: make(map[string]*Container),
needsRefresh: make(chan string, 60),
}
cm.refreshAll()
go cm.Loop()
cm.refreshAll()
go cm.watchEvents()
return cm
}
@@ -136,6 +136,7 @@ func (cm *DockerContainerSource) All() (containers Containers) {
containers = append(containers, c)
}
sort.Sort(containers)
containers.Filter()
return containers
}

59
grid.go
View File

@@ -6,11 +6,7 @@ import (
ui "github.com/gizak/termui"
)
func maxRows() int {
return ui.TermHeight() - 2 - cGrid.Y
}
func RedrawRows() {
func RedrawRows(clr bool) {
// reinit body rows
cGrid.Clear()
@@ -23,23 +19,14 @@ func RedrawRows() {
}
cGrid.SetY(y)
var cursorVisible bool
max := maxRows()
for n, c := range cursor.containers {
if n >= max {
break
}
for _, c := range cursor.filtered {
cGrid.AddRows(c.Widgets)
if c.Id == cursor.selectedID {
cursorVisible = true
}
}
if !cursorVisible {
cursor.Reset()
}
if clr {
ui.Clear()
log.Debugf("screen cleared")
}
if config.GetSwitchVal("enableHeader") {
header.Render()
}
@@ -63,7 +50,7 @@ func ExpandView(c *Container) {
ui.Handle("/sys/wnd/resize", func(e ui.Event) {
ex.SetWidth(ui.TermWidth())
ex.Align()
log.Infof("resize: width=%v max-rows=%v", ex.Width, maxRows())
log.Infof("resize: width=%v max-rows=%v", ex.Width, cGrid.MaxRows())
})
ui.Handle("/sys/kbd/", func(ui.Event) {
ui.StopLoop()
@@ -73,6 +60,11 @@ func ExpandView(c *Container) {
c.SetUpdater(c.Widgets)
}
func RefreshDisplay() {
needsClear := cursor.RefreshContainers()
RedrawRows(needsClear)
}
func Display() bool {
var menu func()
var expand bool
@@ -83,23 +75,21 @@ func Display() bool {
// initial draw
header.Align()
cursor.RefreshContainers()
RedrawRows()
RedrawRows(true)
ui.Handle("/sys/kbd/<up>", func(ui.Event) {
cursor.Up()
})
ui.Handle("/sys/kbd/<down>", func(ui.Event) {
cursor.Down()
})
ui.Handle("/sys/kbd/<up>", func(ui.Event) { cursor.Up() })
ui.Handle("/sys/kbd/<down>", func(ui.Event) { cursor.Down() })
ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
expand = true
ui.StopLoop()
})
ui.Handle("/sys/kbd/q", func(ui.Event) { ui.StopLoop() })
ui.Handle("/sys/kbd/C-c", func(ui.Event) { ui.StopLoop() })
ui.Handle("/sys/kbd/a", func(ui.Event) {
config.Toggle("allContainers")
cursor.RefreshContainers()
RedrawRows()
RefreshDisplay()
})
ui.Handle("/sys/kbd/D", func(ui.Event) {
dumpContainer(cursor.Selected())
@@ -114,10 +104,7 @@ func Display() bool {
})
ui.Handle("/sys/kbd/H", func(ui.Event) {
config.Toggle("enableHeader")
RedrawRows()
})
ui.Handle("/sys/kbd/q", func(ui.Event) {
ui.StopLoop()
RedrawRows(true)
})
ui.Handle("/sys/kbd/r", func(e ui.Event) {
config.Toggle("sortReversed")
@@ -128,15 +115,15 @@ func Display() bool {
})
ui.Handle("/timer/1s", func(e ui.Event) {
cursor.RefreshContainers()
RedrawRows()
RefreshDisplay()
})
ui.Handle("/sys/wnd/resize", func(e ui.Event) {
header.Align()
cursor.ScrollPage()
cGrid.SetWidth(ui.TermWidth())
log.Infof("resize: width=%v max-rows=%v", cGrid.Width, maxRows())
RedrawRows()
log.Infof("resize: width=%v max-rows=%v", cGrid.Width, cGrid.MaxRows())
RedrawRows(true)
})
ui.Loop()

View File

@@ -26,6 +26,7 @@ func main() {
defer panicExit()
// init ui
ui.ColorMap = ColorMap // override default colormap
if err := ui.Init(); err != nil {
panic(err)
}
@@ -82,7 +83,7 @@ func panicExit() {
}
}
var helpMsg = `cTop - container metric viewer
var helpMsg = `ctop - container metric viewer
usage: ctop [options]
@@ -96,5 +97,5 @@ func printHelp() {
}
func printVersion() {
fmt.Printf("cTop version %v, build %v\n", version, build)
fmt.Printf("ctop version %v, build %v\n", version, build)
}

View File

@@ -11,7 +11,7 @@ var helpDialog = []menu.Item{
menu.Item{"[a] - toggle display of all containers", ""},
menu.Item{"[f] - filter displayed containers", ""},
menu.Item{"[h] - open this help dialog", ""},
menu.Item{"[H] - toggle cTop header", ""},
menu.Item{"[H] - toggle ctop header", ""},
menu.Item{"[s] - select container sort field", ""},
menu.Item{"[r] - reverse container sort order", ""},
menu.Item{"[q] - exit ctop", ""},
@@ -23,9 +23,7 @@ func HelpMenu() {
defer ui.DefaultEvtStream.ResetHandlers()
m := menu.NewMenu()
m.TextFgColor = ui.ColorWhite
m.BorderLabel = "Help"
m.BorderFg = ui.ColorCyan
m.AddItems(helpDialog...)
ui.Render(m)
ui.Handle("/sys/kbd/", func(ui.Event) {
@@ -39,9 +37,7 @@ func FilterMenu() {
defer ui.DefaultEvtStream.ResetHandlers()
i := widgets.NewInput()
i.TextFgColor = ui.ColorWhite
i.BorderLabel = "Filter"
i.BorderFg = ui.ColorCyan
i.SetY(ui.TermHeight() - i.Height)
ui.Render(i)
@@ -50,8 +46,7 @@ func FilterMenu() {
go func() {
for s := range stream {
config.Update("filterStr", s)
cursor.RefreshContainers()
RedrawRows()
RefreshDisplay()
ui.Render(i)
}
}()
@@ -72,9 +67,7 @@ func SortMenu() {
m := menu.NewMenu()
m.Selectable = true
m.SortItems = true
m.TextFgColor = ui.ColorWhite
m.BorderLabel = "Sort Field"
m.BorderFg = ui.ColorCyan
for _, field := range SortFields() {
m.AddItems(menu.Item{field, ""})

View File

@@ -13,11 +13,13 @@ type Mock struct {
stream chan Metrics
done bool
running bool
aggression int64
}
func NewMock() *Mock {
func NewMock(a int64) *Mock {
c := &Mock{
Metrics: Metrics{},
aggression: a,
}
c.MemLimit = 2147483648
return c
@@ -47,13 +49,14 @@ func (c *Mock) run() {
defer close(c.stream)
for {
c.CPUUtil += rand.Intn(2)
if c.CPUUtil > 100 {
c.CPUUtil += rand.Intn(2) * int(c.aggression)
if c.CPUUtil >= 100 {
c.CPUUtil = 0
}
c.NetTx += rand.Int63n(600)
c.NetRx += rand.Int63n(600)
c.MemUsage += rand.Int63n(c.MemLimit / 32)
c.NetTx += rand.Int63n(60) * c.aggression
c.NetRx += rand.Int63n(60) * c.aggression
c.MemUsage += rand.Int63n(c.MemLimit/512) * c.aggression
if c.MemUsage > c.MemLimit {
c.MemUsage = 0
}

View File

@@ -26,20 +26,26 @@ func NewMockContainerSource() *MockContainerSource {
// Create Mock containers
func (cs *MockContainerSource) Init() {
total := 20
rand.Seed(int64(time.Now().Nanosecond()))
for i := 0; i < total; i++ {
//time.Sleep(1 * time.Second)
collector := metrics.NewMock()
for i := 0; i < 4; i++ {
cs.makeContainer(3)
}
for i := 0; i < 16; i++ {
cs.makeContainer(1)
}
}
func (cs *MockContainerSource) makeContainer(aggression int64) {
collector := metrics.NewMock(aggression)
c := NewContainer(makeID(), collector)
c.SetMeta("name", makeName())
c.SetState(makeState())
cs.containers = append(cs.containers, c)
}
}
func (cs *MockContainerSource) Loop() {
iter := 0
for {
@@ -66,6 +72,7 @@ func (cs *MockContainerSource) Get(id string) (*Container, bool) {
// Return array of all containers, sorted by field
func (cs *MockContainerSource) All() Containers {
sort.Sort(cs.containers)
cs.containers.Filter()
return cs.containers
}

10
sort.go
View File

@@ -83,23 +83,21 @@ func (a Containers) Less(i, j int) bool {
return f(a[i], a[j])
}
func (a Containers) Filter() (filtered []*Container) {
func (a Containers) Filter() {
filter := config.GetVal("filterStr")
re := regexp.MustCompile(fmt.Sprintf(".*%s", filter))
for _, c := range a {
c.display = true
// Apply name filter
if re.FindAllString(c.GetMeta("name"), 1) == nil {
continue
c.display = false
}
// Apply state filter
if !config.GetSwitchVal("allContainers") && c.GetMeta("state") != "running" {
continue
c.display = false
}
filtered = append(filtered, c)
}
return filtered
}
func sumNet(c *Container) int64 { return c.NetRx + c.NetTx }

View File

@@ -41,7 +41,7 @@ func headerBgBordered() *ui.Par {
bg := ui.NewPar("")
bg.X = 1
bg.Height = 3
bg.Bg = ui.ColorWhite
bg.Bg = ui.ThemeAttr("header.bg")
return bg
}
@@ -50,7 +50,7 @@ func headerBg() *ui.Par {
bg.X = 1
bg.Height = 1
bg.Border = false
bg.Bg = ui.ColorWhite
bg.Bg = ui.ThemeAttr("header.bg")
return bg
}
@@ -68,7 +68,7 @@ func (c *CTopHeader) SetFilter(val string) {
func timeStr() string {
ts := time.Now().Local().Format("15:04:05 MST")
return fmt.Sprintf("cTop - %s", ts)
return fmt.Sprintf("ctop - %s", ts)
}
func headerPar(x int, s string) *ui.Par {
@@ -77,8 +77,8 @@ func headerPar(x int, s string) *ui.Par {
p.Border = false
p.Height = 1
p.Width = 20
p.TextFgColor = ui.ColorDefault
p.TextBgColor = ui.ColorWhite
p.Bg = ui.ColorWhite
p.Bg = ui.ThemeAttr("header.bg")
p.TextFgColor = ui.ThemeAttr("header.fg")
p.TextBgColor = ui.ThemeAttr("header.bg")
return p
}

View File

@@ -28,10 +28,12 @@ func NewInput() *Input {
Block: *ui.NewBlock(),
Label: "input",
MaxLen: 20,
TextFgColor: ui.ThemeAttr("par.text.fg"),
TextBgColor: ui.ThemeAttr("par.text.bg"),
TextFgColor: ui.ThemeAttr("menu.text.fg"),
TextBgColor: ui.ThemeAttr("menu.text.bg"),
padding: Padding{4, 2},
}
i.BorderFg = ui.ThemeAttr("menu.border.fg")
i.BorderLabelFg = ui.ThemeAttr("menu.label.fg")
i.calcSize()
return i
}

View File

@@ -22,11 +22,13 @@ type Menu struct {
func NewMenu() *Menu {
m := &Menu{
Block: *ui.NewBlock(),
TextFgColor: ui.ThemeAttr("par.text.fg"),
TextBgColor: ui.ThemeAttr("par.text.bg"),
TextFgColor: ui.ThemeAttr("menu.text.fg"),
TextBgColor: ui.ThemeAttr("menu.text.bg"),
cursorPos: 0,
padding: Padding{4, 2},
}
m.BorderFg = ui.ThemeAttr("menu.border.fg")
m.BorderLabelFg = ui.ThemeAttr("menu.label.fg")
m.X = 1
return m
}
@@ -86,7 +88,7 @@ func (m *Menu) Buffer() ui.Buffer {
for _, ch := range item.Text() {
// invert bg/fg colors on currently selected row
if m.Selectable && n == m.cursorPos {
cell = ui.Cell{Ch: ch, Fg: m.TextBgColor, Bg: m.TextFgColor}
cell = ui.Cell{Ch: ch, Fg: ui.ColorBlack, Bg: m.TextFgColor}
} else {
cell = ui.Cell{Ch: ch, Fg: m.TextFgColor, Bg: m.TextBgColor}
}