diff --git a/config/columns.go b/config/columns.go new file mode 100644 index 0000000..90d7b38 --- /dev/null +++ b/config/columns.go @@ -0,0 +1,138 @@ +package config + +import ( + "strings" +) + +// defaults +var defaultColumns = []Column{ + Column{ + Name: "status", + Label: "Status Indicator", + Enabled: true, + }, + Column{ + Name: "name", + Label: "Container Name", + Enabled: true, + }, + Column{ + Name: "id", + Label: "Container ID", + Enabled: true, + }, + Column{ + Name: "cpu", + Label: "CPU Usage", + Enabled: true, + }, + Column{ + Name: "mem", + Label: "Memory Usage", + Enabled: true, + }, + Column{ + Name: "net", + Label: "Network RX/TX", + Enabled: true, + }, + Column{ + Name: "io", + Label: "Disk IO Read/Write", + Enabled: true, + }, + Column{ + Name: "pids", + Label: "Container PID Count", + Enabled: true, + }, +} + +type Column struct { + Name string + Label string + Enabled bool +} + +// ColumnsString returns an ordered and comma-delimited string of currently enabled Columns +func ColumnsString() string { return strings.Join(EnabledColumns(), ",") } + +// EnabledColumns returns an ordered array of enabled column names +func EnabledColumns() (a []string) { + lock.RLock() + defer lock.RUnlock() + for _, col := range GlobalColumns { + if col.Enabled { + a = append(a, col.Name) + } + } + return a +} + +// ColumnLeft moves the column with given name up one position, if possible +func ColumnLeft(name string) { + idx := colIndex(name) + if idx > 0 { + swapCols(idx, idx-1) + } +} + +// ColumnRight moves the column with given name up one position, if possible +func ColumnRight(name string) { + idx := colIndex(name) + if idx < len(GlobalColumns)-1 { + swapCols(idx, idx+1) + } +} + +// Set Column order and enabled status from one or more provided Column names +func SetColumns(names []string) { + var ( + n int + curColStr = ColumnsString() + newColumns = make([]*Column, len(GlobalColumns)) + ) + + lock.Lock() + + // add enabled columns by name + for _, name := range names { + newColumns[n] = popColumn(name) + newColumns[n].Enabled = true + n++ + } + + // extend with omitted columns as disabled + for _, col := range GlobalColumns { + newColumns[n] = col + newColumns[n].Enabled = false + n++ + } + + GlobalColumns = newColumns + lock.Unlock() + + log.Noticef("config change [columns]: %s -> %s", curColStr, ColumnsString()) +} + +func swapCols(i, j int) { GlobalColumns[i], GlobalColumns[j] = GlobalColumns[j], GlobalColumns[i] } + +func popColumn(name string) *Column { + idx := colIndex(name) + if idx < 0 { + panic("no such column name: " + name) + } + col := GlobalColumns[idx] + GlobalColumns = append(GlobalColumns[:idx], GlobalColumns[idx+1:]...) + return col +} + +// return index of column with given name, if any +func colIndex(name string) int { + for n, c := range GlobalColumns { + if c.Name == name { + return n + } + } + return -1 +} diff --git a/config/file.go b/config/file.go index 4019807..fc08a06 100644 --- a/config/file.go +++ b/config/file.go @@ -16,17 +16,18 @@ var ( type File struct { Options map[string]string `toml:"options"` Toggles map[string]bool `toml:"toggles"` - Widgets []Widget `toml:"widget"` } func exportConfig() File { + // update columns param from working config + Update("columns", ColumnsString()) + lock.RLock() defer lock.RUnlock() c := File{ Options: make(map[string]string), Toggles: make(map[string]bool), - Widgets: make([]Widget, len(GlobalWidgets)), } for _, p := range GlobalParams { @@ -35,9 +36,6 @@ func exportConfig() File { for _, sw := range GlobalSwitches { c.Toggles[sw.Key] = sw.Val } - for n, w := range GlobalWidgets { - c.Widgets[n] = *w - } return c } @@ -54,15 +52,21 @@ func Read() error { if _, err := toml.DecodeFile(path, &config); err != nil { return err } - for k, v := range config.Options { Update(k, v) } for k, v := range config.Toggles { UpdateSwitch(k, v) } - for _, w := range config.Widgets { - UpdateWidget(strings.ToLower(w.Name), w.Enabled) + + // set working column config, if provided + colStr := GetVal("columns") + if len(colStr) > 0 { + var colNames []string + for _, s := range strings.Split(colStr, ",") { + colNames = append(colNames, strings.TrimSpace(s)) + } + SetColumns(colNames) } return nil diff --git a/config/main.go b/config/main.go index cb873cc..d856ab5 100644 --- a/config/main.go +++ b/config/main.go @@ -11,7 +11,7 @@ import ( var ( GlobalParams []*Param GlobalSwitches []*Switch - GlobalWidgets []*Widget + GlobalColumns []*Column lock sync.RWMutex log = logging.Init() ) @@ -19,15 +19,16 @@ var ( func Init() { for _, p := range defaultParams { GlobalParams = append(GlobalParams, p) - log.Infof("loaded default config param: %s: %s", quote(p.Key), quote(p.Val)) + log.Infof("loaded default config param [%s]: %s", quote(p.Key), quote(p.Val)) } for _, s := range defaultSwitches { GlobalSwitches = append(GlobalSwitches, s) - log.Infof("loaded default config switch: %s: %t", quote(s.Key), s.Val) + log.Infof("loaded default config switch [%s]: %t", quote(s.Key), s.Val) } - for _, w := range defaultWidgets { - GlobalWidgets = append(GlobalWidgets, w) - log.Infof("loaded default widget: %s: %t", quote(w.Name), w.Enabled) + for _, c := range defaultColumns { + x := c + GlobalColumns = append(GlobalColumns, &x) + log.Infof("loaded default widget config [%s]: %t", quote(x.Name), x.Enabled) } } diff --git a/config/param.go b/config/param.go index 6233d5d..dc334f5 100644 --- a/config/param.go +++ b/config/param.go @@ -17,6 +17,11 @@ var defaultParams = []*Param{ Val: "sh", Label: "Shell", }, + &Param{ + Key: "columns", + Val: "status,name,id,cpu,mem,net,io,pids", + Label: "Enabled Columns", + }, } type Param struct { diff --git a/config/widget.go b/config/widget.go deleted file mode 100644 index 3783950..0000000 --- a/config/widget.go +++ /dev/null @@ -1,122 +0,0 @@ -package config - -import ( - "strings" -) - -// defaults -var defaultWidgets = []*Widget{ - &Widget{ - Name: "status", - Enabled: true, - }, - &Widget{ - Name: "name", - Enabled: true, - }, - &Widget{ - Name: "id", - Enabled: true, - }, - &Widget{ - Name: "cpu", - Enabled: true, - }, - &Widget{ - Name: "mem", - Enabled: true, - }, - &Widget{ - Name: "net", - Enabled: true, - }, - &Widget{ - Name: "io", - Enabled: true, - }, - &Widget{ - Name: "pids", - Enabled: true, - }, -} - -type Widget struct { - Name string - Enabled bool -} - -// GetWidget returns a Widget by name -func GetWidget(name string) *Widget { - lock.RLock() - defer lock.RUnlock() - - for _, w := range GlobalWidgets { - if w.Name == name { - return w - } - } - log.Errorf("widget name not found: %s", name) - return &Widget{} // default -} - -// Widgets returns a copy of all configurable Widgets, in order -func Widgets() []Widget { - a := make([]Widget, len(GlobalWidgets)) - - lock.RLock() - defer lock.RUnlock() - - for n, w := range GlobalWidgets { - a[n] = *w - } - return a -} - -// EnabledWidgets returns an ordered array of enabled widget names -func EnabledWidgets() (a []string) { - for _, w := range Widgets() { - if w.Enabled { - a = append(a, w.Name) - } - } - return a -} - -func UpdateWidget(name string, enabled bool) { - w := GetWidget(name) - oldVal := w.Enabled - log.Noticef("config change [%s-enabled]: %t -> %t", name, oldVal, enabled) - - lock.Lock() - defer lock.Unlock() - w.Enabled = enabled -} - -func ToggleWidgetEnabled(name string) { - w := GetWidget(name) - newVal := !w.Enabled - log.Noticef("config change [%s-enabled]: %t -> %t", name, w.Enabled, newVal) - - lock.Lock() - defer lock.Unlock() - w.Enabled = newVal -} - -// UpdateWidgets replaces existing ordered widgets with those provided -func UpdateWidgets(newWidgets []Widget) { - oldOrder := widgetNames() - lock.Lock() - for n, w := range newWidgets { - GlobalWidgets[n] = &w - } - lock.Unlock() - log.Noticef("config change [widget-order]: %s -> %s", oldOrder, widgetNames()) -} - -func widgetNames() string { - a := make([]string, len(GlobalWidgets)) - for n, w := range Widgets() { - a[n] = w.Name - } - return strings.Join(a, ", ") -}