Update logic to determine if layer is empty

Handle reliance on the size field when the throwaway field is not used.

Signed-off-by: Derek McGowan <derek@mcgstyle.net>
This commit is contained in:
Derek McGowan 2017-06-07 14:41:45 -07:00
parent 9f90d8a9b4
commit 3a226ef17d
No known key found for this signature in database
GPG Key ID: F58C5D0A4405ACDB

View File

@ -35,6 +35,7 @@ type Converter struct {
fetcher remotes.Fetcher fetcher remotes.Fetcher
pulledManifest *manifest pulledManifest *manifest
layers []ocispec.Descriptor
mu sync.Mutex mu sync.Mutex
blobMap map[digest.Digest]digest.Digest blobMap map[digest.Digest]digest.Digest
@ -66,21 +67,17 @@ func (c *Converter) Handle(ctx context.Context, desc ocispec.Descriptor) ([]ocis
if err := json.Unmarshal([]byte(m.History[i].V1Compatibility), &h); err != nil { if err := json.Unmarshal([]byte(m.History[i].V1Compatibility), &h); err != nil {
return nil, err return nil, err
} }
if !h.ThrowAway { if !h.EmptyLayer() {
descs = append(descs, ocispec.Descriptor{ descs = append([]ocispec.Descriptor{
{
MediaType: images.MediaTypeDockerSchema2LayerGzip, MediaType: images.MediaTypeDockerSchema2LayerGzip,
Digest: c.pulledManifest.FSLayers[i].BlobSum, Digest: c.pulledManifest.FSLayers[i].BlobSum,
}) },
}, descs...)
} }
} }
// Reverse c.layers = descs
for i := 0; i <= len(descs)/2; i++ { return c.layers, nil
j := len(descs) - i - 1
if i != j {
descs[i], descs[j] = descs[j], descs[i]
}
}
return descs, nil
case images.MediaTypeDockerSchema2LayerGzip: case images.MediaTypeDockerSchema2LayerGzip:
if c.pulledManifest == nil { if c.pulledManifest == nil {
return nil, errors.New("manifest required for schema 1 blob pull") return nil, errors.New("manifest required for schema 1 blob pull")
@ -95,11 +92,45 @@ func (c *Converter) Convert(ctx context.Context) (ocispec.Descriptor, error) {
if c.pulledManifest == nil { if c.pulledManifest == nil {
return ocispec.Descriptor{}, errors.New("missing schema 1 manifest for conversion") return ocispec.Descriptor{}, errors.New("missing schema 1 manifest for conversion")
} }
if len(c.pulledManifest.History) == 0 {
return ocispec.Descriptor{}, errors.New("no history")
}
if len(c.layers) == 0 {
return ocispec.Descriptor{}, errors.New("schema 1 manifest has no usable layers")
}
img, err := convertSchema1Manifest(c.pulledManifest, c.blobMap) var img ocispec.Image
if err := json.Unmarshal([]byte(c.pulledManifest.History[0].V1Compatibility), &img); err != nil {
return ocispec.Descriptor{}, errors.Wrap(err, "failed to unmarshal image from schema 1 history")
}
history, err := schema1ManifestHistory(c.pulledManifest)
if err != nil { if err != nil {
return ocispec.Descriptor{}, errors.Wrap(err, "schema 1 conversion failed") return ocispec.Descriptor{}, errors.Wrap(err, "schema 1 conversion failed")
} }
img.History = history
diffIDs := make([]digest.Digest, len(c.layers))
for i, layer := range c.layers {
info, err := c.contentStore.Info(ctx, layer.Digest)
if err != nil {
return ocispec.Descriptor{}, errors.Wrap(err, "failed to get blob info")
}
// Fill in size since not given by schema 1 manifest
c.layers[i].Size = info.Size
diffID, ok := c.blobMap[layer.Digest]
if !ok {
return ocispec.Descriptor{}, errors.New("missing diff id")
}
diffIDs[i] = diffID
}
img.RootFS = ocispec.RootFS{
Type: "layers",
DiffIDs: diffIDs,
}
b, err := json.Marshal(img) b, err := json.Marshal(img)
if err != nil { if err != nil {
return ocispec.Descriptor{}, errors.Wrap(err, "failed to marshal image") return ocispec.Descriptor{}, errors.Wrap(err, "failed to marshal image")
@ -116,30 +147,12 @@ func (c *Converter) Convert(ctx context.Context) (ocispec.Descriptor, error) {
return ocispec.Descriptor{}, errors.Wrap(err, "failed to write config") return ocispec.Descriptor{}, errors.Wrap(err, "failed to write config")
} }
layers := make([]ocispec.Descriptor, 0)
for _, layer := range c.pulledManifest.FSLayers {
// TODO: Use rootfs mapping!
info, err := c.contentStore.Info(ctx, layer.BlobSum)
if err != nil {
if content.IsNotFound(err) {
continue
}
return ocispec.Descriptor{}, errors.Wrap(err, "failed to get blob info")
}
layers = append([]ocispec.Descriptor{{
MediaType: ocispec.MediaTypeImageLayerGzip,
Digest: layer.BlobSum,
Size: info.Size,
}}, layers...)
}
manifest := ocispec.Manifest{ manifest := ocispec.Manifest{
Versioned: specs.Versioned{ Versioned: specs.Versioned{
SchemaVersion: 2, SchemaVersion: 2,
}, },
Config: config, Config: config,
Layers: layers, Layers: c.layers,
} }
b, err = json.Marshal(manifest) b, err = json.Marshal(manifest)
@ -272,62 +285,44 @@ type v1History struct {
Author string `json:"author,omitempty"` Author string `json:"author,omitempty"`
Created time.Time `json:"created"` Created time.Time `json:"created"`
Comment string `json:"comment,omitempty"` Comment string `json:"comment,omitempty"`
ThrowAway bool `json:"throwaway,omitempty"` ThrowAway *bool `json:"throwaway,omitempty"`
Size *int `json:"Size,omitempty"` // used before ThrowAway field
ContainerConfig struct { ContainerConfig struct {
Cmd []string `json:"Cmd,omitempty"` Cmd []string `json:"Cmd,omitempty"`
} `json:"container_config,omitempty"` } `json:"container_config,omitempty"`
} }
func convertSchema1Manifest(m *manifest, blobs map[digest.Digest]digest.Digest) (*ocispec.Image, error) { func (h *v1History) EmptyLayer() bool {
if len(m.History) == 0 { if h.ThrowAway != nil {
return nil, errors.New("no history") return !(*h.ThrowAway)
}
if h.Size != nil {
return *h.Size == 0
} }
var img ocispec.Image // If no size is given or `ThrowAway` specified, the image is empty
if err := json.Unmarshal([]byte(m.History[0].V1Compatibility), &img); err != nil { return true
return nil, errors.Wrap(err, "failed to unmarshal image from schema 1 history")
} }
diffIDs := make([]digest.Digest, 0, len(m.History)) func schema1ManifestHistory(m *manifest) ([]ocispec.History, error) {
img.History = make([]ocispec.History, len(m.History)) history := make([]ocispec.History, len(m.History))
for i := range m.History { for i := range m.History {
var h v1History var h v1History
if err := json.Unmarshal([]byte(m.History[i].V1Compatibility), &h); err != nil { if err := json.Unmarshal([]byte(m.History[i].V1Compatibility), &h); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal history") return nil, errors.Wrap(err, "failed to unmarshal history")
} }
img.History[len(img.History)-i-1] = ocispec.History{ empty := h.EmptyLayer()
history[len(history)-i-1] = ocispec.History{
Author: h.Author, Author: h.Author,
Comment: h.Comment, Comment: h.Comment,
Created: &h.Created, Created: &h.Created,
CreatedBy: strings.Join(h.ContainerConfig.Cmd, " "), CreatedBy: strings.Join(h.ContainerConfig.Cmd, " "),
EmptyLayer: h.ThrowAway, EmptyLayer: empty,
}
if !h.ThrowAway {
diffID, ok := blobs[m.FSLayers[i].BlobSum]
if !ok {
return nil, errors.Errorf("no diff id for blob %s", m.FSLayers[i].BlobSum.String())
}
diffIDs = append(diffIDs, diffID)
}
}
for i := 0; i <= len(diffIDs)/2; i++ {
j := len(diffIDs) - i - 1
if i != j {
diffIDs[i], diffIDs[j] = diffIDs[j], diffIDs[i]
} }
} }
img.RootFS = ocispec.RootFS{ return history, nil
Type: "layers",
DiffIDs: diffIDs,
}
return &img, nil
} }
type signature struct { type signature struct {