Browse code

Merge pull request #38246 from tonistiigi/gcr-workaround

builder: add workaround for gcr auth issue

Akihiro Suda authored on 2019/03/22 15:55:55
Showing 1 changed files
... ...
@@ -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
+}