From 70856f475a00a41f1da05e36871639486644853f Mon Sep 17 00:00:00 2001 From: Steven Hartland Date: Tue, 16 Jul 2024 08:13:32 +0100 Subject: [PATCH 1/2] perf: optimise docker authentication config lookup (#2646) Improve the performance of retrieving docker authentication configuration by performing requests in parallel and only performing lookups once. For a simple double layer image with standard Google Cloud Registry configuration this reduced the time spent doing authentication config lookups from ~4.6s to ~400ms. --- docker_auth.go | 79 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 27 deletions(-) diff --git a/docker_auth.go b/docker_auth.go index 04b8527ccb7..d45393f3252 100644 --- a/docker_auth.go +++ b/docker_auth.go @@ -6,6 +6,7 @@ import ( "encoding/json" "net/url" "os" + "sync" "github.com/cpuguy83/dockercfg" "github.com/docker/docker/api/types/registry" @@ -79,52 +80,76 @@ func defaultRegistry(ctx context.Context) string { return info.IndexServerAddress } +// authConfig represents the details of the auth config for a registry. +type authConfig struct { + key string + cfg registry.AuthConfig +} + // getDockerAuthConfigs returns a map with the auth configs from the docker config file // using the registry as the key -func getDockerAuthConfigs() (map[string]registry.AuthConfig, error) { +var getDockerAuthConfigs = sync.OnceValues(func() (map[string]registry.AuthConfig, error) { cfg, err := getDockerConfig() if err != nil { return nil, err } cfgs := map[string]registry.AuthConfig{} + results := make(chan authConfig, len(cfg.AuthConfigs)+len(cfg.CredentialHelpers)) + var wg sync.WaitGroup + wg.Add(len(cfg.AuthConfigs) + len(cfg.CredentialHelpers)) for k, v := range cfg.AuthConfigs { - ac := registry.AuthConfig{ - Auth: v.Auth, - Email: v.Email, - IdentityToken: v.IdentityToken, - Password: v.Password, - RegistryToken: v.RegistryToken, - ServerAddress: v.ServerAddress, - Username: v.Username, - } + go func(k string, v dockercfg.AuthConfig) { + defer wg.Done() + + ac := registry.AuthConfig{ + Auth: v.Auth, + Email: v.Email, + IdentityToken: v.IdentityToken, + Password: v.Password, + RegistryToken: v.RegistryToken, + ServerAddress: v.ServerAddress, + Username: v.Username, + } + + if v.Username == "" && v.Password == "" { + u, p, _ := dockercfg.GetRegistryCredentials(k) + ac.Username = u + ac.Password = p + } + + if v.Auth == "" { + ac.Auth = base64.StdEncoding.EncodeToString([]byte(ac.Username + ":" + ac.Password)) + } + results <- authConfig{key: k, cfg: ac} + }(k, v) + } + + // in the case where the auth field in the .docker/conf.json is empty, and the user has credential helpers registered + // the auth comes from there + for k := range cfg.CredentialHelpers { + go func(k string) { + defer wg.Done() - if v.Username == "" && v.Password == "" { + ac := registry.AuthConfig{} u, p, _ := dockercfg.GetRegistryCredentials(k) ac.Username = u ac.Password = p - } - - if v.Auth == "" { - ac.Auth = base64.StdEncoding.EncodeToString([]byte(ac.Username + ":" + ac.Password)) - } - - cfgs[k] = ac + results <- authConfig{key: k, cfg: ac} + }(k) } - // in the case where the auth field in the .docker/conf.json is empty, and the user has credential helpers registered - // the auth comes from there - for k := range cfg.CredentialHelpers { - ac := registry.AuthConfig{} - u, p, _ := dockercfg.GetRegistryCredentials(k) - ac.Username = u - ac.Password = p + go func() { + wg.Wait() + close(results) + }() - cfgs[k] = ac + for ac := range results { + cfgs[ac.key] = ac.cfg } return cfgs, nil -} +}) // getDockerConfig returns the docker config file. It will internally check, in this particular order: // 1. the DOCKER_AUTH_CONFIG environment variable, unmarshalling it into a dockercfg.Config From d16750b3a3ea12b69993b864822827108580c1ab Mon Sep 17 00:00:00 2001 From: Steven Hartland Date: Tue, 16 Jul 2024 11:39:34 +0100 Subject: [PATCH 2/2] fix: retry on label error (#2644) Add system errors to list of permanent errors which fixes infinite retries when a build is attempted with an invalid label. --- docker.go | 1 + 1 file changed, 1 insertion(+) diff --git a/docker.go b/docker.go index 4a641b87085..5e11b12425f 100644 --- a/docker.go +++ b/docker.go @@ -1653,6 +1653,7 @@ var permanentClientErrors = []func(error) bool{ errdefs.IsUnauthorized, errdefs.IsForbidden, errdefs.IsNotImplemented, + errdefs.IsSystem, } func isPermanentClientError(err error) bool {