@@ -12,6 +12,8 @@ import (
1212 "strings"
1313 "time"
1414
15+ "github.com/cenkalti/backoff"
16+ "github.com/hashicorp/go-multierror"
1517 "github.com/jetstack/preflight/api"
1618 "github.com/jetstack/preflight/pkg/client"
1719 "github.com/jetstack/preflight/pkg/datagatherer"
@@ -25,8 +27,8 @@ var ConfigFilePath string
2527// AuthToken is the authorization token that will be used for API calls
2628var AuthToken string
2729
28- // Period is the number of seconds between scans
29- var Period uint
30+ // Period is the time waited between scans
31+ var Period time. Duration
3032
3133// OneShot flag causes agent to run once
3234var OneShot bool
@@ -40,6 +42,9 @@ var OutputPath string
4042// InputPath is where the agent will read data from instead of gathering from clusters if specified
4143var InputPath string
4244
45+ // BackoffMaxTime is the maximum time for which data gatherers will be retried
46+ var BackoffMaxTime time.Duration
47+
4348// Run starts the agent process
4449func Run (cmd * cobra.Command , args []string ) {
4550 ctx := context .Background ()
@@ -49,7 +54,7 @@ func Run(cmd *cobra.Command, args []string) {
4954 if OneShot {
5055 break
5156 }
52- time .Sleep (time . Duration ( Period ) * time . Second )
57+ time .Sleep (Period )
5358 }
5459}
5560
@@ -170,7 +175,16 @@ func gatherAndOutputData(ctx context.Context, config Config, preflightClient *cl
170175 }
171176 log .Println ("Data saved locally to" , OutputPath )
172177 } else {
173- postData (config , preflightClient , readings )
178+ backOff := backoff .NewExponentialBackOff ()
179+ backOff .MaxElapsedTime = BackoffMaxTime
180+ post := func () error {
181+ return postData (config , preflightClient , readings )
182+ }
183+ err := backoff .RetryNotify (post , backOff , notify )
184+ if err != nil {
185+ log .Fatalf ("%v" , err )
186+ }
187+
174188 }
175189}
176190
@@ -193,39 +207,67 @@ func gatherData(ctx context.Context, config Config) []*api.DataReading {
193207 dataGatherers [dgConfig .Name ] = dg
194208 }
195209
196- // Fetch from all datagatherers
197- now := time .Now ()
210+ //TODO Change backoff parameters to those desired
211+ backOff := backoff .NewExponentialBackOff ()
212+ backOff .MaxElapsedTime = BackoffMaxTime
198213 readings := []* api.DataReading {}
199- failedDataGatherers := []string {}
200- for k , dg := range dataGatherers {
201- i , err := dg .Fetch ()
202- if err != nil {
203- log .Printf ("Error fetching with DataGatherer %q: %s" , k , err )
204- failedDataGatherers = append (failedDataGatherers , k )
205- continue
206- }
214+ completedDataGatherers := make (map [string ]bool , len (dataGatherers ))
207215
208- log .Printf ("Gathered data for %q:\n " , k )
209-
210- readings = append (readings , & api.DataReading {
211- ClusterID : config .ClusterID ,
212- DataGatherer : k ,
213- Timestamp : api.Time {Time : now },
214- Data : i ,
215- })
216+ // Fetch from all datagatherers
217+ getReadings := func () error {
218+ var dgError * multierror.Error
219+ for k , dg := range dataGatherers {
220+ if completedDataGatherers [k ] {
221+ continue
222+ }
223+ dgData , err := dg .Fetch ()
224+ if err != nil {
225+ err = fmt .Errorf ("%s: %v" , k , err )
226+ dgError = multierror .Append (dgError , err )
227+ continue
228+ } else {
229+ completedDataGatherers [k ] = true
230+
231+ log .Printf ("Successfully gathered data for %q" , k )
232+ now := time .Now ()
233+
234+ readings = append (readings , & api.DataReading {
235+ ClusterID : config .ClusterID ,
236+ DataGatherer : k ,
237+ Timestamp : api.Time {Time : now },
238+ Data : dgData ,
239+ })
240+ }
241+ }
242+ dgError .ErrorFormat = func (es []error ) string {
243+ points := make ([]string , len (es ))
244+ for i , err := range es {
245+ points [i ] = fmt .Sprintf ("* %s" , err )
246+ }
247+ return fmt .Sprintf (
248+ "The following %d data gatherer(s) have failed:\n \t %s" ,
249+ len (es ), strings .Join (points , "\n \t " ))
250+ }
251+ return dgError
216252 }
217253
218- if len (failedDataGatherers ) > 0 {
219- log .Printf (
220- "Warning, the following DataGatherers failed, %s. Their data is not being sent." ,
221- strings .Join (failedDataGatherers , ", " ),
222- )
254+ err := backoff .RetryNotify (getReadings , backOff , notify )
255+ if err != nil {
256+ log .Println (err )
257+ log .Printf ("This will not be retried" )
258+ } else {
259+ log .Printf ("All data gatherers successfull" )
223260 }
224261 return readings
225262}
226263
227- func postData (config Config , preflightClient * client.PreflightClient , readings []* api.DataReading ) {
264+ func notify (err error , t time.Duration ) {
265+ log .Println (err , "\n Retrying..." )
266+ }
267+
268+ func postData (config Config , preflightClient * client.PreflightClient , readings []* api.DataReading ) error {
228269 baseURL := config .Server
270+ var err error
229271
230272 log .Println ("Running Agent..." )
231273 log .Println ("Posting data to " , baseURL )
@@ -241,7 +283,7 @@ func postData(config Config, preflightClient *client.PreflightClient, readings [
241283 res , err := preflightClient .Post (path , bytes .NewBuffer (data ))
242284
243285 if err != nil {
244- log . Fatalf ("Failed to post data: %+v" , err )
286+ return fmt . Errorf ("Failed to post data: %+v" , err )
245287 }
246288 if code := res .StatusCode ; code < 200 || code >= 300 {
247289 errorContent := ""
@@ -251,15 +293,13 @@ func postData(config Config, preflightClient *client.PreflightClient, readings [
251293 }
252294 defer res .Body .Close ()
253295
254- log . Fatalf ("Received response with status code %d. Body: %s" , code , errorContent )
296+ return fmt . Errorf ("Received response with status code %d. Body: %s" , code , errorContent )
255297 }
256298 } else {
257299 err := preflightClient .PostDataReadings (config .OrganizationID , readings )
258- // TODO: handle errors gracefully: e.g. handle retries when it is possible
259300 if err != nil {
260- log . Fatalf ("Post to server failed: %+v" , err )
301+ return fmt . Errorf ("Post to server failed: %+v" , err )
261302 }
262303 }
263-
264- log .Println ("Data sent successfully." )
304+ return err
265305}
0 commit comments