Merge pull request #113 from tonistiigi/share-resolver
imagesource: share config resolver with frontenddocker-18.09
commit
2f3ecb50dd
|
@ -3,9 +3,6 @@ package llb
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -13,9 +10,9 @@ import (
|
||||||
"github.com/BurntSushi/locker"
|
"github.com/BurntSushi/locker"
|
||||||
"github.com/containerd/containerd/content"
|
"github.com/containerd/containerd/content"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/containerd/containerd/images"
|
|
||||||
"github.com/containerd/containerd/remotes"
|
"github.com/containerd/containerd/remotes"
|
||||||
"github.com/containerd/containerd/remotes/docker"
|
"github.com/containerd/containerd/remotes/docker"
|
||||||
|
"github.com/moby/buildkit/util/imageutil"
|
||||||
digest "github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -35,7 +32,7 @@ func WithDefaultMetaResolver(ii *ImageInfo) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImageMetaResolver interface {
|
type ImageMetaResolver interface {
|
||||||
Resolve(ctx context.Context, ref string) (*ocispec.Image, error)
|
ResolveImageConfig(ctx context.Context, ref string) (*ocispec.Image, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewImageMetaResolver() ImageMetaResolver {
|
func NewImageMetaResolver() ImageMetaResolver {
|
||||||
|
@ -63,7 +60,7 @@ type imageMetaResolver struct {
|
||||||
cache map[string]*ocispec.Image
|
cache map[string]*ocispec.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
func (imr *imageMetaResolver) Resolve(ctx context.Context, ref string) (*ocispec.Image, error) {
|
func (imr *imageMetaResolver) ResolveImageConfig(ctx context.Context, ref string) (*ocispec.Image, error) {
|
||||||
imr.locker.Lock(ref)
|
imr.locker.Lock(ref)
|
||||||
defer imr.locker.Unlock(ref)
|
defer imr.locker.Unlock(ref)
|
||||||
|
|
||||||
|
@ -71,87 +68,13 @@ func (imr *imageMetaResolver) Resolve(ctx context.Context, ref string) (*ocispec
|
||||||
return img, nil
|
return img, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ref, desc, err := imr.resolver.Resolve(ctx, ref)
|
img, err := imageutil.Config(ctx, ref, imr.resolver, imr.ingester)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
fetcher, err := imr.resolver.Fetcher(ctx, ref)
|
imr.cache[ref] = img
|
||||||
if err != nil {
|
return img, nil
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
handlers := []images.Handler{
|
|
||||||
remotes.FetchHandler(imr.ingester, fetcher),
|
|
||||||
childrenConfigHandler(imr.ingester),
|
|
||||||
}
|
|
||||||
if err := images.Dispatch(ctx, images.Handlers(handlers...), desc); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
config, err := images.Config(ctx, imr.ingester, desc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var ociimage ocispec.Image
|
|
||||||
|
|
||||||
r, err := imr.ingester.Reader(ctx, config.Digest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer r.Close()
|
|
||||||
dec := json.NewDecoder(r)
|
|
||||||
if err := dec.Decode(&ociimage); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if dec.More() {
|
|
||||||
return nil, errors.New("invalid image config")
|
|
||||||
}
|
|
||||||
|
|
||||||
imr.cache[ref] = &ociimage
|
|
||||||
|
|
||||||
return &ociimage, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func childrenConfigHandler(provider content.Provider) images.HandlerFunc {
|
|
||||||
return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
|
|
||||||
var descs []ocispec.Descriptor
|
|
||||||
switch desc.MediaType {
|
|
||||||
case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
|
|
||||||
p, err := content.ReadBlob(ctx, provider, desc.Digest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(stevvooe): We just assume oci manifest, for now. There may be
|
|
||||||
// subtle differences from the docker version.
|
|
||||||
var manifest ocispec.Manifest
|
|
||||||
if err := json.Unmarshal(p, &manifest); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
descs = append(descs, manifest.Config)
|
|
||||||
case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
|
|
||||||
p, err := content.ReadBlob(ctx, provider, desc.Digest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var index ocispec.Index
|
|
||||||
if err := json.Unmarshal(p, &index); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
descs = append(descs, index.Manifests...)
|
|
||||||
case images.MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig:
|
|
||||||
// childless data types.
|
|
||||||
return nil, nil
|
|
||||||
default:
|
|
||||||
return nil, errors.Errorf("encountered unknown type %v; children may not be fetched", desc.MediaType)
|
|
||||||
}
|
|
||||||
|
|
||||||
return descs, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type inMemoryIngester struct {
|
type inMemoryIngester struct {
|
||||||
|
@ -184,20 +107,6 @@ func (i *inMemoryIngester) Writer(ctx context.Context, ref string, size int64, e
|
||||||
return w, nil
|
return w, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *inMemoryIngester) Reader(ctx context.Context, dgst digest.Digest) (io.ReadCloser, error) {
|
|
||||||
rdr, err := i.reader(ctx, dgst)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ioutil.NopCloser(rdr), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReaderAt interface {
|
|
||||||
io.ReaderAt
|
|
||||||
io.Closer
|
|
||||||
Size() int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *inMemoryIngester) ReaderAt(ctx context.Context, dgst digest.Digest) (content.ReaderAt, error) {
|
func (i *inMemoryIngester) ReaderAt(ctx context.Context, dgst digest.Digest) (content.ReaderAt, error) {
|
||||||
r, err := i.reader(ctx, dgst)
|
r, err := i.reader(ctx, dgst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -85,7 +85,7 @@ func Image(ref string, opts ...ImageOption) State {
|
||||||
opt(&info)
|
opt(&info)
|
||||||
}
|
}
|
||||||
if info.metaResolver != nil {
|
if info.metaResolver != nil {
|
||||||
img, err := info.metaResolver.Resolve(context.TODO(), ref)
|
img, err := info.metaResolver.ResolveImageConfig(context.TODO(), ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
src.err = err
|
src.err = err
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -28,6 +28,7 @@ type Opt struct {
|
||||||
Exporters map[string]exporter.Exporter
|
Exporters map[string]exporter.Exporter
|
||||||
SessionManager *session.Manager
|
SessionManager *session.Manager
|
||||||
Frontends map[string]frontend.Frontend
|
Frontends map[string]frontend.Frontend
|
||||||
|
ImageSource source.Source
|
||||||
}
|
}
|
||||||
|
|
||||||
type Controller struct { // TODO: ControlService
|
type Controller struct { // TODO: ControlService
|
||||||
|
@ -43,6 +44,7 @@ func NewController(opt Opt) (*Controller, error) {
|
||||||
CacheManager: opt.CacheManager,
|
CacheManager: opt.CacheManager,
|
||||||
Worker: opt.Worker,
|
Worker: opt.Worker,
|
||||||
InstructionCache: opt.InstructionCache,
|
InstructionCache: opt.InstructionCache,
|
||||||
|
ImageSource: opt.ImageSource,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
|
|
|
@ -137,5 +137,6 @@ func defaultControllerOpts(root string, pd pullDeps) (*Opt, error) {
|
||||||
Exporters: exporters,
|
Exporters: exporters,
|
||||||
SessionManager: sessm,
|
SessionManager: sessm,
|
||||||
Frontends: frontends,
|
Frontends: frontends,
|
||||||
|
ImageSource: is,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
|
||||||
}
|
}
|
||||||
d.stage.BaseName = reference.TagNameOnly(ref).String()
|
d.stage.BaseName = reference.TagNameOnly(ref).String()
|
||||||
if opt.MetaResolver != nil {
|
if opt.MetaResolver != nil {
|
||||||
img, err := metaResolver.Resolve(ctx, d.stage.BaseName)
|
img, err := metaResolver.ResolveImageConfig(ctx, d.stage.BaseName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package frontend
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/moby/buildkit/cache"
|
"github.com/moby/buildkit/cache"
|
||||||
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -12,5 +13,5 @@ type Frontend interface {
|
||||||
|
|
||||||
type FrontendLLBBridge interface {
|
type FrontendLLBBridge interface {
|
||||||
Solve(ctx context.Context, vtx [][]byte) (cache.ImmutableRef, error)
|
Solve(ctx context.Context, vtx [][]byte) (cache.ImmutableRef, error)
|
||||||
// ImageConfigResolver
|
ResolveImageConfig(ctx context.Context, ref string) (*ocispec.Image, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/moby/buildkit/util/progress"
|
"github.com/moby/buildkit/util/progress"
|
||||||
"github.com/moby/buildkit/worker"
|
"github.com/moby/buildkit/worker"
|
||||||
digest "github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -25,6 +26,7 @@ type LLBOpt struct {
|
||||||
CacheManager cache.Manager // TODO: this shouldn't be needed before instruction cache
|
CacheManager cache.Manager // TODO: this shouldn't be needed before instruction cache
|
||||||
Worker worker.Worker
|
Worker worker.Worker
|
||||||
InstructionCache InstructionCache
|
InstructionCache InstructionCache
|
||||||
|
ImageSource source.Source
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLLBSolver(opt LLBOpt) *Solver {
|
func NewLLBSolver(opt LLBOpt) *Solver {
|
||||||
|
@ -40,7 +42,7 @@ func NewLLBSolver(opt LLBOpt) *Solver {
|
||||||
default:
|
default:
|
||||||
return nil, errors.Errorf("invalid op type %T", op)
|
return nil, errors.Errorf("invalid op type %T", op)
|
||||||
}
|
}
|
||||||
}, opt.InstructionCache)
|
}, opt.InstructionCache, opt.ImageSource)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,13 +73,19 @@ type Solver struct {
|
||||||
jobs *jobList
|
jobs *jobList
|
||||||
activeState activeState
|
activeState activeState
|
||||||
cache InstructionCache
|
cache InstructionCache
|
||||||
|
imageSource source.Source
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(resolve ResolveOpFunc, cache InstructionCache) *Solver {
|
func New(resolve ResolveOpFunc, cache InstructionCache, imageSource source.Source) *Solver {
|
||||||
return &Solver{resolve: resolve, jobs: newJobList(), cache: cache}
|
return &Solver{resolve: resolve, jobs: newJobList(), cache: cache, imageSource: imageSource}
|
||||||
|
}
|
||||||
|
|
||||||
|
type resolveImageConfig interface {
|
||||||
|
ResolveImageConfig(ctx context.Context, ref string) (*ocispec.Image, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type llbBridge struct {
|
type llbBridge struct {
|
||||||
|
resolveImageConfig
|
||||||
solver func(ctx context.Context, v *vertex, i Index) (Reference, error)
|
solver func(ctx context.Context, v *vertex, i Index) (Reference, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,6 +156,7 @@ func (s *Solver) Solve(ctx context.Context, id string, f frontend.Frontend, v Ve
|
||||||
} else {
|
} else {
|
||||||
ref, err = f.Solve(ctx, &llbBridge{
|
ref, err = f.Solve(ctx, &llbBridge{
|
||||||
solver: s.getRef,
|
solver: s.getRef,
|
||||||
|
resolveImageConfig: s.imageSource.(resolveImageConfig),
|
||||||
}, frontendOpt)
|
}, frontendOpt)
|
||||||
}
|
}
|
||||||
s.activeState.cancel(j)
|
s.activeState.cancel(j)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/locker"
|
||||||
"github.com/containerd/containerd/content"
|
"github.com/containerd/containerd/content"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/containerd/containerd/images"
|
"github.com/containerd/containerd/images"
|
||||||
|
@ -17,6 +18,7 @@ import (
|
||||||
"github.com/containerd/containerd/snapshot"
|
"github.com/containerd/containerd/snapshot"
|
||||||
"github.com/moby/buildkit/cache"
|
"github.com/moby/buildkit/cache"
|
||||||
"github.com/moby/buildkit/source"
|
"github.com/moby/buildkit/source"
|
||||||
|
"github.com/moby/buildkit/util/imageutil"
|
||||||
"github.com/moby/buildkit/util/progress"
|
"github.com/moby/buildkit/util/progress"
|
||||||
digest "github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
"github.com/opencontainers/image-spec/identity"
|
"github.com/opencontainers/image-spec/identity"
|
||||||
|
@ -40,17 +42,24 @@ type blobmapper interface {
|
||||||
SetBlob(ctx gocontext.Context, key string, blob digest.Digest) error
|
SetBlob(ctx gocontext.Context, key string, blob digest.Digest) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type resolveRecord struct {
|
||||||
|
desc ocispec.Descriptor
|
||||||
|
ts time.Time
|
||||||
|
}
|
||||||
|
|
||||||
type imageSource struct {
|
type imageSource struct {
|
||||||
SourceOpt
|
SourceOpt
|
||||||
resolver remotes.Resolver
|
resolver remotes.Resolver
|
||||||
|
lru map[string]resolveRecord
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSource(opt SourceOpt) (source.Source, error) {
|
func NewSource(opt SourceOpt) (source.Source, error) {
|
||||||
is := &imageSource{
|
is := &imageSource{
|
||||||
SourceOpt: opt,
|
SourceOpt: opt,
|
||||||
resolver: docker.NewResolver(docker.ResolverOptions{
|
lru: map[string]resolveRecord{},
|
||||||
|
resolver: newCachedResolver(docker.NewResolver(docker.ResolverOptions{
|
||||||
Client: http.DefaultClient,
|
Client: http.DefaultClient,
|
||||||
}),
|
}), 5*time.Second),
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := opt.Snapshotter.(blobmapper); !ok {
|
if _, ok := opt.Snapshotter.(blobmapper); !ok {
|
||||||
|
@ -64,13 +73,8 @@ func (is *imageSource) ID() string {
|
||||||
return source.DockerImageScheme
|
return source.DockerImageScheme
|
||||||
}
|
}
|
||||||
|
|
||||||
type puller struct {
|
func (is *imageSource) ResolveImageConfig(ctx context.Context, ref string) (*ocispec.Image, error) {
|
||||||
is *imageSource
|
return imageutil.Config(ctx, ref, is.resolver, is.ContentStore)
|
||||||
resolveOnce sync.Once
|
|
||||||
src *source.ImageIdentifier
|
|
||||||
desc ocispec.Descriptor
|
|
||||||
ref string
|
|
||||||
resolveErr error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (is *imageSource) Resolve(ctx context.Context, id source.Identifier) (source.SourceInstance, error) {
|
func (is *imageSource) Resolve(ctx context.Context, id source.Identifier) (source.SourceInstance, error) {
|
||||||
|
@ -86,9 +90,34 @@ func (is *imageSource) Resolve(ctx context.Context, id source.Identifier) (sourc
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type puller struct {
|
||||||
|
is *imageSource
|
||||||
|
resolveOnce sync.Once
|
||||||
|
src *source.ImageIdentifier
|
||||||
|
desc ocispec.Descriptor
|
||||||
|
ref string
|
||||||
|
resolveErr error
|
||||||
|
}
|
||||||
|
|
||||||
func (p *puller) resolve(ctx context.Context) error {
|
func (p *puller) resolve(ctx context.Context) error {
|
||||||
p.resolveOnce.Do(func() {
|
p.resolveOnce.Do(func() {
|
||||||
resolveProgressDone := oneOffProgress(ctx, "resolve "+p.src.Reference.String())
|
resolveProgressDone := oneOffProgress(ctx, "resolve "+p.src.Reference.String())
|
||||||
|
|
||||||
|
dgst := p.src.Reference.Digest()
|
||||||
|
if dgst != "" {
|
||||||
|
info, err := p.is.ContentStore.Info(ctx, dgst)
|
||||||
|
if err == nil {
|
||||||
|
p.ref = p.src.Reference.String()
|
||||||
|
p.desc = ocispec.Descriptor{
|
||||||
|
Size: info.Size,
|
||||||
|
Digest: dgst,
|
||||||
|
MediaType: ocispec.MediaTypeImageManifest, // TODO: detect schema1/manifest-list
|
||||||
|
}
|
||||||
|
resolveProgressDone(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ref, desc, err := p.is.resolver.Resolve(ctx, p.src.Reference.String())
|
ref, desc, err := p.is.resolver.Resolve(ctx, p.src.Reference.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.resolveErr = err
|
p.resolveErr = err
|
||||||
|
@ -383,3 +412,45 @@ func oneOffProgress(ctx context.Context, id string) func(err error) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newCachedResolver(r remotes.Resolver, timeout time.Duration) remotes.Resolver {
|
||||||
|
return &cachedResolver{
|
||||||
|
Resolver: r,
|
||||||
|
cache: map[string]cachedResult{},
|
||||||
|
timeout: timeout,
|
||||||
|
locker: locker.NewLocker(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type cachedResolver struct {
|
||||||
|
remotes.Resolver
|
||||||
|
cache map[string]cachedResult
|
||||||
|
timeout time.Duration
|
||||||
|
locker *locker.Locker
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *cachedResolver) Resolve(ctx gocontext.Context, ref string) (name string, desc ocispec.Descriptor, err error) {
|
||||||
|
cr.locker.Lock(ref)
|
||||||
|
defer cr.locker.Unlock(ref)
|
||||||
|
r, ok := cr.cache[ref]
|
||||||
|
if ok && time.Since(r.ts) < cr.timeout {
|
||||||
|
return r.name, r.desc, nil
|
||||||
|
}
|
||||||
|
delete(cr.cache, ref)
|
||||||
|
n, d, err := cr.Resolver.Resolve(ctx, ref)
|
||||||
|
if err != nil {
|
||||||
|
return "", d, err
|
||||||
|
}
|
||||||
|
cr.cache[ref] = cachedResult{
|
||||||
|
name: n,
|
||||||
|
desc: d,
|
||||||
|
ts: time.Now(),
|
||||||
|
}
|
||||||
|
return n, d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type cachedResult struct {
|
||||||
|
name string
|
||||||
|
desc ocispec.Descriptor
|
||||||
|
ts time.Time
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
package imageutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/content"
|
||||||
|
"github.com/containerd/containerd/images"
|
||||||
|
"github.com/containerd/containerd/remotes"
|
||||||
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IngesterProvider interface {
|
||||||
|
content.Ingester
|
||||||
|
content.Provider
|
||||||
|
}
|
||||||
|
|
||||||
|
func Config(ctx context.Context, ref string, resolver remotes.Resolver, ingester IngesterProvider) (*ocispec.Image, error) {
|
||||||
|
ref, desc, err := resolver.Resolve(ctx, ref)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fetcher, err := resolver.Fetcher(ctx, ref)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
handlers := []images.Handler{
|
||||||
|
remotes.FetchHandler(ingester, fetcher),
|
||||||
|
childrenConfigHandler(ingester),
|
||||||
|
}
|
||||||
|
if err := images.Dispatch(ctx, images.Handlers(handlers...), desc); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
config, err := images.Config(ctx, ingester, desc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ociimage ocispec.Image
|
||||||
|
|
||||||
|
r, err := ingester.ReaderAt(ctx, config.Digest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
dec := json.NewDecoder(readerAtToReader(r))
|
||||||
|
if err := dec.Decode(&ociimage); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if dec.More() {
|
||||||
|
return nil, errors.New("invalid image config")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ociimage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func childrenConfigHandler(provider content.Provider) images.HandlerFunc {
|
||||||
|
return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
|
||||||
|
var descs []ocispec.Descriptor
|
||||||
|
switch desc.MediaType {
|
||||||
|
case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
|
||||||
|
p, err := content.ReadBlob(ctx, provider, desc.Digest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(stevvooe): We just assume oci manifest, for now. There may be
|
||||||
|
// subtle differences from the docker version.
|
||||||
|
var manifest ocispec.Manifest
|
||||||
|
if err := json.Unmarshal(p, &manifest); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
descs = append(descs, manifest.Config)
|
||||||
|
case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
|
||||||
|
p, err := content.ReadBlob(ctx, provider, desc.Digest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var index ocispec.Index
|
||||||
|
if err := json.Unmarshal(p, &index); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
descs = append(descs, index.Manifests...)
|
||||||
|
case images.MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig:
|
||||||
|
// childless data types.
|
||||||
|
return nil, nil
|
||||||
|
default:
|
||||||
|
return nil, errors.Errorf("encountered unknown type %v; children may not be fetched", desc.MediaType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return descs, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readerAtToReader(r io.ReaderAt) io.Reader {
|
||||||
|
return &reader{ra: r}
|
||||||
|
}
|
||||||
|
|
||||||
|
type reader struct {
|
||||||
|
ra io.ReaderAt
|
||||||
|
offset int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reader) Read(b []byte) (int, error) {
|
||||||
|
n, err := r.ra.ReadAt(b, r.offset)
|
||||||
|
r.offset += int64(n)
|
||||||
|
return n, err
|
||||||
|
}
|
|
@ -33,7 +33,7 @@ github.com/google/shlex 6f45313302b9c56850fc17f99e40caebce98c716
|
||||||
golang.org/x/time 8be79e1e0910c292df4e79c241bb7e8f7e725959
|
golang.org/x/time 8be79e1e0910c292df4e79c241bb7e8f7e725959
|
||||||
|
|
||||||
github.com/BurntSushi/locker 392720b78f44e9d0249fcac6c43b111b47a370b8
|
github.com/BurntSushi/locker 392720b78f44e9d0249fcac6c43b111b47a370b8
|
||||||
github.com/docker/docker 6f723db8c6f0c7f0b252674a9673a25b5978db04 https://github.com/simonferquel/docker.git
|
github.com/docker/docker 6f723db8c6f0c7f0b252674a9673a25b5978db04 https://github.com/tonistiigi/docker.git
|
||||||
github.com/pkg/profile 5b67d428864e92711fcbd2f8629456121a56d91f
|
github.com/pkg/profile 5b67d428864e92711fcbd2f8629456121a56d91f
|
||||||
|
|
||||||
github.com/tonistiigi/fsutil d49833a9a6fa5b41f63e7e338038633d10276b57
|
github.com/tonistiigi/fsutil d49833a9a6fa5b41f63e7e338038633d10276b57
|
||||||
|
|
Loading…
Reference in New Issue