@@ -2,103 +2,283 @@ package cli
22
33import (
44 "fmt"
5+ "io/ioutil"
6+ "os"
7+ "path"
8+ "strings"
9+ "time"
510
611 ui "github.com/gizak/termui/v3"
712 "github.com/gizak/termui/v3/widgets"
813 analyzerunner "github.com/replicatedhq/troubleshoot/pkg/analyze"
914)
1015
11- type nodeValue string
12-
13- func (nv nodeValue ) String () string {
14- return string (nv )
15- }
16+ var (
17+ selectedResult = 0
18+ isShowingSaved = false
19+ )
1620
17- func showInteractiveResults (analyzeResults []* analyzerunner.AnalyzeResult ) error {
21+ func showInteractiveResults (preflightName string , analyzeResults []* analyzerunner.AnalyzeResult ) error {
1822 if err := ui .Init (); err != nil {
1923 return err
2024 }
2125 defer ui .Close ()
2226
23- selectedResult := 0
24-
25- preflightTable := getPreflightTable (analyzeResults )
26- details := getDetails (analyzeResults [selectedResult ])
27-
28- grid := ui .NewGrid ()
29- termWidth , termHeight := ui .TerminalDimensions ()
30- grid .SetRect (0 , 0 , termWidth , termHeight )
31-
32- grid .Set (
33- ui .NewRow (1.0 ,
34- ui .NewCol (1.0 / 2 , preflightTable ),
35- ui .NewCol (1.0 / 2 , details ),
36- ),
37- )
38-
39- ui .Render (grid )
27+ drawUI (preflightName , analyzeResults )
4028
4129 uiEvents := ui .PollEvents ()
4230 for {
4331 select {
4432 case e := <- uiEvents :
4533 switch e .ID {
46- case "q" , " <C-c>" :
34+ case "<C-c>" :
4735 return nil
36+ case "q" :
37+ if isShowingSaved == true {
38+ isShowingSaved = false
39+ ui .Clear ()
40+ drawUI (preflightName , analyzeResults )
41+ } else {
42+ return nil
43+ }
44+ case "s" :
45+ filename , err := save (preflightName , analyzeResults )
46+ if err != nil {
47+ // show
48+ } else {
49+ showSaved (filename )
50+ go func () {
51+ time .Sleep (time .Second * 5 )
52+ isShowingSaved = false
53+ ui .Clear ()
54+ drawUI (preflightName , analyzeResults )
55+ }()
56+ }
4857 case "<Resize>" :
49- payload := e .Payload .(ui.Resize )
50- grid .SetRect (0 , 0 , payload .Width , payload .Height )
5158 ui .Clear ()
52- ui .Render (grid )
59+ drawUI (preflightName , analyzeResults )
60+ case "<Down>" :
61+ if selectedResult < len (analyzeResults )- 1 {
62+ selectedResult ++
63+ } else {
64+ selectedResult = 0
65+ }
66+ ui .Clear ()
67+ drawUI (preflightName , analyzeResults )
68+ case "<Up>" :
69+ if selectedResult > 0 {
70+ selectedResult --
71+ } else {
72+ selectedResult = len (analyzeResults ) - 1
73+ }
74+ ui .Clear ()
75+ drawUI (preflightName , analyzeResults )
5376 }
5477 }
5578 }
5679}
5780
58- func getPreflightTable (analyzeResults []* analyzerunner.AnalyzeResult ) * widgets.Table {
81+ func drawUI (preflightName string , analyzeResults []* analyzerunner.AnalyzeResult ) {
82+ drawGrid (analyzeResults )
83+ drawHeader (preflightName )
84+ drawFooter ()
85+ }
86+
87+ func drawGrid (analyzeResults []* analyzerunner.AnalyzeResult ) {
88+ drawPreflightTable (analyzeResults )
89+ drawDetails (analyzeResults [selectedResult ])
90+ }
91+
92+ func drawHeader (preflightName string ) {
93+ termWidth , _ := ui .TerminalDimensions ()
94+
95+ title := widgets .NewParagraph ()
96+ title .Text = fmt .Sprintf ("%s Preflight Checks" , appName (preflightName ))
97+ title .TextStyle .Fg = ui .ColorWhite
98+ title .TextStyle .Bg = ui .ColorClear
99+ title .TextStyle .Modifier = ui .ModifierBold
100+ title .Border = false
101+
102+ left := termWidth / 2 - 2 * len (title .Text )/ 3
103+ right := termWidth / 2 + (termWidth / 2 - left )
104+
105+ title .SetRect (left , 0 , right , 1 )
106+ ui .Render (title )
107+ }
108+
109+ func drawFooter () {
110+ termWidth , termHeight := ui .TerminalDimensions ()
111+
112+ instructions := widgets .NewParagraph ()
113+ instructions .Text = "[q] quit [s] save [↑][↓] scroll"
114+ instructions .Border = false
115+
116+ left := 0
117+ right := termWidth
118+ top := termHeight - 1
119+ bottom := termHeight
120+
121+ instructions .SetRect (left , top , right , bottom )
122+ ui .Render (instructions )
123+ }
124+
125+ func drawPreflightTable (analyzeResults []* analyzerunner.AnalyzeResult ) {
126+ termWidth , termHeight := ui .TerminalDimensions ()
127+
59128 table := widgets .NewTable ()
129+ table .SetRect (0 , 3 , termWidth / 2 , termHeight - 6 )
130+ table .FillRow = true
60131 table .Border = true
61132 table .Rows = [][]string {}
133+ table .ColumnWidths = []int {termWidth }
62134
63135 for i , analyzeResult := range analyzeResults {
136+ title := analyzeResult .Title
137+ if analyzeResult .IsPass {
138+ title = fmt .Sprintf ("✔ %s" , title )
139+ } else if analyzeResult .IsWarn {
140+ title = fmt .Sprintf ("⚠️ %s" , title )
141+ } else if analyzeResult .IsFail {
142+ title = fmt .Sprintf ("✘ %s" , title )
143+ }
64144 table .Rows = append (table .Rows , []string {
65- analyzeResult . Title ,
145+ title ,
66146 })
67147
68148 if analyzeResult .IsPass {
69- table .RowStyles [i ] = ui .NewStyle (ui .ColorGreen , ui .ColorClear , ui .ModifierBold )
149+ if i == selectedResult {
150+ table .RowStyles [i ] = ui .NewStyle (ui .ColorGreen , ui .ColorClear , ui .ModifierReverse )
151+ } else {
152+ table .RowStyles [i ] = ui .NewStyle (ui .ColorGreen , ui .ColorClear )
153+ }
70154 } else if analyzeResult .IsWarn {
71- table .RowStyles [i ] = ui .NewStyle (ui .ColorYellow , ui .ColorClear , ui .ModifierBold )
155+ if i == selectedResult {
156+ table .RowStyles [i ] = ui .NewStyle (ui .ColorYellow , ui .ColorClear , ui .ModifierReverse )
157+ } else {
158+ table .RowStyles [i ] = ui .NewStyle (ui .ColorYellow , ui .ColorClear )
159+ }
72160 } else if analyzeResult .IsFail {
73- table .RowStyles [i ] = ui .NewStyle (ui .ColorRed , ui .ColorClear )
161+ if i == selectedResult {
162+ table .RowStyles [i ] = ui .NewStyle (ui .ColorRed , ui .ColorClear , ui .ModifierReverse )
163+ } else {
164+ table .RowStyles [i ] = ui .NewStyle (ui .ColorRed , ui .ColorClear )
165+ }
74166 }
75167 }
76168
77- return table
169+ ui . Render ( table )
78170}
79171
80- func getDetails (analysisResult * analyzerunner.AnalyzeResult ) * ui.Grid {
81- grid := ui .NewGrid ()
82-
83- entries := []interface {}{}
172+ func drawDetails (analysisResult * analyzerunner.AnalyzeResult ) {
173+ termWidth , _ := ui .TerminalDimensions ()
84174
175+ currentTop := 4
85176 title := widgets .NewParagraph ()
86177 title .Text = analysisResult .Title
87178 title .Border = false
88- entries = append (entries , ui .NewRow (0.2 , ui .NewCol (1.0 , title )))
179+ if analysisResult .IsPass {
180+ title .TextStyle = ui .NewStyle (ui .ColorGreen , ui .ColorClear , ui .ModifierBold )
181+ } else if analysisResult .IsWarn {
182+ title .TextStyle = ui .NewStyle (ui .ColorYellow , ui .ColorClear , ui .ModifierBold )
183+ } else if analysisResult .IsFail {
184+ title .TextStyle = ui .NewStyle (ui .ColorRed , ui .ColorClear , ui .ModifierBold )
185+ }
186+ height := estimateNumberOfLines (title .Text , termWidth / 2 )
187+ title .SetRect (termWidth / 2 , currentTop , termWidth , currentTop + height )
188+ ui .Render (title )
189+ currentTop = currentTop + height + 1
89190
90191 message := widgets .NewParagraph ()
91192 message .Text = analysisResult .Message
92193 message .Border = false
93- entries = append (entries , ui .NewRow (0.2 , ui .NewCol (1.0 , message )))
194+ height = estimateNumberOfLines (message .Text , termWidth / 2 ) + 2
195+ message .SetRect (termWidth / 2 , currentTop , termWidth , currentTop + height )
196+ ui .Render (message )
197+ currentTop = currentTop + height + 1
94198
95199 if analysisResult .URI != "" {
96200 uri := widgets .NewParagraph ()
97201 uri .Text = fmt .Sprintf ("For more information: %s" , analysisResult .URI )
98202 uri .Border = false
99- entries = append (entries , ui .NewRow (0.2 , ui .NewCol (1.0 , uri )))
203+ height = estimateNumberOfLines (uri .Text , termWidth / 2 )
204+ uri .SetRect (termWidth / 2 , currentTop , termWidth , currentTop + height )
205+ ui .Render (uri )
206+ currentTop = currentTop + height + 1
207+ }
208+ }
209+
210+ func estimateNumberOfLines (text string , width int ) int {
211+ lines := len (text )/ width + 1
212+ return lines
213+ }
214+
215+ func save (preflightName string , analyzeResults []* analyzerunner.AnalyzeResult ) (string , error ) {
216+ filename := path .Join (homeDir (), fmt .Sprintf ("%s-results.txt" , preflightName ))
217+ _ , err := os .Stat (filename )
218+ if err == nil {
219+ os .Remove (filename )
220+ }
221+
222+ results := fmt .Sprintf ("%s Preflight Checks\n \n " , appName (preflightName ))
223+ for _ , analyzeResult := range analyzeResults {
224+ result := ""
225+
226+ if analyzeResult .IsPass {
227+ result = "Check PASS\n "
228+ } else if analyzeResult .IsWarn {
229+ result = "Check WARN\n "
230+ } else if analyzeResult .IsFail {
231+ result = "Check FAIL\n "
232+ }
233+
234+ result = result + fmt .Sprintf ("Title: %s\n " , analyzeResult .Title )
235+ result = result + fmt .Sprintf ("Message: %s\n " , analyzeResult .Message )
236+
237+ if analyzeResult .URI != "" {
238+ result = result + fmt .Sprintf ("URI: %s\n " , analyzeResult .URI )
239+ }
240+
241+ result = result + "\n ------------\n "
242+
243+ results = results + result
244+ }
245+
246+ if err := ioutil .WriteFile (filename , []byte (results ), 0644 ); err != nil {
247+ return "" , err
248+ }
249+
250+ return filename , nil
251+ }
252+
253+ func showSaved (filename string ) {
254+ termWidth , termHeight := ui .TerminalDimensions ()
255+
256+ savedMessage := widgets .NewParagraph ()
257+ savedMessage .Text = fmt .Sprintf ("Preflight results saved to\n \n %s" , filename )
258+ savedMessage .WrapText = true
259+ savedMessage .Border = true
260+
261+ left := termWidth / 2 - 20
262+ right := termWidth / 2 + 20
263+ top := termHeight / 2 - 4
264+ bottom := termHeight / 2 + 4
265+
266+ savedMessage .SetRect (left , top , right , bottom )
267+ ui .Render (savedMessage )
268+
269+ isShowingSaved = true
270+ }
271+
272+ func appName (preflightName string ) string {
273+ words := strings .Split (strings .Title (strings .Replace (preflightName , "-" , " " , - 1 )), " " )
274+ casedWords := []string {}
275+ for _ , word := range words {
276+ if strings .ToLower (word ) == "ai" {
277+ casedWords = append (casedWords , "AI" )
278+ } else {
279+ casedWords = append (casedWords , word )
280+ }
100281 }
101282
102- grid .Set (entries ... )
103- return grid
283+ return strings .Join (casedWords , " " )
104284}
0 commit comments