11package cli
22
33import (
4+ "context"
45 "fmt"
56 "io/ioutil"
67 "net/http"
@@ -21,19 +22,21 @@ import (
2122 "github.com/replicatedhq/troubleshoot/pkg/specs"
2223 "github.com/spf13/viper"
2324 spin "github.com/tj/go-spin"
25+ "golang.org/x/sync/errgroup"
2426 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2527 "k8s.io/client-go/kubernetes/scheme"
2628)
2729
2830func runPreflights (v * viper.Viper , arg string ) error {
29- fmt .Print (cursor .Hide ())
30- defer fmt .Print (cursor .Show ())
31+ if v .GetBool ("interactive" ) {
32+ fmt .Print (cursor .Hide ())
33+ defer fmt .Print (cursor .Show ())
34+ }
3135
3236 go func () {
3337 signalChan := make (chan os.Signal , 1 )
3438 signal .Notify (signalChan , os .Interrupt )
3539 <- signalChan
36- fmt .Print (cursor .Show ())
3740 os .Exit (0 )
3841 }()
3942
@@ -97,70 +100,30 @@ func runPreflights(v *viper.Viper, arg string) error {
97100
98101 var collectResults preflight.CollectResult
99102 preflightSpecName := ""
100- finishedCh := make (chan bool , 1 )
101- progressCh := make (chan interface {}, 0 ) // non-zero buffer will result in missed messages
103+
104+ progressCh := make (chan interface {})
105+ defer close (progressCh )
106+
107+ ctx , stopProgressCollection := context .WithCancel (context .Background ())
108+ // make sure we shut down progress collection goroutines if an error occurs
109+ defer stopProgressCollection ()
110+ progressCollection , ctx := errgroup .WithContext (ctx )
102111
103112 if v .GetBool ("interactive" ) {
104- s := spin .New ()
105- go func () {
106- lastMsg := ""
107- for {
108- select {
109- case msg , ok := <- progressCh :
110- if ! ok {
111- continue
112- }
113- switch msg := msg .(type ) {
114- case error :
115- c := color .New (color .FgHiRed )
116- c .Println (fmt .Sprintf ("%s\r * %v" , cursor .ClearEntireLine (), msg ))
117- case string :
118- if lastMsg == msg {
119- break
120- }
121- lastMsg = msg
122- c := color .New (color .FgCyan )
123- c .Println (fmt .Sprintf ("%s\r * %s" , cursor .ClearEntireLine (), msg ))
124- }
125- case <- time .After (time .Millisecond * 100 ):
126- fmt .Printf ("\r \033 [36mRunning Preflight checks\033 [m %s " , s .Next ())
127- case <- finishedCh :
128- fmt .Printf ("\r %s\r " , cursor .ClearEntireLine ())
129- return
130- }
131- }
132- }()
113+ progressCollection .Go (collectInteractiveProgress (ctx , progressCh ))
133114 } else {
134- // make sure we don't block any senders
135- go func () {
136- for {
137- select {
138- case msg , ok := <- progressCh :
139- if ! ok {
140- return
141- }
142- fmt .Fprintf (os .Stderr , "%v\n " , msg )
143- case <- finishedCh :
144- return
145- }
146- }
147- }()
115+ progressCollection .Go (collectNonInteractiveProgess (ctx , progressCh ))
148116 }
149117
150- defer func () {
151- close (finishedCh )
152- close (progressCh )
153- }()
154-
155118 if preflightSpec , ok := obj .(* troubleshootv1beta2.Preflight ); ok {
156- r , err := collectInCluster (preflightSpec , finishedCh , progressCh )
119+ r , err := collectInCluster (preflightSpec , progressCh )
157120 if err != nil {
158121 return errors .Wrap (err , "failed to collect in cluster" )
159122 }
160123 collectResults = * r
161124 preflightSpecName = preflightSpec .Name
162125 } else if hostPreflightSpec , ok := obj .(* troubleshootv1beta2.HostPreflight ); ok {
163- r , err := collectHost (hostPreflightSpec , finishedCh , progressCh )
126+ r , err := collectHost (hostPreflightSpec , progressCh )
164127 if err != nil {
165128 return errors .Wrap (err , "failed to collect from host" )
166129 }
@@ -183,7 +146,8 @@ func runPreflights(v *viper.Viper, arg string) error {
183146 }
184147 }
185148
186- finishedCh <- true
149+ stopProgressCollection ()
150+ progressCollection .Wait ()
187151
188152 if v .GetBool ("interactive" ) {
189153 if len (analyzeResults ) == 0 {
@@ -195,7 +159,60 @@ func runPreflights(v *viper.Viper, arg string) error {
195159 return showStdoutResults (v .GetString ("format" ), preflightSpecName , analyzeResults )
196160}
197161
198- func collectInCluster (preflightSpec * troubleshootv1beta2.Preflight , finishedCh chan bool , progressCh chan interface {}) (* preflight.CollectResult , error ) {
162+ func collectInteractiveProgress (ctx context.Context , progressCh <- chan interface {}) func () error {
163+ return func () error {
164+ spinner := spin .New ()
165+ lastMsg := ""
166+
167+ errorTxt := color .New (color .FgHiRed )
168+ infoTxt := color .New (color .FgCyan )
169+
170+ for {
171+ select {
172+ case msg := <- progressCh :
173+ switch msg := msg .(type ) {
174+ case error :
175+ errorTxt .Printf ("%s\r * %v\n " , cursor .ClearEntireLine (), msg )
176+ case string :
177+ if lastMsg == msg {
178+ break
179+ }
180+ lastMsg = msg
181+ infoTxt .Printf ("%s\r * %s\n " , cursor .ClearEntireLine (), msg )
182+
183+ }
184+ case <- time .After (time .Millisecond * 100 ):
185+ fmt .Printf ("\r %s %s " , color .CyanString ("Running Preflight Checks" ), spinner .Next ())
186+ case <- ctx .Done ():
187+ fmt .Printf ("\r %s\r " , cursor .ClearEntireLine ())
188+ return nil
189+ }
190+ }
191+ }
192+ }
193+
194+ func collectNonInteractiveProgess (ctx context.Context , progressCh <- chan interface {}) func () error {
195+ return func () error {
196+ for {
197+ select {
198+ case msg := <- progressCh :
199+ switch msg := msg .(type ) {
200+ case error :
201+ fmt .Fprintf (os .Stderr , "error - %v\n " , msg )
202+ case string :
203+ fmt .Fprintf (os .Stderr , "%s\n " , msg )
204+ case preflight.CollectProgress :
205+ fmt .Fprintf (os .Stderr , "%s\n " , msg .String ())
206+
207+ }
208+ case <- ctx .Done ():
209+ return nil
210+ }
211+ }
212+ }
213+ }
214+
215+ func collectInCluster (preflightSpec * troubleshootv1beta2.Preflight , progressCh chan interface {}) (* preflight.CollectResult , error ) {
199216 v := viper .GetViper ()
200217
201218 restConfig , err := k8sutil .GetRESTConfig ()
@@ -217,7 +234,7 @@ func collectInCluster(preflightSpec *troubleshootv1beta2.Preflight, finishedCh c
217234 }
218235
219236 if v .GetString ("since" ) != "" || v .GetString ("since-time" ) != "" {
220- err := parseTimeFlags (v , progressCh , preflightSpec .Spec .Collectors )
237+ err := parseTimeFlags (v , preflightSpec .Spec .Collectors )
221238 if err != nil {
222239 return nil , err
223240 }
@@ -240,7 +257,7 @@ func collectInCluster(preflightSpec *troubleshootv1beta2.Preflight, finishedCh c
240257 return & collectResults , nil
241258}
242259
243- func collectHost (hostPreflightSpec * troubleshootv1beta2.HostPreflight , finishedCh chan bool , progressCh chan interface {}) (* preflight.CollectResult , error ) {
260+ func collectHost (hostPreflightSpec * troubleshootv1beta2.HostPreflight , progressCh chan interface {}) (* preflight.CollectResult , error ) {
244261 collectOpts := preflight.CollectOpts {
245262 ProgressChan : progressCh ,
246263 }
@@ -253,7 +270,7 @@ func collectHost(hostPreflightSpec *troubleshootv1beta2.HostPreflight, finishedC
253270 return & collectResults , nil
254271}
255272
256- func parseTimeFlags (v * viper.Viper , progressChan chan interface {}, collectors []* troubleshootv1beta2.Collect ) error {
273+ func parseTimeFlags (v * viper.Viper , collectors []* troubleshootv1beta2.Collect ) error {
257274 var (
258275 sinceTime time.Time
259276 err error
0 commit comments