github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/hack/integration-cli-on-swarm/agent/master/call.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "log" 7 "strings" 8 "sync" 9 "sync/atomic" 10 "time" 11 12 "github.com/bfirsh/funker-go" 13 "github.com/docker/docker/hack/integration-cli-on-swarm/agent/types" 14 ) 15 16 const ( 17 // funkerRetryTimeout is for the issue https://github.com/bfirsh/funker/issues/3 18 // When all the funker replicas are busy in their own job, we cannot connect to funker. 19 funkerRetryTimeout = 1 * time.Hour 20 funkerRetryDuration = 1 * time.Second 21 ) 22 23 // ticker is needed for some CI (e.g., on Travis, job is aborted when no output emitted for 10 minutes) 24 func ticker(d time.Duration) chan struct{} { 25 t := time.NewTicker(d) 26 stop := make(chan struct{}) 27 go func() { 28 for { 29 select { 30 case <-t.C: 31 log.Printf("tick (just for keeping CI job active) per %s", d.String()) 32 case <-stop: 33 t.Stop() 34 } 35 } 36 }() 37 return stop 38 } 39 40 func executeTests(funkerName string, testChunks [][]string) error { 41 tickerStopper := ticker(9*time.Minute + 55*time.Second) 42 defer func() { 43 close(tickerStopper) 44 }() 45 begin := time.Now() 46 log.Printf("Executing %d chunks in parallel, against %q", len(testChunks), funkerName) 47 var wg sync.WaitGroup 48 var passed, failed uint32 49 for chunkID, tests := range testChunks { 50 log.Printf("Executing chunk %d (contains %d test filters)", chunkID, len(tests)) 51 wg.Add(1) 52 go func(chunkID int, tests []string) { 53 defer wg.Done() 54 chunkBegin := time.Now() 55 result, err := executeTestChunkWithRetry(funkerName, types.Args{ 56 ChunkID: chunkID, 57 Tests: tests, 58 }) 59 if result.RawLog != "" { 60 for _, s := range strings.Split(result.RawLog, "\n") { 61 log.Printf("Log (chunk %d): %s", chunkID, s) 62 } 63 } 64 if err != nil { 65 log.Printf("Error while executing chunk %d: %v", 66 chunkID, err) 67 atomic.AddUint32(&failed, 1) 68 } else { 69 if result.Code == 0 { 70 atomic.AddUint32(&passed, 1) 71 } else { 72 atomic.AddUint32(&failed, 1) 73 } 74 log.Printf("Finished chunk %d [%d/%d] with %d test filters in %s, code=%d.", 75 chunkID, passed+failed, len(testChunks), len(tests), 76 time.Since(chunkBegin), result.Code) 77 } 78 }(chunkID, tests) 79 } 80 wg.Wait() 81 // TODO: print actual tests rather than chunks 82 log.Printf("Executed %d chunks in %s. PASS: %d, FAIL: %d.", 83 len(testChunks), time.Since(begin), passed, failed) 84 if failed > 0 { 85 return fmt.Errorf("%d chunks failed", failed) 86 } 87 return nil 88 } 89 90 func executeTestChunk(funkerName string, args types.Args) (types.Result, error) { 91 ret, err := funker.Call(funkerName, args) 92 if err != nil { 93 return types.Result{}, err 94 } 95 tmp, err := json.Marshal(ret) 96 if err != nil { 97 return types.Result{}, err 98 } 99 var result types.Result 100 err = json.Unmarshal(tmp, &result) 101 return result, err 102 } 103 104 func executeTestChunkWithRetry(funkerName string, args types.Args) (types.Result, error) { 105 begin := time.Now() 106 for i := 0; time.Since(begin) < funkerRetryTimeout; i++ { 107 result, err := executeTestChunk(funkerName, args) 108 if err == nil { 109 log.Printf("executeTestChunk(%q, %d) returned code %d in trial %d", funkerName, args.ChunkID, result.Code, i) 110 return result, nil 111 } 112 if errorSeemsInteresting(err) { 113 log.Printf("Error while calling executeTestChunk(%q, %d), will retry (trial %d): %v", 114 funkerName, args.ChunkID, i, err) 115 } 116 // TODO: non-constant sleep 117 time.Sleep(funkerRetryDuration) 118 } 119 return types.Result{}, fmt.Errorf("could not call executeTestChunk(%q, %d) in %v", funkerName, args.ChunkID, funkerRetryTimeout) 120 } 121 122 // errorSeemsInteresting returns true if err does not seem about https://github.com/bfirsh/funker/issues/3 123 func errorSeemsInteresting(err error) bool { 124 boringSubstrs := []string{"connection refused", "connection reset by peer", "no such host", "transport endpoint is not connected", "no route to host"} 125 errS := err.Error() 126 for _, boringS := range boringSubstrs { 127 if strings.Contains(errS, boringS) { 128 return false 129 } 130 } 131 return true 132 }