From 6e1f8f71087e66b0534b4c8578c15f838d9fe477 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. Signed-off-by: Justin Randell --- 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