From 8eaa1f210937cdfc2fff53b2ea9f35656da77595 Mon Sep 17 00:00:00 2001 From: Richard North Date: Sat, 4 Apr 2020 20:45:58 +0100 Subject: [PATCH] Cache results of auth lookups (#2213) --- .../utility/RegistryAuthLocator.java | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java index e8651167221..32b49bd8172 100644 --- a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java +++ b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java @@ -16,6 +16,8 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -38,6 +40,8 @@ public class RegistryAuthLocator { private final String commandExtension; private final File configFile; + private final Map> cache = new ConcurrentHashMap<>(); + /** * key - credential helper's name * value - helper's response for "credentials not found" use case @@ -97,9 +101,22 @@ static void setInstance(RegistryAuthLocator overrideInstance) { * @return an AuthConfig that is applicable to this specific image OR the defaultAuthConfig. */ public AuthConfig lookupAuthConfig(DockerImageName dockerImageName, AuthConfig defaultAuthConfig) { + final String registryName = effectiveRegistryName(dockerImageName); + log.debug("Looking up auth config for image: {} at registry: {}", dockerImageName, registryName); + + final Optional cachedAuth = cache.computeIfAbsent(registryName, __ -> lookupUncachedAuthConfig(registryName, dockerImageName)); - log.debug("Looking up auth config for image: {}", dockerImageName); + if (cachedAuth.isPresent()) { + log.debug("Cached auth found: [{}]", toSafeString(cachedAuth.get())); + return cachedAuth.get(); + } else { + log.debug("No matching Auth Configs - falling back to defaultAuthConfig [{}]", toSafeString(defaultAuthConfig)); + // otherwise, defaultAuthConfig should already contain any credentials available + return defaultAuthConfig; + } + } + private Optional lookupUncachedAuthConfig(String registryName, DockerImageName dockerImageName) { log.debug("RegistryAuthLocator has configFile: {} ({}) and commandPathPrefix: {}", configFile, configFile.exists() ? "exists" : "does not exist", @@ -107,37 +124,33 @@ public AuthConfig lookupAuthConfig(DockerImageName dockerImageName, AuthConfig d try { final JsonNode config = OBJECT_MAPPER.readTree(configFile); - final String registryName = effectiveRegistryName(dockerImageName); log.debug("registryName [{}] for dockerImageName [{}]", registryName, dockerImageName); // use helper preferentially (per https://docs.docker.com/engine/reference/commandline/cli/) final AuthConfig helperAuthConfig = authConfigUsingHelper(config, registryName); if (helperAuthConfig != null) { log.debug("found helper auth config [{}]", toSafeString(helperAuthConfig)); - return helperAuthConfig; + return Optional.of(helperAuthConfig); } // no credsHelper to use, using credsStore: final AuthConfig storeAuthConfig = authConfigUsingStore(config, registryName); if (storeAuthConfig != null) { log.debug("found creds store auth config [{}]", toSafeString(storeAuthConfig)); - return storeAuthConfig; + return Optional.of(storeAuthConfig); } // fall back to base64 encoded auth hardcoded in config file final AuthConfig existingAuthConfig = findExistingAuthConfig(config, registryName); if (existingAuthConfig != null) { log.debug("found existing auth config [{}]", toSafeString(existingAuthConfig)); - return existingAuthConfig; + return Optional.of(existingAuthConfig); } - - log.debug("no matching Auth Configs - falling back to defaultAuthConfig [{}]", toSafeString(defaultAuthConfig)); - // otherwise, defaultAuthConfig should already contain any credentials available } catch (Exception e) { log.warn("Failure when attempting to lookup auth config (dockerImageName: {}, configFile: {}. Falling back to docker-java default behaviour. Exception message: {}", dockerImageName, configFile, e.getMessage()); } - return defaultAuthConfig; + return Optional.empty(); } private AuthConfig findExistingAuthConfig(final JsonNode config, final String reposName) throws Exception {