From f99918f65c1e57f774c35dfa14b92e39f6f9d04c Mon Sep 17 00:00:00 2001 From: Navid Yaghoobi Date: Sun, 26 Nov 2023 14:00:16 +1100 Subject: [PATCH] running golangci-lint on ui/pods package Signed-off-by: Navid Yaghoobi --- .golangci.yml | 1 - ui/pods/commands.go | 107 ++++++++++++++--- ui/pods/data.go | 12 +- ui/pods/draw.go | 22 +++- ui/pods/key.go | 34 ++++-- ui/pods/poddialogs/create.go | 219 ++++++++++++++++++++++++++--------- ui/pods/poddialogs/stats.go | 101 ++++++++++++---- ui/pods/pods.go | 78 +++++++++++-- ui/pods/refresh.go | 18 +-- 9 files changed, 469 insertions(+), 123 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 7e602850e..7ea704b36 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -7,7 +7,6 @@ run: - ui/dialogs - ui/images - ui/networks - - ui/pods linters: enable-all: true disable: diff --git a/ui/pods/commands.go b/ui/pods/commands.go index 730236503..8e5a46f61 100644 --- a/ui/pods/commands.go +++ b/ui/pods/commands.go @@ -10,7 +10,7 @@ import ( "github.com/rs/zerolog/log" ) -func (p *Pods) runCommand(cmd string) { +func (p *Pods) runCommand(cmd string) { //nolint:cyclop switch cmd { case "create": p.createDialog.Display() @@ -20,7 +20,7 @@ func (p *Pods) runCommand(cmd string) { p.kill() case "pause": p.pause() - case "prune": + case "prune": //nolint:goconst p.confirmDialog.SetTitle("podman pod prune") p.confirmData = "prune" p.confirmDialog.SetText("Are you sure you want to remove all stopped pods ?") @@ -51,43 +51,57 @@ func (p *Pods) displayError(title string, err error) { func (p *Pods) stats() { if p.selectedID == "" { - p.displayError("", fmt.Errorf("there is no pod to perform stats command")) + p.displayError("", errNoPodStat) + return } + podOptions := p.getAllItemsForStats() + p.statsDialog.SetPodsOptions(podOptions) p.statsDialog.Display() } func (p *Pods) create() { podSpec := p.createDialog.GetPodSpec() + p.progressDialog.SetTitle("pod create in progress") p.progressDialog.Display() + createFunc := func() { err := ppods.Create(podSpec) + p.progressDialog.Hide() + if err != nil { p.displayError("POD CREATE ERROR", err) + return } } + go createFunc() } func (p *Pods) inspect() { podID, podName := p.getSelectedItem() if podID == "" { - p.displayError("", fmt.Errorf("there is no pod to display inspect")) + p.displayError("", errNoPodInspect) + return } + data, err := ppods.Inspect(podID) if err != nil { title := fmt.Sprintf("POD (%s) INSPECT ERROR", podID) + p.displayError(title, err) + return } headerLabel := fmt.Sprintf("%12s (%s)", podID, podName) + p.messageDialog.SetTitle("podman pod inspect") p.messageDialog.SetText(dialogs.MessagePodInfo, headerLabel, data) p.messageDialog.Display() @@ -95,93 +109,126 @@ func (p *Pods) inspect() { func (p *Pods) kill() { if p.selectedID == "" { - p.displayError("", fmt.Errorf("there is no pod to kill")) + p.displayError("", errNoPodKill) + return } + p.progressDialog.SetTitle("pod kill in progress") p.progressDialog.Display() + kill := func(id string) { err := ppods.Kill(id) + p.progressDialog.Hide() + if err != nil { title := fmt.Sprintf("POD (%s) KILL ERROR", p.selectedID) + p.displayError(title, err) + return } } + go kill(p.selectedID) } func (p *Pods) pause() { if p.selectedID == "" { - p.displayError("", fmt.Errorf("there is no pod to pause")) + p.displayError("", errNoPodPause) + return } + p.progressDialog.SetTitle("pod pause in progress") p.progressDialog.Display() + pause := func(id string) { err := ppods.Pause(id) + p.progressDialog.Hide() + if err != nil { title := fmt.Sprintf("POD (%s) PAUSE ERROR", p.selectedID) + p.displayError(title, err) + return } } + go pause(p.selectedID) } func (p *Pods) prune() { p.progressDialog.SetTitle("pod purne in progress") p.progressDialog.Display() + unpause := func() { errData, err := ppods.Prune() + p.progressDialog.Hide() + if err != nil { p.displayError("PODS PRUNE ERROR", err) + return } + if len(errData) > 0 { - errMessages := fmt.Errorf("%v", errData) + errMessages := fmt.Errorf("%w %v", errPodPrune, errData) + p.displayError("PODS PRUNE ERROR", errMessages) } - } + go unpause() } func (p *Pods) restart() { if p.selectedID == "" { - p.displayError("", fmt.Errorf("there is no pod to restart")) + p.displayError("", errNoPodRestart) + return } + p.progressDialog.SetTitle("pod restart in progress") p.progressDialog.Display() + restart := func(id string) { err := ppods.Restart(id) + p.progressDialog.Hide() + if err != nil { title := fmt.Sprintf("POD (%s) RESTART ERROR", p.selectedID) p.displayError(title, err) + return } } + go restart(p.selectedID) } func (p *Pods) rm() { podID, podName := p.getSelectedItem() if podID == "" { - p.displayError("", fmt.Errorf("there is no pod to remove")) + p.displayError("", errNoPodRemove) + return } + p.confirmDialog.SetTitle("podman pod rm") + p.confirmData = "rm" bgColor := style.GetColorHex(style.DialogBorderColor) fgColor := style.GetColorHex(style.DialogFgColor) podItem := fmt.Sprintf("[%s:%s:b]POD ID:[:-:-] %s (%s)", fgColor, bgColor, podID, podName) description := fmt.Sprintf("%s\n\nAre you sure you want to remove the selected pod?", podItem) + p.confirmDialog.SetText(description) p.confirmDialog.Display() } @@ -189,69 +236,92 @@ func (p *Pods) rm() { func (p *Pods) remove() { p.progressDialog.SetTitle("pod remove in progress") p.progressDialog.Display() + remove := func(id string) { errData, err := ppods.Remove(id) + p.progressDialog.Hide() + if err != nil { title := fmt.Sprintf("POD (%s) REMOVE ERROR", p.selectedID) p.displayError(title, err) + return } + if len(errData) > 0 { title := fmt.Sprintf("POD (%s) REMOVE ERROR", p.selectedID) - p.displayError(title, fmt.Errorf("%v", errData)) + p.displayError(title, fmt.Errorf("%w %v", errPodRemove, errData)) } } + go remove(p.selectedID) } func (p *Pods) start() { if p.selectedID == "" { - p.displayError("", fmt.Errorf("there is no pod to start")) + p.displayError("", errNoPodStart) + return } + p.progressDialog.SetTitle("pod start in progress") p.progressDialog.Display() + start := func(id string) { err := ppods.Start(id) + p.progressDialog.Hide() + if err != nil { title := fmt.Sprintf("POD (%s) START ERROR", p.selectedID) p.displayError(title, err) + return } } + go start(p.selectedID) } func (p *Pods) stop() { if p.selectedID == "" { - p.displayError("", fmt.Errorf("there is no pod to stop")) + p.displayError("", errNoPodStop) + return } + p.progressDialog.SetTitle("pod stop in progress") p.progressDialog.Display() + stop := func(id string) { err := ppods.Stop(id) + p.progressDialog.Hide() + if err != nil { title := fmt.Sprintf("POD (%s) STOP ERROR", p.selectedID) p.displayError(title, err) + return } } + go stop(p.selectedID) } func (p *Pods) top() { if p.selectedID == "" { - p.displayError("", fmt.Errorf("there is no pod to display top")) + p.displayError("", errNoPodTop) + return } + data, err := ppods.Top(p.selectedID) if err != nil { title := fmt.Sprintf("POD (%s) TOP ERROR", p.selectedID) p.displayError(title, err) + return } @@ -262,19 +332,26 @@ func (p *Pods) top() { func (p *Pods) unpause() { if p.selectedID == "" { - p.displayError("", fmt.Errorf("there is no pod to unpause")) + p.displayError("", errNoPodUnpause) + return } + p.progressDialog.SetTitle("pod unpause in progress") p.progressDialog.Display() + unpause := func(id string) { err := ppods.Unpause(id) + p.progressDialog.Hide() + if err != nil { title := fmt.Sprintf("POD (%s) UNPAUSE ERROR", p.selectedID) p.displayError(title, err) + return } } + go unpause(p.selectedID) } diff --git a/ui/pods/data.go b/ui/pods/data.go index 62cf4537a..d990c3214 100644 --- a/ui/pods/data.go +++ b/ui/pods/data.go @@ -11,15 +11,17 @@ import ( "github.com/rs/zerolog/log" ) -// UpdateData retrieves pods list data +// UpdateData retrieves pods list data. func (pods *Pods) UpdateData() { podList, err := ppods.List() if err != nil { log.Error().Msgf("view: pods update %v", err) pods.errorDialog.SetText(fmt.Sprintf("%v", err)) pods.errorDialog.Display() + return } + pods.podsList.mu.Lock() pods.podsList.report = podList pods.podsList.mu.Unlock() @@ -29,15 +31,17 @@ func (pods *Pods) getData() []*entities.ListPodsReport { pods.podsList.mu.Lock() data := pods.podsList.report pods.podsList.mu.Unlock() + return data } -// ClearData clears table data -func (pods *Pods) ClearData() { +// ClearData clears table data. +func (pods *Pods) ClearData() { //nolint:stylecheck pods.podsList.mu.Lock() pods.podsList.report = nil pods.podsList.mu.Unlock() pods.table.Clear() + expand := 1 fgColor := style.PageHeaderFgColor bgColor := style.PageHeaderBgColor @@ -51,6 +55,6 @@ func (pods *Pods) ClearData() { SetAlign(tview.AlignLeft). SetSelectable(false)) } - pods.table.SetTitle(fmt.Sprintf("[::b]%s[0]", strings.ToUpper(pods.title))) + pods.table.SetTitle(fmt.Sprintf("[::b]%s[0]", strings.ToUpper(pods.title))) } diff --git a/ui/pods/draw.go b/ui/pods/draw.go index 5705314da..94ddb5450 100644 --- a/ui/pods/draw.go +++ b/ui/pods/draw.go @@ -9,57 +9,75 @@ func (pods *Pods) Draw(screen tcell.Screen) { pods.refresh() pods.Box.DrawForSubclass(screen, pods) pods.Box.SetBorder(false) + x, y, width, height := pods.GetInnerRect() + pods.table.SetRect(x, y, width, height) pods.table.SetBorder(true) pods.table.Draw(screen) + x, y, width, height = pods.table.GetInnerRect() + // error dialog if pods.errorDialog.IsDisplay() { pods.errorDialog.SetRect(x, y, width, height) pods.errorDialog.Draw(screen) + return } - // command dialog dialog + + // command dialog if pods.cmdDialog.IsDisplay() { pods.cmdDialog.SetRect(x, y, width, height) pods.cmdDialog.Draw(screen) + return } - // create dialog dialog + + // create dialog if pods.createDialog.IsDisplay() { pods.createDialog.SetRect(x, y, width, height) pods.createDialog.Draw(screen) + return } + // confirm dialog if pods.confirmDialog.IsDisplay() { pods.confirmDialog.SetRect(x, y, width, height) pods.confirmDialog.Draw(screen) + return } + // message dialog if pods.messageDialog.IsDisplay() { pods.messageDialog.SetRect(x, y, width, height+1) pods.messageDialog.Draw(screen) + return } + // progress dialog if pods.progressDialog.IsDisplay() { pods.progressDialog.SetRect(x, y, width, height) pods.progressDialog.Draw(screen) } + // top dialog if pods.topDialog.IsDisplay() { pods.topDialog.SetRect(x, y, width, height) pods.topDialog.Draw(screen) + return } + // stats dialogs if pods.statsDialog.IsDisplay() { pods.statsDialog.SetRect(x, y, width, height) pods.statsDialog.Draw(screen) + return } } diff --git a/ui/pods/key.go b/ui/pods/key.go index 4cb8188b4..982097e5d 100644 --- a/ui/pods/key.go +++ b/ui/pods/key.go @@ -8,36 +8,42 @@ import ( ) // InputHandler returns the handler for this primitive. -func (pods *Pods) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) { +func (pods *Pods) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) { //nolint:gocognit,cyclop,lll return pods.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) { log.Debug().Msgf("view: pods event %v received", event) + if pods.progressDialog.IsDisplay() { return } + // error dialog handler if pods.errorDialog.HasFocus() { if errorDialogHandler := pods.errorDialog.InputHandler(); errorDialogHandler != nil { errorDialogHandler(event, setFocus) } } + // message dialog handler if pods.messageDialog.HasFocus() { if messageDialogHandler := pods.messageDialog.InputHandler(); messageDialogHandler != nil { messageDialogHandler(event, setFocus) } } - // create dialog dialog handler + + // create dialog handler if pods.createDialog.HasFocus() { if createDialogHandler := pods.createDialog.InputHandler(); createDialogHandler != nil { createDialogHandler(event, setFocus) } } + // confirm dialog handler if pods.confirmDialog.HasFocus() { if confirmDialogHandler := pods.confirmDialog.InputHandler(); confirmDialogHandler != nil { confirmDialogHandler(event, setFocus) } } + // command dialog handler if pods.cmdDialog.HasFocus() { if cmdHandler := pods.cmdDialog.InputHandler(); cmdHandler != nil { @@ -51,28 +57,40 @@ func (pods *Pods) InputHandler() func(event *tcell.EventKey, setFocus func(p tvi topDialogHandler(event, setFocus) } } + // container stats dialog handler if pods.statsDialog.HasFocus() { if podStatsDialogHandler := pods.statsDialog.InputHandler(); podStatsDialogHandler != nil { podStatsDialogHandler(event, setFocus) } } + // table handlers - if pods.table.HasFocus() { + if pods.table.HasFocus() { //nolint:nestif pods.selectedID, _ = pods.getSelectedItem() if event.Rune() == utils.CommandMenuKey.Rune() { if pods.cmdDialog.GetCommandCount() <= 1 { return } + pods.cmdDialog.Display() - } else if event.Key() == utils.DeleteKey.EventKey() { + setFocus(pods) + + return + } + + if event.Key() == utils.DeleteKey.EventKey() { pods.rm() - } else { - if tableHandler := pods.table.InputHandler(); tableHandler != nil { - tableHandler(event, setFocus) - } + setFocus(pods) + + return + } + + if tableHandler := pods.table.InputHandler(); tableHandler != nil { + tableHandler(event, setFocus) } } + setFocus(pods) }) } diff --git a/ui/pods/poddialogs/create.go b/ui/pods/poddialogs/create.go index e4ca1c5aa..5401e27f8 100644 --- a/ui/pods/poddialogs/create.go +++ b/ui/pods/poddialogs/create.go @@ -54,7 +54,7 @@ const ( securityOptsPageIndex ) -// PodCreateDialog implements pod create dialog +// PodCreateDialog implements pod create dialog. type PodCreateDialog struct { *tview.Box layout *tview.Flex @@ -95,7 +95,7 @@ type PodCreateDialog struct { createHandler func() } -// NewPodCreateDialog returns new pod create dialog primitive PodCreateDialog +// NewPodCreateDialog returns new pod create dialog primitive PodCreateDialog. func NewPodCreateDialog() *PodCreateDialog { podDialog := PodCreateDialog{ Box: tview.NewBox(), @@ -113,7 +113,8 @@ func NewPodCreateDialog() *PodCreateDialog { "DNS Setup", "Infra Setup", "Networking", - "Security Options"}, + "Security Options", + }, activePageIndex: 0, display: false, podNameField: tview.NewInputField(), @@ -330,6 +331,7 @@ func NewPodCreateDialog() *PodCreateDialog { podDialog.setActiveCategory(0) podDialog.initCustomInputHanlers() + return &podDialog } @@ -403,32 +405,31 @@ func (d *PodCreateDialog) setupLayout() { // add it to layout. _, layoutWidth := utils.AlignStringListWidth(d.categoryLabels) layout := tview.NewFlex().SetDirection(tview.FlexColumn) - layout.AddItem(d.categories, layoutWidth+6, 0, true) + layout.AddItem(d.categories, layoutWidth+6, 0, true) //nolint:gomnd layout.AddItem(d.categoryPages, 0, 1, true) layout.SetBackgroundColor(bgColor) d.layout.AddItem(layout, 0, 1, true) - } -// Display displays this primitive +// Display displays this primitive. func (d *PodCreateDialog) Display() { d.display = true d.initData() d.focusElement = categoryPagesFocus } -// IsDisplay returns true if primitive is shown +// IsDisplay returns true if primitive is shown. func (d *PodCreateDialog) IsDisplay() bool { return d.display } -// Hide stops displaying this primitive +// Hide stops displaying this primitive. func (d *PodCreateDialog) Hide() { d.display = false } -// HasFocus returns whether or not this primitive has focus +// HasFocus returns whether or not this primitive has focus. func (d *PodCreateDialog) HasFocus() bool { if d.categories.HasFocus() || d.categoryPages.HasFocus() { return true @@ -437,14 +438,14 @@ func (d *PodCreateDialog) HasFocus() bool { return d.Box.HasFocus() || d.form.HasFocus() } -// dropdownHasFocus returns true if pod create dialog dropdown primitives -// has focus +// dropdownHasFocus returns true if pod create dialog dropdown primitives. +// has focus. func (d *PodCreateDialog) dropdownHasFocus() bool { return d.podNetworkField.HasFocus() } -// Focus is called when this primitive receives focus -func (d *PodCreateDialog) Focus(delegate func(p tview.Primitive)) { +// Focus is called when this primitive receives focus. +func (d *PodCreateDialog) Focus(delegate func(p tview.Primitive)) { //nolint:cyclop switch d.focusElement { // form has focus case podFormFocus: @@ -454,14 +455,18 @@ func (d *PodCreateDialog) Focus(delegate func(p tview.Primitive)) { d.focusElement = categoriesFocus // category text view d.Focus(delegate) d.form.SetFocus(0) + return nil } + if event.Key() == tcell.KeyEnter { - //d.pullSelectHandler() + // d.pullSelectHandler() return nil } + return event }) + delegate(d.form) // category text view case categoriesFocus: @@ -469,16 +474,20 @@ func (d *PodCreateDialog) Focus(delegate func(p tview.Primitive)) { if event.Key() == tcell.KeyTab { d.focusElement = categoryPagesFocus // category page view d.Focus(delegate) + return nil } + // scroll between categories event = utils.ParseKeyEventKey(event) if event.Key() == tcell.KeyDown { d.nextCategory() } + if event.Key() == tcell.KeyUp { d.previousCategory() } + return nil }) delegate(d.categories) @@ -525,77 +534,97 @@ func (d *PodCreateDialog) Focus(delegate func(p tview.Primitive)) { case categoryPagesFocus: delegate(d.categoryPages) } - } func (d *PodCreateDialog) initCustomInputHanlers() { // newtwork dropdown d.podNetworkField.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { event = utils.ParseKeyEventKey(event) + return event }) } -// InputHandler returns input handler function for this primitive -func (d *PodCreateDialog) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) { +// InputHandler returns input handler function for this primitive. +func (d *PodCreateDialog) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) { //nolint:gocognit,lll,cyclop return d.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) { log.Debug().Msgf("pod create dialog: event %v received", event) + if event.Key() == tcell.KeyEsc && !d.dropdownHasFocus() { d.cancelHandler() + return } + if d.basicInfoPage.HasFocus() { if handler := d.basicInfoPage.InputHandler(); handler != nil { if event.Key() == tcell.KeyTab { d.setBasicInfoPageNextFocus() } + handler(event, setFocus) + return } } + if d.dnsSetupPage.HasFocus() { if handler := d.dnsSetupPage.InputHandler(); handler != nil { if event.Key() == tcell.KeyTab { d.setDNSSetupPageNextFocus() } + handler(event, setFocus) + return } } + if d.infraSetupPage.HasFocus() { if handler := d.infraSetupPage.InputHandler(); handler != nil { if event.Key() == tcell.KeyTab { d.setInfraSetupPageNextFocus() } + handler(event, setFocus) + return } } + if d.networkingPage.HasFocus() { if handler := d.networkingPage.InputHandler(); handler != nil { if event.Key() == tcell.KeyTab { d.setNetworkingPageNextFocus() } + handler(event, setFocus) + return } } + if d.securityOptsPage.HasFocus() { if handler := d.securityOptsPage.InputHandler(); handler != nil { if event.Key() == tcell.KeyTab { d.setSecurityOptsPageNextFocus() } + handler(event, setFocus) + return } } + if d.categories.HasFocus() { if categroryHandler := d.categories.InputHandler(); categroryHandler != nil { categroryHandler(event, setFocus) + return } } - if d.form.HasFocus() { + + if d.form.HasFocus() { //nolint:nestif if formHandler := d.form.InputHandler(); formHandler != nil { if event.Key() == tcell.KeyEnter { enterButton := d.form.GetButton(d.form.GetButtonCount() - 1) @@ -603,26 +632,26 @@ func (d *PodCreateDialog) InputHandler() func(event *tcell.EventKey, setFocus fu d.createHandler() } } + formHandler(event, setFocus) + return } } - }) } // SetRect set rects for this primitive. func (d *PodCreateDialog) SetRect(x, y, width, height int) { - if width > podCreateDialogMaxWidth { - emptySpace := (width - podCreateDialogMaxWidth) / 2 - x = x + emptySpace + emptySpace := (width - podCreateDialogMaxWidth) / 2 //nolint:gomnd + x += emptySpace width = podCreateDialogMaxWidth } if height > podCreateDialogHeight { - emptySpace := (height - podCreateDialogHeight) / 2 - y = y + emptySpace + emptySpace := (height - podCreateDialogHeight) / 2 //nolint:gomnd + y += emptySpace height = podCreateDialogHeight } @@ -634,25 +663,31 @@ func (d *PodCreateDialog) Draw(screen tcell.Screen) { if !d.display { return } + d.Box.DrawForSubclass(screen, d) + x, y, width, height := d.Box.GetInnerRect() + d.layout.SetRect(x, y, width, height) d.layout.Draw(screen) } -// SetCancelFunc sets form cancel button selected function +// SetCancelFunc sets form cancel button selected function. func (d *PodCreateDialog) SetCancelFunc(handler func()) *PodCreateDialog { d.cancelHandler = handler - cancelButton := d.form.GetButton(d.form.GetButtonCount() - 2) + cancelButton := d.form.GetButton(d.form.GetButtonCount() - 2) //nolint:gomnd cancelButton.SetSelectedFunc(handler) + return d } -// SetCreateFunc sets form create button selected function +// SetCreateFunc sets form create button selected function. func (d *PodCreateDialog) SetCreateFunc(handler func()) *PodCreateDialog { d.createHandler = handler enterButton := d.form.GetButton(d.form.GetButtonCount() - 1) + enterButton.SetSelectedFunc(handler) + return d } @@ -663,18 +698,24 @@ func (d *PodCreateDialog) setActiveCategory(index int) { ctgBgColor := style.GetColorHex(bgColor) d.activePageIndex = index + d.categories.Clear() + var ctgList []string + alignedList, _ := utils.AlignStringListWidth(d.categoryLabels) + for i := 0; i < len(d.categoryLabels); i++ { if i == index { ctgList = append(ctgList, fmt.Sprintf("[%s:%s:b]-> %s ", ctgTextColor, ctgBgColor, alignedList[i])) + continue } + ctgList = append(ctgList, fmt.Sprintf("[-:-:-] %s ", alignedList[i])) } - d.categories.SetText(strings.Join(ctgList, "\n")) + d.categories.SetText(strings.Join(ctgList, "\n")) // switch the page d.categoryPages.SwitchToPage(d.categoryLabels[index]) } @@ -682,20 +723,24 @@ func (d *PodCreateDialog) setActiveCategory(index int) { func (d *PodCreateDialog) nextCategory() { activePage := d.activePageIndex if d.activePageIndex < len(d.categoryLabels)-1 { - activePage = activePage + 1 + activePage++ d.setActiveCategory(activePage) + return } + d.setActiveCategory(0) } func (d *PodCreateDialog) previousCategory() { activePage := d.activePageIndex if d.activePageIndex > 0 { - activePage = activePage - 1 + activePage-- d.setActiveCategory(activePage) + return } + d.setActiveCategory(len(d.categoryLabels) - 1) } @@ -703,6 +748,7 @@ func (d *PodCreateDialog) initData() { // get available networks networkOptions := []string{""} networkList, _ := networks.List() + for i := 0; i < len(networkList); i++ { networkOptions = append(networkOptions, networkList[i][1]) } @@ -735,74 +781,126 @@ func (d *PodCreateDialog) initData() { d.podNetworkField.SetOptions(networkOptions, nil) d.podNetworkField.SetCurrentOption(0) d.podPublishField.SetText("") - } func (d *PodCreateDialog) setBasicInfoPageNextFocus() { if d.podNameField.HasFocus() { d.focusElement = podNoHostsCheckBoxFocus - } else if d.podNoHostsCheckBox.HasFocus() { + + return + } + + if d.podNoHostsCheckBox.HasFocus() { d.focusElement = podLabelsFieldFocus - } else { - d.focusElement = podFormFocus + + return } + + d.focusElement = podFormFocus } func (d *PodCreateDialog) setSecurityOptsPageNextFocus() { if d.podSelinuxLabelField.HasFocus() { d.focusElement = podApparmorFieldFocus - } else if d.podApparmorField.HasFocus() { + + return + } + + if d.podApparmorField.HasFocus() { d.focusElement = podSeccompFieldFocus - } else if d.podSeccompField.HasFocus() { + + return + } + + if d.podSeccompField.HasFocus() { d.focusElement = podMaskFieldFocus - } else if d.podMaskField.HasFocus() { + + return + } + + if d.podMaskField.HasFocus() { d.focusElement = podUnmaskFieldFocus - } else if d.podUnmaskField.HasFocus() { + + return + } + + if d.podUnmaskField.HasFocus() { d.focusElement = podNoNewPrivFieldFocus - } else { - d.focusElement = podFormFocus + + return } + + d.focusElement = podFormFocus } func (d *PodCreateDialog) setDNSSetupPageNextFocus() { if d.podDNSServerField.HasFocus() { d.focusElement = podDNSOptionsFieldFocus - } else if d.podDNSOptionsField.HasFocus() { + + return + } + + if d.podDNSOptionsField.HasFocus() { d.focusElement = podDNSSearchDomaindFieldFocus - } else { - d.focusElement = podFormFocus + + return } + + d.focusElement = podFormFocus } func (d *PodCreateDialog) setInfraSetupPageNextFocus() { if d.podInfraCheckBox.HasFocus() { d.focusElement = podInfraCommandFieldFocus - } else if d.podInfraCommandField.HasFocus() { + + return + } + + if d.podInfraCommandField.HasFocus() { d.focusElement = podInfraImageFieldFocus - } else { - d.focusElement = podFormFocus + + return } + + d.focusElement = podFormFocus } func (d *PodCreateDialog) setNetworkingPageNextFocus() { if d.podHostnameField.HasFocus() { d.focusElement = podIPAddressFieldFocus - } else if d.podIPAddressField.HasFocus() { + + return + } + + if d.podIPAddressField.HasFocus() { d.focusElement = podMacAddressFieldFocus - } else if d.podMacAddressField.HasFocus() { + + return + } + + if d.podMacAddressField.HasFocus() { d.focusElement = podAddHostFieldFocus - } else if d.podAddHostField.HasFocus() { + + return + } + + if d.podAddHostField.HasFocus() { d.focusElement = podNetworkFieldFocus - } else if d.podNetworkField.HasFocus() { + + return + } + + if d.podNetworkField.HasFocus() { d.focusElement = podPublishFieldFocus - } else { - d.focusElement = podFormFocus + + return } -} -// GetPodSpec returns pod create option spec -func (d *PodCreateDialog) GetPodSpec() pods.CreateOptions { + d.focusElement = podFormFocus +} +// GetPodSpec returns pod create option spec. +func (d *PodCreateDialog) GetPodSpec() pods.CreateOptions { //nolint:gocognit,cyclop var ( labels = make(map[string]string) dnsServers []string @@ -817,9 +915,10 @@ func (d *PodCreateDialog) GetPodSpec() pods.CreateOptions { for _, label := range strings.Split(d.podLabelsField.GetText(), " ") { if label != "" { split := strings.Split(label, "=") - if len(split) == 2 { + if len(split) == 2 { //nolint:gomnd key := split[0] value := split[1] + if key != "" && value != "" { labels[key] = value } @@ -832,11 +931,13 @@ func (d *PodCreateDialog) GetPodSpec() pods.CreateOptions { dnsServers = append(dnsServers, dns) } } + for _, do := range strings.Split(d.podDNSOptionsField.GetText(), " ") { if do != "" { dnsOptions = append(dnsOptions, do) } } + for _, ds := range strings.Split(d.podDNSSearchDomaindField.GetText(), " ") { if ds != "" { dnsSearchDomains = append(dnsSearchDomains, ds) @@ -864,23 +965,28 @@ func (d *PodCreateDialog) GetPodSpec() pods.CreateOptions { if d.podNoNewPrivField.IsChecked() { securityOpts = append(securityOpts, "no-new-privileges") } + apparmor := strings.TrimSpace(d.podApparmorField.GetText()) if apparmor != "" { securityOpts = append(securityOpts, fmt.Sprintf("apparmor=%s", apparmor)) } + seccomp := strings.TrimSpace(d.podSeccompField.GetText()) if seccomp != "" { securityOpts = append(securityOpts, fmt.Sprintf("seccomp=%s", seccomp)) } + for _, selinuxLabel := range strings.Split(d.podSelinuxLabelField.GetText(), " ") { if selinuxLabel != "" { securityOpts = append(securityOpts, fmt.Sprintf("label=%s", selinuxLabel)) } } + mask := strings.TrimSpace(d.podMaskField.GetText()) if seccomp != "" { securityOpts = append(securityOpts, fmt.Sprintf("mask=%s", mask)) } + unmask := strings.TrimSpace(d.podUnmaskField.GetText()) if seccomp != "" { securityOpts = append(securityOpts, fmt.Sprintf("unmask=%s", unmask)) @@ -904,5 +1010,6 @@ func (d *PodCreateDialog) GetPodSpec() pods.CreateOptions { SecurityOpts: securityOpts, Publish: publish, } + return opts } diff --git a/ui/pods/poddialogs/stats.go b/ui/pods/poddialogs/stats.go index 094e41d9b..96e1b77b7 100644 --- a/ui/pods/poddialogs/stats.go +++ b/ui/pods/poddialogs/stats.go @@ -33,7 +33,7 @@ const ( podStatTablePidsIndex ) -// PodStatsDialog implements the pods stats dialog primitive +// PodStatsDialog implements the pods stats dialog primitive. type PodStatsDialog struct { *tview.Box layout *tview.Flex @@ -59,21 +59,23 @@ type PodStatsDropDownOptions struct { Name string } -// NewPodStatsDialog returns new pod stats dialog +// NewPodStatsDialog returns new pod stats dialog. func NewPodStatsDialog() *PodStatsDialog { statsDialog := PodStatsDialog{ Box: tview.NewBox(), podDropDown: tview.NewDropDown(), podSortByDropDown: tview.NewDropDown(), statQueryOpts: &ppods.StatsOptions{}, - queryRefreshInterval: 3000 * time.Millisecond, + queryRefreshInterval: 3000 * time.Millisecond, //nolint:gomnd } ddUnselectedStyle := style.DropDownUnselected ddselectedStyle := style.DropDownSelected + // pod dropdown pddLabel := "POD ID:" labelBgColor := fmt.Sprintf("#%x", style.DialogBorderColor.Hex()) + statsDialog.podDropDown.SetLabel(fmt.Sprintf("[:%s:b]%s[::-]", labelBgColor, pddLabel)) statsDialog.podDropDown.SetLabelWidth(len(pddLabel) + 1) statsDialog.podDropDown.SetBackgroundColor(style.DialogBgColor) @@ -83,6 +85,7 @@ func NewPodStatsDialog() *PodStatsDialog { // pod sortby dropdown pddSortByLabel := "SORT BY:" + statsDialog.podSortByDropDown.SetLabel(fmt.Sprintf("[:%s:b]%s[::-]", labelBgColor, pddSortByLabel)) statsDialog.podSortByDropDown.SetLabelWidth(len(pddSortByLabel) + 1) statsDialog.podSortByDropDown.SetBackgroundColor(style.DialogBgColor) @@ -92,7 +95,8 @@ func NewPodStatsDialog() *PodStatsDialog { "pod ID", "container name", "cpu %", - "mem %"}, statsDialog.setStatsQuerySortBy) + "mem %", + }, statsDialog.setStatsQuerySortBy) statsDialog.podSortByDropDown.SetFieldBackgroundColor(style.InputFieldBgColor) // table @@ -141,42 +145,49 @@ func NewPodStatsDialog() *PodStatsDialog { return &statsDialog } -// Display displays this primitive +// Display displays this primitive. func (d *PodStatsDialog) Display() { d.display = true + d.podSortByDropDown.SetCurrentOption(0) + d.focusElement = podStatDialogResultTableFocus d.doneChan = make(chan bool) + d.startStatsQueryLoop() } -// IsDisplay returns true if primitive is shown +// IsDisplay returns true if primitive is shown. func (d *PodStatsDialog) IsDisplay() bool { return d.display } -// Hide stops displaying this primitive +// Hide stops displaying this primitive. func (d *PodStatsDialog) Hide() { d.display = false d.doneChan <- true + d.SetPodsOptions([]PodStatsDropDownOptions{}) + d.mu.Lock() defer d.mu.Unlock() close(d.doneChan) } -// HasFocus returns whether or not this primitive has focus +// HasFocus returns whether or not this primitive has focus. func (d *PodStatsDialog) HasFocus() bool { if d.podDropDown.HasFocus() || d.podSortByDropDown.HasFocus() { return true } + if d.table.HasFocus() || d.form.HasFocus() { return true } + return d.Box.HasFocus() } -// Focus is called when this primitive receives focus +// Focus is called when this primitive receives focus. func (d *PodStatsDialog) Focus(delegate func(p tview.Primitive)) { switch d.focusElement { case podStatDialogFormFocus: @@ -190,8 +201,8 @@ func (d *PodStatsDialog) Focus(delegate func(p tview.Primitive)) { } } -// InputHandler returns input handler function for this primitive -func (d *PodStatsDialog) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) { +// InputHandler returns input handler function for this primitive. +func (d *PodStatsDialog) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) { //nolint:gocognit,lll,cyclop return d.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) { log.Debug().Msgf("pod stats dialog: event %v received", event) // pod ID dropdown @@ -199,58 +210,74 @@ func (d *PodStatsDialog) InputHandler() func(event *tcell.EventKey, setFocus fun if event.Key() == tcell.KeyTab { d.focusElement = podStatDialogPodDropDownSoryByFocus setFocus(d) + return } + if podDropDownHandler := d.podDropDown.InputHandler(); podDropDownHandler != nil { event = utils.ParseKeyEventKey(event) podDropDownHandler(event, setFocus) + return } } + // sortby dropdown if d.podSortByDropDown.HasFocus() { if event.Key() == tcell.KeyTab { d.focusElement = podStatDialogResultTableFocus setFocus(d) + return } + if podSortByDropDownHandler := d.podSortByDropDown.InputHandler(); podSortByDropDownHandler != nil { event = utils.ParseKeyEventKey(event) podSortByDropDownHandler(event, setFocus) + return } } + // Esc key shall be after drop down so it won't overwrite default // dropdown handler if event.Key() == tcell.KeyEsc { d.doneHandler() + return } + // form if d.form.HasFocus() { if event.Key() == tcell.KeyTab { d.focusElement = podStatDialogPodDropDownFocus setFocus(d) + return } + if formHandler := d.form.InputHandler(); formHandler != nil { formHandler(event, setFocus) + return } } + // stats table if d.table.HasFocus() { if event.Key() == tcell.KeyTab { d.focusElement = podStatDialogFormFocus setFocus(d) + return } + if tableHanlder := d.table.InputHandler(); tableHanlder != nil { tableHanlder(event, setFocus) + return } } - }) } @@ -259,49 +286,60 @@ func (d *PodStatsDialog) Draw(screen tcell.Screen) { if !d.display { return } + d.Box.DrawForSubclass(screen, d) + x, y, width, height := d.Box.GetInnerRect() + d.layout.SetRect(x, y, width, height) d.layout.Draw(screen) - } // SetRect set rects for this primitive. func (d *PodStatsDialog) SetRect(x, y, width, height int) { dX := x + dialogs.DialogPadding dY := y + dialogs.DialogPadding - 1 - dWidth := width - (2 * dialogs.DialogPadding) - dHeight := height - (2 * (dialogs.DialogPadding - 1)) + dWidth := width - (2 * dialogs.DialogPadding) //nolint:gomnd + dHeight := height - (2 * (dialogs.DialogPadding - 1)) //nolint:gomnd + d.Box.SetRect(dX, dY, dWidth, dHeight) } -// SetDoneFunc sets form cancel button selected function +// SetDoneFunc sets form cancel button selected function. func (d *PodStatsDialog) SetDoneFunc(handler func()) *PodStatsDialog { d.doneHandler = handler cancelButton := d.form.GetButton(d.form.GetButtonCount() - 1) + cancelButton.SetSelectedFunc(handler) + return d } -// SetPodsOptions sets pod drop down options +// SetPodsOptions sets pod drop down options. func (d *PodStatsDialog) SetPodsOptions(options []PodStatsDropDownOptions) { maxWidth := 0 d.podDropDownOptions = options + if len(options) == 0 { return } + podOptions := []string{"all"} + for i := 0; i < len(options); i++ { item := options[i].ID if options[i].Name != "" { item = fmt.Sprintf("%s (%s)", item, options[i].Name) } + if len(item) > maxWidth { maxWidth = len(item) } + podOptions = append(podOptions, item) } - maxWidth = maxWidth + 11 + + maxWidth += 11 d.controlLayout.ResizeItem(d.podDropDown, maxWidth, 0) d.podDropDown.SetOptions(podOptions, d.setStatsQueryPodIDs) d.podDropDown.SetCurrentOption(0) @@ -309,26 +347,32 @@ func (d *PodStatsDialog) SetPodsOptions(options []PodStatsDropDownOptions) { func (d *PodStatsDialog) query() { opts := d.getStatsQueryOptions() + podStats, err := ppods.Stats(opts) if err != nil { log.Error().Msgf("pod stats dialog: query error: %v", err) + return } + d.updateData(podStats) } func (d *PodStatsDialog) startStatsQueryLoop() { log.Debug().Msgf("pod stats dialog: starting pod stats query loop") + go func() { tick := time.NewTicker(d.queryRefreshInterval) // initial query d.query() + for { select { case <-tick.C: d.query() case <-d.doneChan: log.Debug().Msgf("pod stats dialog: stats query loop stopped") + return } } @@ -339,6 +383,7 @@ func (d *PodStatsDialog) getStatsQueryOptions() *ppods.StatsOptions { d.mu.Lock() opts := d.statQueryOpts d.mu.Unlock() + return opts } @@ -346,8 +391,10 @@ func (d *PodStatsDialog) setStatsQueryPodIDs(name string, index int) { if index == -1 { return } + d.mu.Lock() defer d.mu.Unlock() + if index == 0 { d.statQueryOpts.IDs = d.getAllPodIDs() } else { @@ -355,6 +402,7 @@ func (d *PodStatsDialog) setStatsQueryPodIDs(name string, index int) { d.podDropDownOptions[index-1].ID, } } + go d.query() } @@ -362,25 +410,32 @@ func (d *PodStatsDialog) setStatsQuerySortBy(name string, index int) { if index == -1 { return } + d.mu.Lock() defer d.mu.Unlock() d.statQueryOpts.SortBy = index + go d.query() } func (d *PodStatsDialog) getAllPodIDs() []string { - var ids []string + ids := make([]string, 0) + for _, item := range d.podDropDownOptions { ids = append(ids, item.ID) } + return ids } func (d *PodStatsDialog) initTableUI() { - var tableHeaders = []string{"POD ID", "CID", "NAME", "CPU %", "MEM USAGE / LIMIT", "MEM %", "NET IO", "BLOCK IO", "PIDS"} + tableHeaders := []string{"POD ID", "CID", "NAME", "CPU %", "MEM USAGE / LIMIT", "MEM %", "NET IO", "BLOCK IO", "PIDS"} + headerBgColor := style.TableHeaderBgColor headerFgColor := style.TableHeaderFgColor + d.table.Clear() + for index, header := range tableHeaders { headerItem := fmt.Sprintf("[::b]%s[::-]", header) d.table.SetCell(0, index, @@ -391,6 +446,7 @@ func (d *PodStatsDialog) initTableUI() { SetTextColor(headerFgColor). SetSelectable(false)) } + d.table.SetFixed(1, 1) d.table.SetSelectable(true, false) } @@ -399,9 +455,12 @@ func (d *PodStatsDialog) updateData(statReport []ppods.StatReporter) { d.mu.Lock() d.statsResult = statReport d.mu.Unlock() + fgColor := style.DialogFgColor row := 1 + d.initTableUI() + for i := 0; i < len(d.statsResult); i++ { podID := d.statsResult[i].Pod cntID := d.statsResult[i].CID @@ -476,6 +535,6 @@ func (d *PodStatsDialog) updateData(statReport []ppods.StatReporter) { SetAlign(tview.AlignLeft). SetTextColor(fgColor)) - row = row + 1 + row++ } } diff --git a/ui/pods/pods.go b/ui/pods/pods.go index 81df826cd..835a7ec79 100644 --- a/ui/pods/pods.go +++ b/ui/pods/pods.go @@ -1,6 +1,7 @@ package pods import ( + "errors" "fmt" "strings" "sync" @@ -12,7 +13,31 @@ import ( "github.com/rivo/tview" ) -// Pods implemnents the pods page primitive +const ( + viewPodIDColIndex = 0 + iota + viewPodNameColIndex + viewPodStatusColIndex + viewPodCreatedColIndex + viewPodInfraIDColIndex + viewPodContainersColIndex +) + +var ( + errNoPodUnpause = errors.New("there is no pod to unpause") + errNoPodPause = errors.New("there is no pod to pause") + errNoPodTop = errors.New("there is no pod to display top") + errNoPodStop = errors.New("there is no pod to stop") + errNoPodStart = errors.New("there is no pod to start") + errNoPodRemove = errors.New("there is no pod to remove") + errNoPodRestart = errors.New("there is no pod to restart") + errNoPodKill = errors.New("there is no pod to kill") + errNoPodInspect = errors.New("there is no pod to display inspect") + errNoPodStat = errors.New("there is no pod to display stats") + errPodRemove = errors.New("remove error") + errPodPrune = errors.New("prune error") +) + +// Pods implemnents the pods page primitive. type Pods struct { *tview.Box title string @@ -36,7 +61,7 @@ type podsListReport struct { report []*entities.ListPodsReport } -// NewPods returns pods page view +// NewPods returns pods page view. func NewPods() *Pods { pods := &Pods{ Box: tview.NewBox(), @@ -63,7 +88,7 @@ func NewPods() *Pods { {"rm", "remove the selected pod"}, {"start", "start the selected pod"}, {"stats", "display live stream of resource usage"}, - {"stop", "stop the the selected pod"}, + {"stop", "stop the selected pod"}, {"top", "display the running processes of the pod's containers"}, {"unpause", "unpause the selected pod"}, }) @@ -106,6 +131,7 @@ func NewPods() *Pods { pods.topDialog.SetCancelFunc(func() { pods.topDialog.Hide() }) + // set confirm dialogs functions pods.confirmDialog.SetSelectedFunc(func() { pods.confirmDialog.Hide() @@ -116,6 +142,7 @@ func NewPods() *Pods { pods.remove() } }) + pods.confirmDialog.SetCancelFunc(func() { pods.confirmDialog.Hide() }) @@ -124,6 +151,7 @@ func NewPods() *Pods { pods.createDialog.SetCancelFunc(func() { pods.createDialog.Hide() }) + pods.createDialog.SetCreateFunc(func() { pods.createDialog.Hide() pods.create() @@ -135,85 +163,108 @@ func NewPods() *Pods { return pods } -// GetTitle returns primitive title +// GetTitle returns primitive title. func (pods *Pods) GetTitle() string { return pods.title } -// HasFocus returns whether or not this primitive has focus +// HasFocus returns whether or not this primitive has focus. func (pods *Pods) HasFocus() bool { if pods.table.HasFocus() || pods.errorDialog.HasFocus() { return true } + if pods.cmdDialog.HasFocus() || pods.messageDialog.IsDisplay() { return true } + if pods.progressDialog.HasFocus() || pods.topDialog.HasFocus() { return true } + if pods.confirmDialog.HasFocus() || pods.createDialog.HasFocus() { return true } + if pods.statsDialog.HasFocus() { return true } + return pods.Box.HasFocus() } -// SubDialogHasFocus returns whether or not sub dialog primitive has focus +// SubDialogHasFocus returns whether or not sub dialog primitive has focus. func (pods *Pods) SubDialogHasFocus() bool { if pods.statsDialog.HasFocus() || pods.errorDialog.HasFocus() { return true } + if pods.cmdDialog.HasFocus() || pods.messageDialog.IsDisplay() { return true } + if pods.progressDialog.HasFocus() || pods.topDialog.HasFocus() { return true } + if pods.confirmDialog.HasFocus() || pods.createDialog.HasFocus() { return true } + return false } -// Focus is called when this primitive receives focus +// Focus is called when this primitive receives focus. func (pods *Pods) Focus(delegate func(p tview.Primitive)) { // error dialog if pods.errorDialog.IsDisplay() { delegate(pods.errorDialog) + return } + // command dialog if pods.cmdDialog.IsDisplay() { delegate(pods.cmdDialog) + return } + // message dialog if pods.messageDialog.IsDisplay() { delegate(pods.messageDialog) + return } + // top dialog if pods.topDialog.IsDisplay() { delegate(pods.topDialog) + return } + // confirm dialog if pods.confirmDialog.IsDisplay() { delegate(pods.confirmDialog) + return } + // create dialog if pods.createDialog.IsDisplay() { delegate(pods.createDialog) + return } + // stats dialog if pods.statsDialog.IsDisplay() { delegate(pods.statsDialog) + return } + delegate(pods.table) } @@ -236,41 +287,52 @@ func (pods *Pods) getSelectedItem() (string, string) { func (pods *Pods) getAllItemsForStats() []poddialogs.PodStatsDropDownOptions { var items []poddialogs.PodStatsDropDownOptions + rows := pods.table.GetRowCount() + for i := 1; i < rows; i++ { podID := pods.table.GetCell(i, 0).Text podName := pods.table.GetCell(i, 1).Text + items = append(items, poddialogs.PodStatsDropDownOptions{ ID: podID, Name: podName, }) } + return items } -// HideAllDialogs hides all sub dialogs +// HideAllDialogs hides all sub dialogs. func (pods *Pods) HideAllDialogs() { if pods.errorDialog.IsDisplay() { pods.errorDialog.Hide() } + if pods.progressDialog.IsDisplay() { pods.progressDialog.Hide() } + if pods.confirmDialog.IsDisplay() { pods.confirmDialog.Hide() } + if pods.cmdDialog.IsDisplay() { pods.cmdDialog.Hide() } + if pods.messageDialog.IsDisplay() { pods.messageDialog.Hide() } + if pods.topDialog.IsDisplay() { pods.topDialog.Hide() } + if pods.createDialog.IsDisplay() { pods.createDialog.Hide() } + if pods.statsDialog.IsDisplay() { pods.statsDialog.Hide() } diff --git a/ui/pods/refresh.go b/ui/pods/refresh.go index 610ab5019..52328ef63 100644 --- a/ui/pods/refresh.go +++ b/ui/pods/refresh.go @@ -13,6 +13,7 @@ import ( func (pods *Pods) refresh() { pods.table.Clear() + expand := 1 alignment := tview.AlignLeft @@ -25,11 +26,12 @@ func (pods *Pods) refresh() { SetAlign(tview.AlignLeft). SetSelectable(false)) } - rowIndex := 1 + rowIndex := 1 podList := pods.getData() pods.table.SetTitle(fmt.Sprintf("[::b]%s[%d]", strings.ToUpper(pods.title), len(podList))) + for i := 0; i < len(podList); i++ { podID := podList[i].Id podID = podID[0:utils.IDLength] @@ -55,45 +57,45 @@ func (pods *Pods) refresh() { cellTextColor = style.PausedStatusFgColor default: podStatus = fmt.Sprintf("[red::]%s[-::] %s", "\u25BC", podStatus) - cellTextColor = style.FgColor } // id column - pods.table.SetCell(rowIndex, 0, + pods.table.SetCell(rowIndex, viewPodIDColIndex, tview.NewTableCell(podID). SetTextColor(cellTextColor). SetExpansion(expand). SetAlign(alignment)) // name column - pods.table.SetCell(rowIndex, 1, + pods.table.SetCell(rowIndex, viewPodNameColIndex, tview.NewTableCell(podName). SetTextColor(cellTextColor). SetExpansion(expand). SetAlign(alignment)) // status column - pods.table.SetCell(rowIndex, 2, + pods.table.SetCell(rowIndex, viewPodStatusColIndex, tview.NewTableCell(podStatus). SetTextColor(cellTextColor). SetExpansion(expand). SetAlign(alignment)) // created column - pods.table.SetCell(rowIndex, 3, + pods.table.SetCell(rowIndex, viewPodCreatedColIndex, tview.NewTableCell(podCreated). SetTextColor(cellTextColor). SetExpansion(expand). SetAlign(alignment)) // infra id at column - pods.table.SetCell(rowIndex, 4, + pods.table.SetCell(rowIndex, viewPodInfraIDColIndex, tview.NewTableCell(podInfraID). SetTextColor(cellTextColor). SetExpansion(expand). SetAlign(alignment)) + // # of container column - pods.table.SetCell(rowIndex, 5, + pods.table.SetCell(rowIndex, viewPodContainersColIndex, tview.NewTableCell(podNumCtn). SetTextColor(cellTextColor). SetExpansion(expand).