export: add --skip-non-distributable
The flag skips adding non-distributable blobs such as Windows layers to archive. Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
This commit is contained in:
parent
9067796ce4
commit
e2e2c5737d
@ -44,6 +44,10 @@ When '--all-platforms' is given all images in a manifest list must be available.
|
|||||||
Name: "skip-manifest-json",
|
Name: "skip-manifest-json",
|
||||||
Usage: "do not add Docker compatible manifest.json to archive",
|
Usage: "do not add Docker compatible manifest.json to archive",
|
||||||
},
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "skip-non-distributable",
|
||||||
|
Usage: "do not add non-distributable blobs such as Windows layers to archive",
|
||||||
|
},
|
||||||
cli.StringSliceFlag{
|
cli.StringSliceFlag{
|
||||||
Name: "platform",
|
Name: "platform",
|
||||||
Usage: "Pull content from a specific platform",
|
Usage: "Pull content from a specific platform",
|
||||||
@ -86,6 +90,10 @@ When '--all-platforms' is given all images in a manifest list must be available.
|
|||||||
exportOpts = append(exportOpts, archive.WithSkipDockerManifest())
|
exportOpts = append(exportOpts, archive.WithSkipDockerManifest())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if context.Bool("skip-non-distributable") {
|
||||||
|
exportOpts = append(exportOpts, archive.WithSkipNonDistributableBlobs())
|
||||||
|
}
|
||||||
|
|
||||||
client, ctx, cancel, err := commands.NewClient(context)
|
client, ctx, cancel, err := commands.NewClient(context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -39,6 +39,7 @@ type exportOptions struct {
|
|||||||
platform platforms.MatchComparer
|
platform platforms.MatchComparer
|
||||||
allPlatforms bool
|
allPlatforms bool
|
||||||
skipDockerManifest bool
|
skipDockerManifest bool
|
||||||
|
blobRecordOptions blobRecordOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExportOpt defines options for configuring exported descriptors
|
// ExportOpt defines options for configuring exported descriptors
|
||||||
@ -108,6 +109,25 @@ func WithManifest(manifest ocispec.Descriptor, names ...string) ExportOpt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BlobFilter returns false if the blob should not be included in the archive.
|
||||||
|
type BlobFilter func(ocispec.Descriptor) bool
|
||||||
|
|
||||||
|
// WithBlobFilter specifies BlobFilter.
|
||||||
|
func WithBlobFilter(f BlobFilter) ExportOpt {
|
||||||
|
return func(ctx context.Context, o *exportOptions) error {
|
||||||
|
o.blobRecordOptions.blobFilter = f
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSkipNonDistributableBlobs excludes non-distributable blobs such as Windows base layers.
|
||||||
|
func WithSkipNonDistributableBlobs() ExportOpt {
|
||||||
|
f := func(desc ocispec.Descriptor) bool {
|
||||||
|
return !images.IsNonDistributable(desc.MediaType)
|
||||||
|
}
|
||||||
|
return WithBlobFilter(f)
|
||||||
|
}
|
||||||
|
|
||||||
func addNameAnnotation(name string, base map[string]string) map[string]string {
|
func addNameAnnotation(name string, base map[string]string) map[string]string {
|
||||||
annotations := map[string]string{}
|
annotations := map[string]string{}
|
||||||
for k, v := range base {
|
for k, v := range base {
|
||||||
@ -143,7 +163,7 @@ func Export(ctx context.Context, store content.Provider, writer io.Writer, opts
|
|||||||
mt, ok := dManifests[desc.Digest]
|
mt, ok := dManifests[desc.Digest]
|
||||||
if !ok {
|
if !ok {
|
||||||
// TODO(containerd): Skip if already added
|
// TODO(containerd): Skip if already added
|
||||||
r, err := getRecords(ctx, store, desc, algorithms)
|
r, err := getRecords(ctx, store, desc, algorithms, &eo.blobRecordOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -162,7 +182,7 @@ func Export(ctx context.Context, store content.Provider, writer io.Writer, opts
|
|||||||
case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
|
case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
|
||||||
d, ok := resolvedIndex[desc.Digest]
|
d, ok := resolvedIndex[desc.Digest]
|
||||||
if !ok {
|
if !ok {
|
||||||
records = append(records, blobRecord(store, desc))
|
records = append(records, blobRecord(store, desc, &eo.blobRecordOptions))
|
||||||
|
|
||||||
p, err := content.ReadBlob(ctx, store, desc)
|
p, err := content.ReadBlob(ctx, store, desc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -184,7 +204,7 @@ func Export(ctx context.Context, store content.Provider, writer io.Writer, opts
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := getRecords(ctx, store, m, algorithms)
|
r, err := getRecords(ctx, store, m, algorithms, &eo.blobRecordOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -248,10 +268,10 @@ func Export(ctx context.Context, store content.Provider, writer io.Writer, opts
|
|||||||
return writeTar(ctx, tw, records)
|
return writeTar(ctx, tw, records)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRecords(ctx context.Context, store content.Provider, desc ocispec.Descriptor, algorithms map[string]struct{}) ([]tarRecord, error) {
|
func getRecords(ctx context.Context, store content.Provider, desc ocispec.Descriptor, algorithms map[string]struct{}, brOpts *blobRecordOptions) ([]tarRecord, error) {
|
||||||
var records []tarRecord
|
var records []tarRecord
|
||||||
exportHandler := func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
|
exportHandler := func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
|
||||||
records = append(records, blobRecord(store, desc))
|
records = append(records, blobRecord(store, desc, brOpts))
|
||||||
algorithms[desc.Digest.Algorithm().String()] = struct{}{}
|
algorithms[desc.Digest.Algorithm().String()] = struct{}{}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -277,7 +297,14 @@ type tarRecord struct {
|
|||||||
CopyTo func(context.Context, io.Writer) (int64, error)
|
CopyTo func(context.Context, io.Writer) (int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func blobRecord(cs content.Provider, desc ocispec.Descriptor) tarRecord {
|
type blobRecordOptions struct {
|
||||||
|
blobFilter BlobFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
func blobRecord(cs content.Provider, desc ocispec.Descriptor, opts *blobRecordOptions) tarRecord {
|
||||||
|
if opts != nil && opts.blobFilter != nil && !opts.blobFilter(desc) {
|
||||||
|
return tarRecord{}
|
||||||
|
}
|
||||||
path := path.Join("blobs", desc.Digest.Algorithm().String(), desc.Digest.Encoded())
|
path := path.Join("blobs", desc.Digest.Algorithm().String(), desc.Digest.Encoded())
|
||||||
return tarRecord{
|
return tarRecord{
|
||||||
Header: &tar.Header{
|
Header: &tar.Header{
|
||||||
@ -438,7 +465,13 @@ func manifestsRecord(ctx context.Context, store content.Provider, manifests map[
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeTar(ctx context.Context, tw *tar.Writer, records []tarRecord) error {
|
func writeTar(ctx context.Context, tw *tar.Writer, recordsWithEmpty []tarRecord) error {
|
||||||
|
var records []tarRecord
|
||||||
|
for _, r := range recordsWithEmpty {
|
||||||
|
if r.Header != nil {
|
||||||
|
records = append(records, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
sort.Slice(records, func(i, j int) bool {
|
sort.Slice(records, func(i, j int) bool {
|
||||||
return records[i].Header.Name < records[j].Header.Name
|
return records[i].Header.Name < records[j].Header.Name
|
||||||
})
|
})
|
||||||
|
@ -102,6 +102,12 @@ func parseMediaTypes(mt string) (string, []string) {
|
|||||||
return s[0], ext
|
return s[0], ext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsNonDistributable returns true if the media type is non-distributable.
|
||||||
|
func IsNonDistributable(mt string) bool {
|
||||||
|
return strings.HasPrefix(mt, "application/vnd.oci.image.layer.nondistributable.") ||
|
||||||
|
strings.HasPrefix(mt, "application/vnd.docker.image.rootfs.foreign.")
|
||||||
|
}
|
||||||
|
|
||||||
// IsLayerTypes returns true if the media type is a layer
|
// IsLayerTypes returns true if the media type is a layer
|
||||||
func IsLayerType(mt string) bool {
|
func IsLayerType(mt string) bool {
|
||||||
if strings.HasPrefix(mt, "application/vnd.oci.image.layer.") {
|
if strings.HasPrefix(mt, "application/vnd.oci.image.layer.") {
|
||||||
|
Loading…
Reference in New Issue
Block a user