@@ -18,8 +18,10 @@ package envtest
1818
1919import (
2020 "fmt"
21+ "net"
2122 "os"
2223 "path/filepath"
24+ "strings"
2325 "time"
2426
2527 apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
@@ -138,8 +140,7 @@ func (te *Environment) Start() (*rest.Config, error) {
138140 te .ControlPlane .APIServer .StartTimeout = te .ControlPlaneStartTimeout
139141 te .ControlPlane .APIServer .StopTimeout = te .ControlPlaneStopTimeout
140142
141- // Start the control plane - retry if it fails
142- if err := te .ControlPlane .Start (); err != nil {
143+ if err := te .startControlPlane (); err != nil {
143144 return nil , err
144145 }
145146
@@ -156,6 +157,32 @@ func (te *Environment) Start() (*rest.Config, error) {
156157 return te .Config , err
157158}
158159
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+
159186func (te * Environment ) defaultTimeouts () error {
160187 var err error
161188 if te .ControlPlaneStartTimeout == 0 {
0 commit comments