2021-03-30 12:59:03 +00:00
|
|
|
package cache
|
|
|
|
|
|
|
|
import (
|
|
|
|
"compress/gzip"
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
|
|
|
|
"github.com/containerd/containerd/content"
|
|
|
|
"github.com/containerd/containerd/errdefs"
|
|
|
|
"github.com/containerd/containerd/images"
|
|
|
|
"github.com/containerd/containerd/images/converter"
|
|
|
|
"github.com/containerd/containerd/images/converter/uncompress"
|
|
|
|
"github.com/containerd/containerd/labels"
|
|
|
|
"github.com/moby/buildkit/util/compression"
|
|
|
|
"github.com/opencontainers/go-digest"
|
2021-07-26 08:53:30 +00:00
|
|
|
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
2021-03-30 12:59:03 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// getConverters returns converter functions according to the specified compression type.
|
|
|
|
// If no conversion is needed, this returns nil without error.
|
2021-07-26 08:53:30 +00:00
|
|
|
func getConverters(desc ocispecs.Descriptor, compressionType compression.Type) (converter.ConvertFunc, func(string) string, error) {
|
2021-03-30 12:59:03 +00:00
|
|
|
switch compressionType {
|
|
|
|
case compression.Uncompressed:
|
|
|
|
if !images.IsLayerType(desc.MediaType) || uncompress.IsUncompressedType(desc.MediaType) {
|
|
|
|
// No conversion. No need to return an error here.
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
return uncompress.LayerConvertFunc, convertMediaTypeToUncompress, nil
|
|
|
|
case compression.Gzip:
|
|
|
|
if !images.IsLayerType(desc.MediaType) || isGzipCompressedType(desc.MediaType) {
|
|
|
|
// No conversion. No need to return an error here.
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
return gzipLayerConvertFunc, convertMediaTypeToGzip, nil
|
|
|
|
default:
|
|
|
|
return nil, nil, fmt.Errorf("unknown compression type during conversion: %q", compressionType)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-26 08:53:30 +00:00
|
|
|
func gzipLayerConvertFunc(ctx context.Context, cs content.Store, desc ocispecs.Descriptor) (*ocispecs.Descriptor, error) {
|
2021-03-30 12:59:03 +00:00
|
|
|
if !images.IsLayerType(desc.MediaType) || isGzipCompressedType(desc.MediaType) {
|
|
|
|
// No conversion. No need to return an error here.
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// prepare the source and destination
|
|
|
|
info, err := cs.Info(ctx, desc.Digest)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
labelz := info.Labels
|
|
|
|
if labelz == nil {
|
|
|
|
labelz = make(map[string]string)
|
|
|
|
}
|
|
|
|
ra, err := cs.ReaderAt(ctx, desc)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer ra.Close()
|
|
|
|
ref := fmt.Sprintf("convert-gzip-from-%s", desc.Digest)
|
|
|
|
w, err := cs.Writer(ctx, content.WithRef(ref))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer w.Close()
|
|
|
|
if err := w.Truncate(0); err != nil { // Old written data possibly remains
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
zw := gzip.NewWriter(w)
|
|
|
|
defer zw.Close()
|
|
|
|
|
|
|
|
// convert this layer
|
|
|
|
diffID := digest.Canonical.Digester()
|
|
|
|
if _, err := io.Copy(zw, io.TeeReader(io.NewSectionReader(ra, 0, ra.Size()), diffID.Hash())); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := zw.Close(); err != nil { // Flush the writer
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
labelz[labels.LabelUncompressed] = diffID.Digest().String() // update diffID label
|
|
|
|
if err = w.Commit(ctx, 0, "", content.WithLabels(labelz)); err != nil && !errdefs.IsAlreadyExists(err) {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := w.Close(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
info, err = cs.Info(ctx, w.Digest())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
newDesc := desc
|
|
|
|
newDesc.MediaType = convertMediaTypeToGzip(newDesc.MediaType)
|
|
|
|
newDesc.Digest = info.Digest
|
|
|
|
newDesc.Size = info.Size
|
|
|
|
return &newDesc, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func isGzipCompressedType(mt string) bool {
|
|
|
|
switch mt {
|
|
|
|
case
|
|
|
|
images.MediaTypeDockerSchema2LayerGzip,
|
|
|
|
images.MediaTypeDockerSchema2LayerForeignGzip,
|
2021-07-26 08:53:30 +00:00
|
|
|
ocispecs.MediaTypeImageLayerGzip,
|
|
|
|
ocispecs.MediaTypeImageLayerNonDistributableGzip:
|
2021-03-30 12:59:03 +00:00
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func convertMediaTypeToUncompress(mt string) string {
|
|
|
|
switch mt {
|
|
|
|
case images.MediaTypeDockerSchema2LayerGzip:
|
|
|
|
return images.MediaTypeDockerSchema2Layer
|
|
|
|
case images.MediaTypeDockerSchema2LayerForeignGzip:
|
|
|
|
return images.MediaTypeDockerSchema2LayerForeign
|
2021-07-26 08:53:30 +00:00
|
|
|
case ocispecs.MediaTypeImageLayerGzip:
|
|
|
|
return ocispecs.MediaTypeImageLayer
|
|
|
|
case ocispecs.MediaTypeImageLayerNonDistributableGzip:
|
|
|
|
return ocispecs.MediaTypeImageLayerNonDistributable
|
2021-03-30 12:59:03 +00:00
|
|
|
default:
|
|
|
|
return mt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func convertMediaTypeToGzip(mt string) string {
|
|
|
|
if uncompress.IsUncompressedType(mt) {
|
|
|
|
if images.IsDockerType(mt) {
|
|
|
|
mt += ".gzip"
|
|
|
|
} else {
|
|
|
|
mt += "+gzip"
|
|
|
|
}
|
|
|
|
return mt
|
|
|
|
}
|
|
|
|
return mt
|
|
|
|
}
|