builder: add workaround for gcr auth issue
| ... | ... |
@@ -8,6 +8,7 @@ import ( |
| 8 | 8 |
"io/ioutil" |
| 9 | 9 |
"runtime" |
| 10 | 10 |
"sync" |
| 11 |
+ "sync/atomic" |
|
| 11 | 12 |
"time" |
| 12 | 13 |
|
| 13 | 14 |
"github.com/containerd/containerd/content" |
| ... | ... |
@@ -56,13 +57,15 @@ type SourceOpt struct {
|
| 56 | 56 |
|
| 57 | 57 |
type imageSource struct {
|
| 58 | 58 |
SourceOpt |
| 59 |
- g flightcontrol.Group |
|
| 59 |
+ g flightcontrol.Group |
|
| 60 |
+ resolverCache *resolverCache |
|
| 60 | 61 |
} |
| 61 | 62 |
|
| 62 | 63 |
// NewSource creates a new image source |
| 63 | 64 |
func NewSource(opt SourceOpt) (source.Source, error) {
|
| 64 | 65 |
is := &imageSource{
|
| 65 |
- SourceOpt: opt, |
|
| 66 |
+ SourceOpt: opt, |
|
| 67 |
+ resolverCache: newResolverCache(), |
|
| 66 | 68 |
} |
| 67 | 69 |
|
| 68 | 70 |
return is, nil |
| ... | ... |
@@ -73,6 +76,10 @@ func (is *imageSource) ID() string {
|
| 73 | 73 |
} |
| 74 | 74 |
|
| 75 | 75 |
func (is *imageSource) getResolver(ctx context.Context, rfn resolver.ResolveOptionsFunc, ref string, sm *session.Manager) remotes.Resolver {
|
| 76 |
+ if res := is.resolverCache.Get(ctx, ref); res != nil {
|
|
| 77 |
+ return res |
|
| 78 |
+ } |
|
| 79 |
+ |
|
| 76 | 80 |
opt := docker.ResolverOptions{
|
| 77 | 81 |
Client: tracing.DefaultClient, |
| 78 | 82 |
} |
| ... | ... |
@@ -81,6 +88,7 @@ func (is *imageSource) getResolver(ctx context.Context, rfn resolver.ResolveOpti |
| 81 | 81 |
} |
| 82 | 82 |
opt.Credentials = is.getCredentialsFromSession(ctx, sm) |
| 83 | 83 |
r := docker.NewResolver(opt) |
| 84 |
+ r = is.resolverCache.Add(ctx, ref, r) |
|
| 84 | 85 |
return r |
| 85 | 86 |
} |
| 86 | 87 |
|
| ... | ... |
@@ -395,6 +403,11 @@ func (p *puller) Snapshot(ctx context.Context) (cache.ImmutableRef, error) {
|
| 395 | 395 |
} |
| 396 | 396 |
|
| 397 | 397 |
platform := platforms.Only(p.platform) |
| 398 |
+ // workaround for GCR bug that requires a request to manifest endpoint for authentication to work. |
|
| 399 |
+ // if current resolver has not used manifests do a dummy request. |
|
| 400 |
+ // in most cases resolver should be cached and extra request is not needed. |
|
| 401 |
+ ensureManifestRequested(ctx, p.resolver, p.ref) |
|
| 402 |
+ |
|
| 398 | 403 |
var ( |
| 399 | 404 |
schema1Converter *schema1.Converter |
| 400 | 405 |
handlers []images.Handler |
| ... | ... |
@@ -808,3 +821,90 @@ func resolveModeToString(rm source.ResolveMode) string {
|
| 808 | 808 |
} |
| 809 | 809 |
return "" |
| 810 | 810 |
} |
| 811 |
+ |
|
| 812 |
+type resolverCache struct {
|
|
| 813 |
+ mu sync.Mutex |
|
| 814 |
+ m map[string]cachedResolver |
|
| 815 |
+} |
|
| 816 |
+ |
|
| 817 |
+type cachedResolver struct {
|
|
| 818 |
+ timeout time.Time |
|
| 819 |
+ remotes.Resolver |
|
| 820 |
+ counter int64 |
|
| 821 |
+} |
|
| 822 |
+ |
|
| 823 |
+func (cr *cachedResolver) Resolve(ctx context.Context, ref string) (name string, desc ocispec.Descriptor, err error) {
|
|
| 824 |
+ atomic.AddInt64(&cr.counter, 1) |
|
| 825 |
+ return cr.Resolver.Resolve(ctx, ref) |
|
| 826 |
+} |
|
| 827 |
+ |
|
| 828 |
+func (r *resolverCache) Add(ctx context.Context, ref string, resolver remotes.Resolver) remotes.Resolver {
|
|
| 829 |
+ r.mu.Lock() |
|
| 830 |
+ defer r.mu.Unlock() |
|
| 831 |
+ |
|
| 832 |
+ ref = r.domain(ref) + "-" + session.FromContext(ctx) |
|
| 833 |
+ |
|
| 834 |
+ cr, ok := r.m[ref] |
|
| 835 |
+ cr.timeout = time.Now().Add(time.Minute) |
|
| 836 |
+ if ok {
|
|
| 837 |
+ return &cr |
|
| 838 |
+ } |
|
| 839 |
+ |
|
| 840 |
+ cr.Resolver = resolver |
|
| 841 |
+ r.m[ref] = cr |
|
| 842 |
+ return &cr |
|
| 843 |
+} |
|
| 844 |
+ |
|
| 845 |
+func (r *resolverCache) domain(refStr string) string {
|
|
| 846 |
+ ref, err := distreference.ParseNormalizedNamed(refStr) |
|
| 847 |
+ if err != nil {
|
|
| 848 |
+ return refStr |
|
| 849 |
+ } |
|
| 850 |
+ return distreference.Domain(ref) |
|
| 851 |
+} |
|
| 852 |
+ |
|
| 853 |
+func (r *resolverCache) Get(ctx context.Context, ref string) remotes.Resolver {
|
|
| 854 |
+ r.mu.Lock() |
|
| 855 |
+ defer r.mu.Unlock() |
|
| 856 |
+ |
|
| 857 |
+ ref = r.domain(ref) + "-" + session.FromContext(ctx) |
|
| 858 |
+ |
|
| 859 |
+ cr, ok := r.m[ref] |
|
| 860 |
+ if !ok {
|
|
| 861 |
+ return nil |
|
| 862 |
+ } |
|
| 863 |
+ return &cr |
|
| 864 |
+} |
|
| 865 |
+ |
|
| 866 |
+func (r *resolverCache) clean(now time.Time) {
|
|
| 867 |
+ r.mu.Lock() |
|
| 868 |
+ for k, cr := range r.m {
|
|
| 869 |
+ if now.After(cr.timeout) {
|
|
| 870 |
+ delete(r.m, k) |
|
| 871 |
+ } |
|
| 872 |
+ } |
|
| 873 |
+ r.mu.Unlock() |
|
| 874 |
+} |
|
| 875 |
+ |
|
| 876 |
+func newResolverCache() *resolverCache {
|
|
| 877 |
+ rc := &resolverCache{
|
|
| 878 |
+ m: map[string]cachedResolver{},
|
|
| 879 |
+ } |
|
| 880 |
+ t := time.NewTicker(time.Minute) |
|
| 881 |
+ go func() {
|
|
| 882 |
+ for {
|
|
| 883 |
+ rc.clean(<-t.C) |
|
| 884 |
+ } |
|
| 885 |
+ }() |
|
| 886 |
+ return rc |
|
| 887 |
+} |
|
| 888 |
+ |
|
| 889 |
+func ensureManifestRequested(ctx context.Context, res remotes.Resolver, ref string) {
|
|
| 890 |
+ cr, ok := res.(*cachedResolver) |
|
| 891 |
+ if !ok {
|
|
| 892 |
+ return |
|
| 893 |
+ } |
|
| 894 |
+ if atomic.LoadInt64(&cr.counter) == 0 {
|
|
| 895 |
+ res.Resolve(ctx, ref) |
|
| 896 |
+ } |
|
| 897 |
+} |