@@ -18,8 +18,10 @@ package envtest
18
18
19
19
import (
20
20
"fmt"
21
+ "net"
21
22
"os"
22
23
"path/filepath"
24
+ "strings"
23
25
"time"
24
26
25
27
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
@@ -138,8 +140,7 @@ func (te *Environment) Start() (*rest.Config, error) {
138
140
te .ControlPlane .APIServer .StartTimeout = te .ControlPlaneStartTimeout
139
141
te .ControlPlane .APIServer .StopTimeout = te .ControlPlaneStopTimeout
140
142
141
- // Start the control plane - retry if it fails
142
- if err := te .ControlPlane .Start (); err != nil {
143
+ if err := te .startControlPlane (); err != nil {
143
144
return nil , err
144
145
}
145
146
@@ -156,6 +157,32 @@ func (te *Environment) Start() (*rest.Config, error) {
156
157
return te .Config , err
157
158
}
158
159
160
+ func (te * Environment ) startControlPlane () error {
161
+ numTries , maxRetries := 0 , 5
162
+ for ; numTries < maxRetries ; numTries ++ {
163
+ // Start the control plane - retry if it fails
164
+ err := te .ControlPlane .Start ()
165
+ if err == nil {
166
+ break
167
+ }
168
+ // code snippet copied from following answer on stackoverflow
169
+ // https://stackoverflow.com/questions/51151973/catching-bind-address-already-in-use-in-golang
170
+ if opErr , ok := err .(* net.OpError ); ok {
171
+ if opErr .Op == "listen" && strings .Contains (opErr .Error (), "address already in use" ) {
172
+ if stopErr := te .ControlPlane .Stop (); stopErr != nil {
173
+ return fmt .Errorf ("failed to stop controlplane in response to bind error 'address already in use'" )
174
+ }
175
+ }
176
+ } else {
177
+ return err
178
+ }
179
+ }
180
+ if numTries == maxRetries {
181
+ return fmt .Errorf ("failed to start the controlplane. retried %d times" , numTries )
182
+ }
183
+ return nil
184
+ }
185
+
159
186
func (te * Environment ) defaultTimeouts () error {
160
187
var err error
161
188
if te .ControlPlaneStartTimeout == 0 {
0 commit comments