package imagemetaresolver import ( "context" "net/http" "sync" "github.com/containerd/containerd/platforms" "github.com/containerd/containerd/remotes" "github.com/containerd/containerd/remotes/docker" "github.com/docker/docker/pkg/locker" "github.com/moby/buildkit/client/llb" gw "github.com/moby/buildkit/frontend/gateway/client" "github.com/moby/buildkit/util/contentutil" "github.com/moby/buildkit/util/imageutil" digest "github.com/opencontainers/go-digest" specs "github.com/opencontainers/image-spec/specs-go/v1" ) var defaultImageMetaResolver llb.ImageMetaResolver var defaultImageMetaResolverOnce sync.Once var WithDefault = imageOptionFunc(func(ii *llb.ImageInfo) { llb.WithMetaResolver(Default()).SetImageOption(ii) }) type imageMetaResolverOpts struct { platform *specs.Platform } type ImageMetaResolverOpt func(o *imageMetaResolverOpts) func WithDefaultPlatform(p *specs.Platform) ImageMetaResolverOpt { return func(o *imageMetaResolverOpts) { o.platform = p } } func New(with ...ImageMetaResolverOpt) llb.ImageMetaResolver { var opts imageMetaResolverOpts for _, f := range with { f(&opts) } return &imageMetaResolver{ resolver: docker.NewResolver(docker.ResolverOptions{ Client: http.DefaultClient, }), platform: opts.platform, buffer: contentutil.NewBuffer(), cache: map[string]resolveResult{}, locker: locker.New(), } } func Default() llb.ImageMetaResolver { defaultImageMetaResolverOnce.Do(func() { defaultImageMetaResolver = New() }) return defaultImageMetaResolver } type imageMetaResolver struct { resolver remotes.Resolver buffer contentutil.Buffer platform *specs.Platform locker *locker.Locker cache map[string]resolveResult } type resolveResult struct { config []byte dgst digest.Digest } func (imr *imageMetaResolver) ResolveImageConfig(ctx context.Context, ref string, opt gw.ResolveImageConfigOpt) (digest.Digest, []byte, error) { imr.locker.Lock(ref) defer imr.locker.Unlock(ref) platform := opt.Platform if platform == nil { platform = imr.platform } k := imr.key(ref, platform) if res, ok := imr.cache[k]; ok { return res.dgst, res.config, nil } dgst, config, err := imageutil.Config(ctx, ref, imr.resolver, imr.buffer, platform) if err != nil { return "", nil, err } imr.cache[k] = resolveResult{dgst: dgst, config: config} return dgst, config, nil } func (imr *imageMetaResolver) key(ref string, platform *specs.Platform) string { if platform != nil { ref += platforms.Format(*platform) } return ref } type imageOptionFunc func(*llb.ImageInfo) func (fn imageOptionFunc) SetImageOption(ii *llb.ImageInfo) { fn(ii) }