diff --git a/client.go b/client.go index 075d09f74..201b18f9c 100644 --- a/client.go +++ b/client.go @@ -64,8 +64,9 @@ func New(address string, opts ...ClientOpt) (*Client, error) { gopts := []grpc.DialOption{ grpc.WithBlock(), grpc.WithInsecure(), - grpc.WithTimeout(100 * time.Second), + grpc.WithTimeout(60 * time.Second), grpc.FailOnNonTempDialError(true), + grpc.WithBackoffMaxDelay(3 * time.Second), grpc.WithDialer(dialer), } if len(copts.dialOptions) > 0 { diff --git a/client_unix.go b/client_unix.go index d5a9f63ec..565a7d166 100644 --- a/client_unix.go +++ b/client_unix.go @@ -5,13 +5,68 @@ package containerd import ( "fmt" "net" + "os" "strings" + "syscall" "time" + + "github.com/pkg/errors" ) +func isNoent(err error) bool { + if err != nil { + if nerr, ok := err.(*net.OpError); ok { + if serr, ok := nerr.Err.(*os.SyscallError); ok { + if serr.Err == syscall.ENOENT { + return true + } + } + } + } + return false +} + +type dialResult struct { + c net.Conn + err error +} + func dialer(address string, timeout time.Duration) (net.Conn, error) { + var ( + stopC = make(chan struct{}) + synC = make(chan *dialResult) + ) address = strings.TrimPrefix(address, "unix://") - return net.DialTimeout("unix", address, timeout) + go func() { + defer close(synC) + for { + select { + case <-stopC: + return + default: + c, err := net.DialTimeout("unix", address, timeout) + if isNoent(err) { + <-time.After(10 * time.Millisecond) + continue + } + synC <- &dialResult{c, err} + return + } + } + }() + select { + case dr := <-synC: + return dr.c, dr.err + case <-time.After(timeout): + close(stopC) + go func() { + dr := <-synC + if dr != nil { + dr.c.Close() + } + }() + return nil, errors.Errorf("dial %s: no such file or directory", address) + } } func dialAddress(address string) string {