From 49860e5b2d82a7a72ff25990b2539e5c6c7929fb Mon Sep 17 00:00:00 2001 From: jrandell Date: Thu, 8 Aug 2024 13:49:08 -0400 Subject: [PATCH] Fixes #333 Handle parallel calls to add with osxkeychain When `docker login` is called in parallel, some of the calls fail with the error `The specified item already exists in the keychain`. This PR checks for this specific error in `osxkeychain.Add` and returns ok. If one process loses the race in this case, the desired credentials were saved by another process and we can ignore the error. --- osxkeychain/osxkeychain.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osxkeychain/osxkeychain.go b/osxkeychain/osxkeychain.go index e6347304..9e4778eb 100644 --- a/osxkeychain/osxkeychain.go +++ b/osxkeychain/osxkeychain.go @@ -24,6 +24,10 @@ import ( // when the credentials are not in the keychain. const errCredentialsNotFound = "The specified item could not be found in the keychain." +// errCredentialsAlreadyExist is the specific error message returned by OS X +// when the credentials are already in the keychain. +const errCredentialsAlreadyExist = "The specified item already exists in the keychain." + // errCredentialsNotFound is the specific error message returned by OS X // when environment does not allow showing dialog to unlock keychain. const errInteractionNotAllowed = "User interaction is not allowed." @@ -54,7 +58,16 @@ func (h Osxkeychain) Add(creds *credentials.Credentials) error { errMsg := C.keychain_add(s, label, username, secret) if errMsg != nil { defer C.free(unsafe.Pointer(errMsg)) - return errors.New(C.GoString(errMsg)) + switch goMsg := C.GoString(errMsg); goMsg { + case errCredentialsAlreadyExist: + // If docker login is called in parallel, we may try to + // save the same credentials twice. In that case, return + // ok, because although we lost the race, the desired + // credentials were saved by another process. + return nil + default: + return errors.New(goMsg) + } } return nil