2017-05-27 05:10:26 +00:00
|
|
|
package containerimage
|
|
|
|
|
|
|
|
import (
|
2018-01-16 22:30:10 +00:00
|
|
|
"context"
|
2018-04-24 21:00:58 +00:00
|
|
|
"encoding/json"
|
2017-07-25 19:11:52 +00:00
|
|
|
"fmt"
|
2018-04-24 21:00:58 +00:00
|
|
|
"runtime"
|
2017-05-27 05:10:26 +00:00
|
|
|
|
|
|
|
"github.com/containerd/containerd/content"
|
2017-11-06 09:44:23 +00:00
|
|
|
"github.com/containerd/containerd/diff"
|
2017-05-27 05:10:26 +00:00
|
|
|
"github.com/containerd/containerd/images"
|
2018-04-24 21:00:58 +00:00
|
|
|
"github.com/docker/distribution/reference"
|
2017-06-22 20:15:46 +00:00
|
|
|
"github.com/moby/buildkit/cache"
|
2017-10-15 06:49:55 +00:00
|
|
|
"github.com/moby/buildkit/session"
|
2017-12-28 07:07:13 +00:00
|
|
|
"github.com/moby/buildkit/snapshot"
|
2017-06-22 20:15:46 +00:00
|
|
|
"github.com/moby/buildkit/source"
|
2017-12-12 21:58:12 +00:00
|
|
|
"github.com/moby/buildkit/util/flightcontrol"
|
2017-09-05 18:26:03 +00:00
|
|
|
"github.com/moby/buildkit/util/imageutil"
|
2018-05-17 20:09:11 +00:00
|
|
|
"github.com/moby/buildkit/util/pull"
|
2017-05-31 22:36:04 +00:00
|
|
|
digest "github.com/opencontainers/go-digest"
|
|
|
|
"github.com/opencontainers/image-spec/identity"
|
2017-05-27 05:10:26 +00:00
|
|
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
// TODO: break apart containerd specifics like contentstore so the resolver
|
|
|
|
// code can be used with any implementation
|
|
|
|
|
2017-05-27 06:12:13 +00:00
|
|
|
type SourceOpt struct {
|
2017-10-15 06:49:55 +00:00
|
|
|
SessionManager *session.Manager
|
2017-12-28 07:07:13 +00:00
|
|
|
Snapshotter snapshot.Snapshotter
|
2017-10-15 06:49:55 +00:00
|
|
|
ContentStore content.Store
|
2018-02-09 19:39:48 +00:00
|
|
|
Applier diff.Applier
|
2017-10-15 06:49:55 +00:00
|
|
|
CacheAccessor cache.Accessor
|
2018-02-23 14:53:11 +00:00
|
|
|
ImageStore images.Store // optional
|
2017-05-27 05:10:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type imageSource struct {
|
2017-05-27 06:12:13 +00:00
|
|
|
SourceOpt
|
2017-12-12 21:58:12 +00:00
|
|
|
g flightcontrol.Group
|
2017-05-27 05:10:26 +00:00
|
|
|
}
|
|
|
|
|
2017-05-27 06:12:13 +00:00
|
|
|
func NewSource(opt SourceOpt) (source.Source, error) {
|
2017-05-27 05:10:26 +00:00
|
|
|
is := &imageSource{
|
2017-05-27 06:12:13 +00:00
|
|
|
SourceOpt: opt,
|
2017-05-27 05:10:26 +00:00
|
|
|
}
|
2017-05-31 22:36:04 +00:00
|
|
|
|
2017-05-27 05:10:26 +00:00
|
|
|
return is, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (is *imageSource) ID() string {
|
2017-05-27 06:12:13 +00:00
|
|
|
return source.DockerImageScheme
|
2017-05-27 05:10:26 +00:00
|
|
|
}
|
|
|
|
|
2017-09-22 22:49:37 +00:00
|
|
|
func (is *imageSource) ResolveImageConfig(ctx context.Context, ref string) (digest.Digest, []byte, error) {
|
2017-12-12 21:58:12 +00:00
|
|
|
type t struct {
|
|
|
|
dgst digest.Digest
|
|
|
|
dt []byte
|
|
|
|
}
|
|
|
|
res, err := is.g.Do(ctx, ref, func(ctx context.Context) (interface{}, error) {
|
2018-05-22 16:18:36 +00:00
|
|
|
dgst, dt, err := imageutil.Config(ctx, ref, pull.NewResolver(ctx, is.SessionManager, is.ImageStore), is.ContentStore, "")
|
2017-12-12 21:58:12 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &t{dgst: dgst, dt: dt}, nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return "", nil, err
|
|
|
|
}
|
|
|
|
typed := res.(*t)
|
|
|
|
return typed.dgst, typed.dt, nil
|
2017-07-06 19:07:42 +00:00
|
|
|
}
|
2017-05-27 05:10:26 +00:00
|
|
|
|
2017-07-06 19:07:42 +00:00
|
|
|
func (is *imageSource) Resolve(ctx context.Context, id source.Identifier) (source.SourceInstance, error) {
|
2017-05-27 06:12:13 +00:00
|
|
|
imageIdentifier, ok := id.(*source.ImageIdentifier)
|
2017-05-27 05:10:26 +00:00
|
|
|
if !ok {
|
2017-07-07 21:35:10 +00:00
|
|
|
return nil, errors.Errorf("invalid image identifier %v", id)
|
2017-05-27 05:10:26 +00:00
|
|
|
}
|
|
|
|
|
2018-05-17 20:09:11 +00:00
|
|
|
pullerUtil := &pull.Puller{
|
|
|
|
Snapshotter: is.Snapshotter,
|
|
|
|
ContentStore: is.ContentStore,
|
|
|
|
Applier: is.Applier,
|
|
|
|
Src: imageIdentifier.Reference,
|
|
|
|
Resolver: pull.NewResolver(ctx, is.SessionManager, is.ImageStore),
|
|
|
|
}
|
2017-07-06 19:07:42 +00:00
|
|
|
p := &puller{
|
2018-05-17 20:09:11 +00:00
|
|
|
CacheAccessor: is.CacheAccessor,
|
|
|
|
Puller: pullerUtil,
|
2017-07-06 19:07:42 +00:00
|
|
|
}
|
|
|
|
return p, nil
|
|
|
|
}
|
|
|
|
|
2017-09-05 18:26:03 +00:00
|
|
|
type puller struct {
|
2018-05-17 20:09:11 +00:00
|
|
|
CacheAccessor cache.Accessor
|
|
|
|
*pull.Puller
|
2017-09-05 18:26:03 +00:00
|
|
|
}
|
|
|
|
|
2018-05-17 20:09:11 +00:00
|
|
|
func mainManifestKey(ctx context.Context, desc ocispec.Descriptor) (digest.Digest, error) {
|
2018-04-24 21:00:58 +00:00
|
|
|
dt, err := json.Marshal(struct {
|
|
|
|
Digest digest.Digest
|
|
|
|
OS string
|
|
|
|
Arch string
|
|
|
|
}{
|
2018-05-17 20:09:11 +00:00
|
|
|
Digest: desc.Digest,
|
2018-04-24 21:00:58 +00:00
|
|
|
OS: runtime.GOOS,
|
|
|
|
Arch: runtime.GOARCH,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return digest.FromBytes(dt), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *puller) CacheKey(ctx context.Context, index int) (string, bool, error) {
|
2018-05-17 20:09:11 +00:00
|
|
|
_, desc, err := p.Puller.Resolve(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return "", false, err
|
|
|
|
}
|
|
|
|
if index == 0 || desc.Digest == "" {
|
|
|
|
k, err := mainManifestKey(ctx, desc)
|
2018-04-24 21:00:58 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", false, err
|
|
|
|
}
|
|
|
|
return k.String(), false, nil
|
|
|
|
}
|
2018-05-17 20:09:11 +00:00
|
|
|
ref, err := reference.ParseNormalizedNamed(p.Src.String())
|
2018-04-24 21:00:58 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", false, err
|
|
|
|
}
|
2018-05-17 20:09:11 +00:00
|
|
|
ref, err = reference.WithDigest(ref, desc.Digest)
|
2018-04-24 21:00:58 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", false, nil
|
|
|
|
}
|
2018-05-22 16:18:36 +00:00
|
|
|
_, dt, err := imageutil.Config(ctx, ref.String(), p.Resolver, p.ContentStore, "")
|
2018-04-24 21:00:58 +00:00
|
|
|
if err != nil {
|
|
|
|
// this happens on schema1 images
|
2018-05-17 20:09:11 +00:00
|
|
|
k, err := mainManifestKey(ctx, desc)
|
2018-04-24 21:00:58 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", false, err
|
|
|
|
}
|
|
|
|
return k.String(), true, nil
|
|
|
|
}
|
|
|
|
return cacheKeyFromConfig(dt).String(), true, nil
|
2017-07-06 19:07:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *puller) Snapshot(ctx context.Context) (cache.ImmutableRef, error) {
|
2018-05-17 20:09:11 +00:00
|
|
|
pulled, err := p.Puller.Pull(ctx)
|
2017-05-27 05:10:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-05-22 21:49:21 +00:00
|
|
|
if pulled.ChainID == "" {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2018-05-17 20:09:11 +00:00
|
|
|
return p.CacheAccessor.GetFromSnapshotter(ctx, string(pulled.ChainID), cache.WithDescription(fmt.Sprintf("pulled from %s", pulled.Ref)))
|
2017-06-15 23:08:20 +00:00
|
|
|
}
|
|
|
|
|
2018-04-24 21:00:58 +00:00
|
|
|
// cacheKeyFromConfig returns a stable digest from image config. If image config
|
|
|
|
// is a known oci image we will use chainID of layers.
|
|
|
|
func cacheKeyFromConfig(dt []byte) digest.Digest {
|
|
|
|
var img ocispec.Image
|
|
|
|
err := json.Unmarshal(dt, &img)
|
|
|
|
if err != nil {
|
|
|
|
return digest.FromBytes(dt)
|
|
|
|
}
|
|
|
|
if img.RootFS.Type != "layers" {
|
|
|
|
return digest.FromBytes(dt)
|
|
|
|
}
|
|
|
|
return identity.ChainID(img.RootFS.DiffIDs)
|
|
|
|
}
|