Merge pull request #634 from tonistiigi/schema1-config-fix

imageutil: fix getting schema1 configs
docker-18.09
Tibor Vass 2018-09-19 15:50:52 -07:00 committed by GitHub
commit 9890dda814
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 105 additions and 7 deletions

View File

@ -251,6 +251,13 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
_ = ref
if len(img.RootFS.DiffIDs) == 0 {
isScratch = true
// schema1 images can't return diffIDs so double check :(
for _, h := range img.History {
if !h.EmptyLayer {
isScratch = false
break
}
}
}
}
}

View File

@ -14,12 +14,12 @@ import (
"github.com/pkg/errors"
)
type IngesterProvider interface {
type ContentCache interface {
content.Ingester
content.Provider
}
func Config(ctx context.Context, str string, resolver remotes.Resolver, ingester IngesterProvider, p *specs.Platform) (digest.Digest, []byte, error) {
func Config(ctx context.Context, str string, resolver remotes.Resolver, cache ContentCache, p *specs.Platform) (digest.Digest, []byte, error) {
// TODO: fix buildkit to take interface instead of struct
var platform platforms.MatchComparer
if p != nil {
@ -36,7 +36,7 @@ func Config(ctx context.Context, str string, resolver remotes.Resolver, ingester
Digest: ref.Digest(),
}
if desc.Digest != "" {
ra, err := ingester.ReaderAt(ctx, desc)
ra, err := cache.ReaderAt(ctx, desc)
if err == nil {
desc.Size = ra.Size()
mt, err := DetectManifestMediaType(ra)
@ -58,19 +58,23 @@ func Config(ctx context.Context, str string, resolver remotes.Resolver, ingester
return "", nil, err
}
if desc.MediaType == images.MediaTypeDockerSchema1Manifest {
return readSchema1Config(ctx, ref.String(), desc, fetcher, cache)
}
handlers := []images.Handler{
remotes.FetchHandler(ingester, fetcher),
childrenConfigHandler(ingester, platform),
remotes.FetchHandler(cache, fetcher),
childrenConfigHandler(cache, platform),
}
if err := images.Dispatch(ctx, images.Handlers(handlers...), desc); err != nil {
return "", nil, err
}
config, err := images.Config(ctx, ingester, desc, platform)
config, err := images.Config(ctx, cache, desc, platform)
if err != nil {
return "", nil, err
}
dt, err := content.ReadBlob(ctx, ingester, config)
dt, err := content.ReadBlob(ctx, cache, config)
if err != nil {
return "", nil, err
}

87
util/imageutil/schema1.go Normal file
View File

@ -0,0 +1,87 @@
package imageutil
import (
"context"
"encoding/json"
"io/ioutil"
"strings"
"time"
"github.com/containerd/containerd/remotes"
digest "github.com/opencontainers/go-digest"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
func readSchema1Config(ctx context.Context, ref string, desc specs.Descriptor, fetcher remotes.Fetcher, cache ContentCache) (digest.Digest, []byte, error) {
rc, err := fetcher.Fetch(ctx, desc)
if err != nil {
return "", nil, err
}
defer rc.Close()
dt, err := ioutil.ReadAll(rc)
if err != nil {
return "", nil, errors.Wrap(err, "failed to fetch schema1 manifest")
}
dt, err = convertSchema1ConfigMeta(dt)
if err != nil {
return "", nil, err
}
return desc.Digest, dt, nil
}
func convertSchema1ConfigMeta(in []byte) ([]byte, error) {
type history struct {
V1Compatibility string `json:"v1Compatibility"`
}
var m struct {
History []history `json:"history"`
}
if err := json.Unmarshal(in, &m); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal schema1 manifest")
}
if len(m.History) == 0 {
return nil, errors.Errorf("invalid schema1 manifest")
}
var img specs.Image
if err := json.Unmarshal([]byte(m.History[0].V1Compatibility), &img); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal image from schema 1 history")
}
img.RootFS = specs.RootFS{
Type: "layers", // filled in by exporter
}
img.History = make([]specs.History, len(m.History))
for i := range m.History {
var h v1History
if err := json.Unmarshal([]byte(m.History[i].V1Compatibility), &h); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal history")
}
img.History[len(m.History)-i-1] = specs.History{
Author: h.Author,
Comment: h.Comment,
Created: &h.Created,
CreatedBy: strings.Join(h.ContainerConfig.Cmd, " "),
EmptyLayer: (h.ThrowAway != nil && *h.ThrowAway) || (h.Size != nil && *h.Size == 0),
}
}
dt, err := json.MarshalIndent(img, "", " ")
if err != nil {
return nil, errors.Wrap(err, "failed to marshal schema1 config")
}
return dt, nil
}
type v1History struct {
Author string `json:"author,omitempty"`
Created time.Time `json:"created"`
Comment string `json:"comment,omitempty"`
ThrowAway *bool `json:"throwaway,omitempty"`
Size *int `json:"Size,omitempty"` // used before ThrowAway field
ContainerConfig struct {
Cmd []string `json:"Cmd,omitempty"`
} `json:"container_config,omitempty"`
}