Skip to content

Commit 997a220

Browse files
authored
Merge pull request #14 from replicatedhq/preflight-ui
preflight ui
2 parents fe8f2be + 1150e10 commit 997a220

File tree

3 files changed

+237
-85
lines changed

3 files changed

+237
-85
lines changed

cmd/preflight/cli/interactive_results.go

Lines changed: 222 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,103 +2,283 @@ package cli
22

33
import (
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
}

cmd/preflight/cli/run_nocrd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func runPreflightsNoCRD(v *viper.Viper, arg string) error {
8888
}
8989

9090
if v.GetBool("interactive") {
91-
return showInteractiveResults(analyzeResults)
91+
return showInteractiveResults(preflight.Name, analyzeResults)
9292
}
9393

9494
fmt.Printf("only interactive results are supported\n")

0 commit comments

Comments
 (0)