1 /* 2 Copyright 2014 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package credentialprovider 18 19 import ( 20 "os" 21 "reflect" 22 "sync" 23 "time" 24 25 "k8s.io/klog/v2" 26 ) 27 28 // DockerConfigProvider is the interface that registered extensions implement 29 // to materialize 'dockercfg' credentials. 30 type DockerConfigProvider interface { 31 // Enabled returns true if the config provider is enabled. 32 // Implementations can be blocking - e.g. metadata server unavailable. 33 Enabled() bool 34 // Provide returns docker configuration. 35 // Implementations can be blocking - e.g. metadata server unavailable. 36 // The image is passed in as context in the event that the 37 // implementation depends on information in the image name to return 38 // credentials; implementations are safe to ignore the image. 39 Provide(image string) DockerConfig 40 } 41 42 // A DockerConfigProvider that simply reads the .dockercfg file 43 type defaultDockerConfigProvider struct{} 44 45 // init registers our default provider, which simply reads the .dockercfg file. 46 func init() { 47 RegisterCredentialProvider(".dockercfg", 48 &CachingDockerConfigProvider{ 49 Provider: &defaultDockerConfigProvider{}, 50 Lifetime: 5 * time.Minute, 51 }) 52 } 53 54 // CachingDockerConfigProvider implements DockerConfigProvider by composing 55 // with another DockerConfigProvider and caching the DockerConfig it provides 56 // for a pre-specified lifetime. 57 type CachingDockerConfigProvider struct { 58 Provider DockerConfigProvider 59 Lifetime time.Duration 60 61 // ShouldCache is an optional function that returns true if the specific config should be cached. 62 // If nil, all configs are treated as cacheable. 63 ShouldCache func(DockerConfig) bool 64 65 // cache fields 66 cacheDockerConfig DockerConfig 67 expiration time.Time 68 mu sync.Mutex 69 } 70 71 // Enabled implements dockerConfigProvider 72 func (d *defaultDockerConfigProvider) Enabled() bool { 73 return true 74 } 75 76 // Provide implements dockerConfigProvider 77 func (d *defaultDockerConfigProvider) Provide(image string) DockerConfig { 78 // Read the standard Docker credentials from .dockercfg 79 if cfg, err := ReadDockerConfigFile(); err == nil { 80 return cfg 81 } else if !os.IsNotExist(err) { 82 klog.V(2).Infof("Docker config file not found: %v", err) 83 } 84 return DockerConfig{} 85 } 86 87 // Enabled implements dockerConfigProvider 88 func (d *CachingDockerConfigProvider) Enabled() bool { 89 return d.Provider.Enabled() 90 } 91 92 // Provide implements dockerConfigProvider 93 func (d *CachingDockerConfigProvider) Provide(image string) DockerConfig { 94 d.mu.Lock() 95 defer d.mu.Unlock() 96 97 // If the cache hasn't expired, return our cache 98 if time.Now().Before(d.expiration) { 99 return d.cacheDockerConfig 100 } 101 102 klog.V(2).Infof("Refreshing cache for provider: %v", reflect.TypeOf(d.Provider).String()) 103 config := d.Provider.Provide(image) 104 if d.ShouldCache == nil || d.ShouldCache(config) { 105 d.cacheDockerConfig = config 106 d.expiration = time.Now().Add(d.Lifetime) 107 } 108 return config 109 } 110