Merge pull request #634 from tonistiigi/schema1-config-fix
imageutil: fix getting schema1 configsdocker-18.09
commit
9890dda814
|
@ -251,6 +251,13 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
|
||||||
_ = ref
|
_ = ref
|
||||||
if len(img.RootFS.DiffIDs) == 0 {
|
if len(img.RootFS.DiffIDs) == 0 {
|
||||||
isScratch = true
|
isScratch = true
|
||||||
|
// schema1 images can't return diffIDs so double check :(
|
||||||
|
for _, h := range img.History {
|
||||||
|
if !h.EmptyLayer {
|
||||||
|
isScratch = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,12 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IngesterProvider interface {
|
type ContentCache interface {
|
||||||
content.Ingester
|
content.Ingester
|
||||||
content.Provider
|
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
|
// TODO: fix buildkit to take interface instead of struct
|
||||||
var platform platforms.MatchComparer
|
var platform platforms.MatchComparer
|
||||||
if p != nil {
|
if p != nil {
|
||||||
|
@ -36,7 +36,7 @@ func Config(ctx context.Context, str string, resolver remotes.Resolver, ingester
|
||||||
Digest: ref.Digest(),
|
Digest: ref.Digest(),
|
||||||
}
|
}
|
||||||
if desc.Digest != "" {
|
if desc.Digest != "" {
|
||||||
ra, err := ingester.ReaderAt(ctx, desc)
|
ra, err := cache.ReaderAt(ctx, desc)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
desc.Size = ra.Size()
|
desc.Size = ra.Size()
|
||||||
mt, err := DetectManifestMediaType(ra)
|
mt, err := DetectManifestMediaType(ra)
|
||||||
|
@ -58,19 +58,23 @@ func Config(ctx context.Context, str string, resolver remotes.Resolver, ingester
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if desc.MediaType == images.MediaTypeDockerSchema1Manifest {
|
||||||
|
return readSchema1Config(ctx, ref.String(), desc, fetcher, cache)
|
||||||
|
}
|
||||||
|
|
||||||
handlers := []images.Handler{
|
handlers := []images.Handler{
|
||||||
remotes.FetchHandler(ingester, fetcher),
|
remotes.FetchHandler(cache, fetcher),
|
||||||
childrenConfigHandler(ingester, platform),
|
childrenConfigHandler(cache, platform),
|
||||||
}
|
}
|
||||||
if err := images.Dispatch(ctx, images.Handlers(handlers...), desc); err != nil {
|
if err := images.Dispatch(ctx, images.Handlers(handlers...), desc); err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
config, err := images.Config(ctx, ingester, desc, platform)
|
config, err := images.Config(ctx, cache, desc, platform)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dt, err := content.ReadBlob(ctx, ingester, config)
|
dt, err := content.ReadBlob(ctx, cache, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"`
|
||||||
|
}
|
Loading…
Reference in New Issue