Skip to content

Commit 150e798

Browse files
committed
authprovider: Add support for expiring auth cache
This exposes a mechanism to expire cached auth configs. Signed-off-by: Brian Goff <[email protected]>
1 parent 81d49f7 commit 150e798

File tree

3 files changed

+86
-20
lines changed

3 files changed

+86
-20
lines changed

cmd/buildctl/build.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,11 @@ func buildAction(clicontext *cli.Context) error {
187187
if err != nil {
188188
return err
189189
}
190-
attachable := []session.Attachable{authprovider.NewDockerAuthProvider(dockerConfig, tlsConfigs)}
190+
191+
attachable := []session.Attachable{authprovider.NewDockerAuthProvider(authprovider.DockerAuthProviderConfig{
192+
ConfigFile: dockerConfig,
193+
TLSConfigs: tlsConfigs,
194+
})}
191195

192196
if ssh := clicontext.StringSlice("ssh"); len(ssh) > 0 {
193197
configs, err := build.ParseSSH(ssh)

session/auth/authprovider/authprovider.go

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,43 @@ const (
3838
dockerHubRegistryHost = "registry-1.docker.io"
3939
)
4040

41-
func NewDockerAuthProvider(cfg *configfile.ConfigFile, tlsConfigs map[string]*AuthTLSConfig) session.Attachable {
41+
type DockerAuthProviderConfig struct {
42+
// ConfigFile is the docker config file
43+
ConfigFile *configfile.ConfigFile
44+
// TLSConfigs is a map of host to TLS config
45+
TLSConfigs map[string]*AuthTLSConfig
46+
// ExpireCachedAuth is a function that returns true auth config should be refreshed
47+
// instead of using a pre-cached result.
48+
// If nil then the cached result will expire after 10 minutes.
49+
// The function is called with the time the cached auth config was created
50+
// and the server URL the auth config is for.
51+
ExpireCachedAuth func(created time.Time, serverURL string) bool
52+
}
53+
54+
type authConfigCacheEntry struct {
55+
Created time.Time
56+
Auth *types.AuthConfig
57+
}
58+
59+
func NewDockerAuthProvider(cfg DockerAuthProviderConfig) session.Attachable {
60+
if cfg.ExpireCachedAuth == nil {
61+
cfg.ExpireCachedAuth = func(created time.Time, _ string) bool {
62+
return time.Since(created) > 10*time.Minute
63+
}
64+
}
4265
return &authProvider{
43-
authConfigCache: map[string]*types.AuthConfig{},
44-
config: cfg,
66+
authConfigCache: map[string]authConfigCacheEntry{},
67+
expireAc: cfg.ExpireCachedAuth,
68+
config: cfg.ConfigFile,
4569
seeds: &tokenSeeds{dir: config.Dir()},
4670
loggerCache: map[string]struct{}{},
47-
tlsConfigs: tlsConfigs,
71+
tlsConfigs: cfg.TLSConfigs,
4872
}
4973
}
5074

5175
type authProvider struct {
52-
authConfigCache map[string]*types.AuthConfig
76+
authConfigCache map[string]authConfigCacheEntry
77+
expireAc func(time.Time, string) bool
5378
config *configfile.ConfigFile
5479
seeds *tokenSeeds
5580
logger progresswriter.Logger
@@ -247,17 +272,25 @@ func (ap *authProvider) getAuthConfig(ctx context.Context, host string) (*types.
247272
host = dockerHubConfigfileKey
248273
}
249274

250-
if _, exists := ap.authConfigCache[host]; !exists {
251-
span, _ := tracing.StartSpan(ctx, fmt.Sprintf("load credentials for %s", host))
252-
ac, err := ap.config.GetAuthConfig(host)
253-
tracing.FinishWithError(span, err)
254-
if err != nil {
255-
return nil, err
256-
}
257-
ap.authConfigCache[host] = &ac
275+
entry, exists := ap.authConfigCache[host]
276+
if exists && !ap.expireAc(entry.Created, host) {
277+
return entry.Auth, nil
258278
}
259279

260-
return ap.authConfigCache[host], nil
280+
span, _ := tracing.StartSpan(ctx, fmt.Sprintf("load credentials for %s", host))
281+
ac, err := ap.config.GetAuthConfig(host)
282+
tracing.FinishWithError(span, err)
283+
if err != nil {
284+
return nil, err
285+
}
286+
entry = authConfigCacheEntry{
287+
Created: time.Now(),
288+
Auth: &ac,
289+
}
290+
291+
ap.authConfigCache[host] = entry
292+
293+
return entry.Auth, nil
261294
}
262295

263296
func (ap *authProvider) getAuthorityKey(ctx context.Context, host string, salt []byte) (ed25519.PrivateKey, error) {

session/auth/authprovider/authprovider_test.go

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package authprovider
33
import (
44
"context"
55
"testing"
6+
"time"
67

78
"github.com/docker/cli/cli/config/configfile"
89
"github.com/docker/cli/cli/config/types"
@@ -12,12 +13,18 @@ import (
1213
)
1314

1415
func TestFetchTokenCaching(t *testing.T) {
15-
cfg := &configfile.ConfigFile{
16-
AuthConfigs: map[string]types.AuthConfig{
17-
dockerHubConfigfileKey: {Username: "user", RegistryToken: "hunter2"},
18-
},
16+
newCfg := func() *configfile.ConfigFile {
17+
return &configfile.ConfigFile{
18+
AuthConfigs: map[string]types.AuthConfig{
19+
dockerHubConfigfileKey: {Username: "user", RegistryToken: "hunter2"},
20+
},
21+
}
1922
}
20-
p := NewDockerAuthProvider(cfg, nil).(*authProvider)
23+
24+
cfg := newCfg()
25+
p := NewDockerAuthProvider(DockerAuthProviderConfig{
26+
ConfigFile: cfg,
27+
}).(*authProvider)
2128
res, err := p.FetchToken(context.Background(), &auth.FetchTokenRequest{Host: dockerHubRegistryHost})
2229
require.NoError(t, err)
2330
assert.Equal(t, "hunter2", res.Token)
@@ -28,4 +35,26 @@ func TestFetchTokenCaching(t *testing.T) {
2835

2936
// Verify that we cached the result instead of returning hunter3.
3037
assert.Equal(t, "hunter2", res.Token)
38+
39+
// Now again but this time expire the auth.
40+
41+
cfg = newCfg()
42+
p = NewDockerAuthProvider(DockerAuthProviderConfig{
43+
ConfigFile: cfg,
44+
ExpireCachedAuth: func(_ time.Time, host string) bool {
45+
require.Equal(t, dockerHubConfigfileKey, host)
46+
return true
47+
},
48+
}).(*authProvider)
49+
50+
res, err = p.FetchToken(context.Background(), &auth.FetchTokenRequest{Host: dockerHubRegistryHost})
51+
require.NoError(t, err)
52+
assert.Equal(t, "hunter2", res.Token)
53+
54+
cfg.AuthConfigs[dockerHubConfigfileKey] = types.AuthConfig{Username: "user", RegistryToken: "hunter3"}
55+
res, err = p.FetchToken(context.Background(), &auth.FetchTokenRequest{Host: dockerHubRegistryHost})
56+
require.NoError(t, err)
57+
58+
// Verify that we re-fetched the token after it expired.
59+
assert.Equal(t, "hunter3", res.Token)
3160
}

0 commit comments

Comments
 (0)