/
resolver.go
126 lines (114 loc) · 2.88 KB
/
resolver.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package llblib
import (
"context"
"sync"
"github.com/coryb/llblib/progress"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/llb"
gateway "github.com/moby/buildkit/frontend/gateway/client"
bksess "github.com/moby/buildkit/session"
"github.com/opencontainers/go-digest"
)
type resolver struct {
cache *resolveImageCache
cln *client.Client
sess *bksess.Session
prog progress.Progress
}
func newResolver(cln *client.Client, cache *resolveImageCache, sess *bksess.Session, p progress.Progress) *resolver {
return &resolver{
cache: cache,
cln: cln,
sess: sess,
prog: p,
}
}
func (r *resolver) ResolveImageConfig(ctx context.Context, ref string, opt llb.ResolveImageConfigOpt) (string, digest.Digest, []byte, error) {
return r.cache.lookup(ctx, ref, opt, func() (string, digest.Digest, []byte, error) {
opts := client.SolveOpt{}
if r.sess != nil {
opts.SharedSession = r.sess
opts.SessionPreInitialized = true
}
var (
d digest.Digest
config []byte
err error
)
_, buildErr := r.cln.Build(ctx, opts, "resolver", func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
ref, d, config, err = c.ResolveImageConfig(ctx, ref, opt)
return nil, nil
}, r.prog.Channel())
if buildErr != nil {
return "", "", nil, buildErr
}
return ref, d, config, err
})
}
type resolveImageCacheKey struct {
resolveType llb.ResolverType
ref string
os string
arch string
variant string
mode string
store llb.ResolveImageConfigOptStore
}
type resolveImageCacheValue struct {
ref string
digest digest.Digest
config []byte
err error
inflight chan struct{}
}
type resolveImageCache struct {
mu sync.Mutex
cache map[resolveImageCacheKey]*resolveImageCacheValue
}
func (r *resolveImageCache) lookup(
ctx context.Context,
ref string,
opt llb.ResolveImageConfigOpt,
resolver func() (string, digest.Digest, []byte, error),
) (string, digest.Digest, []byte, error) {
key := resolveImageCacheKey{
resolveType: opt.ResolverType,
ref: ref,
mode: opt.ResolveMode,
store: opt.Store,
}
if opt.Platform != nil {
key.os = opt.Platform.OS
key.arch = opt.Platform.Architecture
key.variant = opt.Platform.Variant
}
r.mu.Lock()
val, ok := r.cache[key]
if !ok {
val = &resolveImageCacheValue{
inflight: make(chan struct{}),
}
r.cache[key] = val
}
r.mu.Unlock()
if ok {
return val.fetch(ctx)
}
val.store(resolver())
return val.fetch(ctx)
}
func (v *resolveImageCacheValue) fetch(ctx context.Context) (string, digest.Digest, []byte, error) {
select {
case <-ctx.Done():
return "", "", nil, ctx.Err()
case <-v.inflight:
return v.ref, v.digest, v.config, v.err
}
}
func (v *resolveImageCacheValue) store(ref string, d digest.Digest, config []byte, err error) {
v.ref = ref
v.digest = d
v.config = config
v.err = err
close(v.inflight)
}