Add filter fields to image store types

Signed-off-by: Derek McGowan <derek@mcg.dev>
This commit is contained in:
Derek McGowan 2022-09-15 22:19:20 -07:00
parent 9ae92f16e0
commit 40d3fa3afd
No known key found for this signature in database
GPG Key ID: F58C5D0A4405ACDB

View File

@ -20,6 +20,7 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/containerd/containerd/api/types"
transfertypes "github.com/containerd/containerd/api/types/transfer" transfertypes "github.com/containerd/containerd/api/types/transfer"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
@ -39,23 +40,73 @@ func init() {
} }
type Store struct { type Store struct {
// TODO: Put these configurations in object which can convert to/from any
// Embed generated type
imageName string imageName string
imageLabels map[string]string imageLabels map[string]string
platforms platforms.MatchComparer platforms []ocispec.Platform
allMetadata bool allMetadata bool
labelMap func(ocispec.Descriptor) []string labelMap func(ocispec.Descriptor) []string
manifestLimit int manifestLimit int
// TODO: Convert these to unpack platforms unpacks []UnpackConfiguration
unpacks []unpack.Platform
} }
func NewStore(image string) *Store { // UnpackConfiguration specifies the platform and snapshotter to use for resolving
return &Store{ // the unpack Platform, if snapshotter is not specified the platform default will
// be used.
type UnpackConfiguration struct {
Platform ocispec.Platform
Snapshotter string
}
// StoreOpt defines options when configuring an image store source or destination
type StoreOpt func(*Store)
// WithImageLabels are the image labels to apply to a new image
func WithImageLabels(labels map[string]string) StoreOpt {
return func(s *Store) {
s.imageLabels = labels
}
}
// WithPlatforms specifies which platforms to fetch content for
func WithPlatforms(p ...ocispec.Platform) StoreOpt {
return func(s *Store) {
s.platforms = append(s.platforms, p...)
}
}
// WithManifestLimit defines the max number of manifests to fetch
func WithManifestLimit(limit int) StoreOpt {
return func(s *Store) {
s.manifestLimit = limit
}
}
func WithAllMetadata(s *Store) {
s.allMetadata = true
}
// WithUnpack specifies a platform to unpack for and an optional snapshotter to use
func WithUnpack(p ocispec.Platform, snapshotter string) StoreOpt {
return func(s *Store) {
s.unpacks = append(s.unpacks, UnpackConfiguration{
Platform: p,
Snapshotter: snapshotter,
})
}
}
// NewStore creates a new image store source or Destination
func NewStore(image string, opts ...StoreOpt) *Store {
s := &Store{
imageName: image, imageName: image,
} }
for _, opt := range opts {
opt(s)
}
return s
} }
func (is *Store) String() string { func (is *Store) String() string {
@ -63,19 +114,25 @@ func (is *Store) String() string {
} }
func (is *Store) ImageFilter(h images.HandlerFunc, cs content.Store) images.HandlerFunc { func (is *Store) ImageFilter(h images.HandlerFunc, cs content.Store) images.HandlerFunc {
var p platforms.MatchComparer
if len(is.platforms) == 0 {
p = platforms.All
} else {
p = platforms.Ordered(is.platforms...)
}
h = images.SetChildrenMappedLabels(cs, h, is.labelMap) h = images.SetChildrenMappedLabels(cs, h, is.labelMap)
if is.allMetadata { if is.allMetadata {
// Filter manifests by platforms but allow to handle manifest // Filter manifests by platforms but allow to handle manifest
// and configuration for not-target platforms // and configuration for not-target platforms
h = remotes.FilterManifestByPlatformHandler(h, is.platforms) h = remotes.FilterManifestByPlatformHandler(h, p)
} else { } else {
// Filter children by platforms if specified. // Filter children by platforms if specified.
h = images.FilterPlatforms(h, is.platforms) h = images.FilterPlatforms(h, p)
} }
// Sort and limit manifests if a finite number is needed // Sort and limit manifests if a finite number is needed
if is.manifestLimit > 0 { if is.manifestLimit > 0 {
h = images.LimitManifests(h, is.platforms, is.manifestLimit) h = images.LimitManifests(h, p, is.manifestLimit)
} }
return h return h
} }
@ -116,13 +173,23 @@ func (is *Store) Get(ctx context.Context, store images.Store) (images.Image, err
} }
func (is *Store) UnpackPlatforms() []unpack.Platform { func (is *Store) UnpackPlatforms() []unpack.Platform {
return is.unpacks unpacks := make([]unpack.Platform, len(is.unpacks))
for i, uc := range is.unpacks {
unpacks[i].SnapshotterKey = uc.Snapshotter
unpacks[i].Platform = platforms.Only(uc.Platform)
}
return unpacks
} }
func (is *Store) MarshalAny(ctx context.Context, sm streaming.StreamCreator) (typeurl.Any, error) { func (is *Store) MarshalAny(context.Context, streaming.StreamCreator) (typeurl.Any, error) {
//unpack.Platform
s := &transfertypes.ImageStore{ s := &transfertypes.ImageStore{
Name: is.imageName, Name: is.imageName,
// TODO: Support other fields Labels: is.imageLabels,
ManifestLimit: uint32(is.manifestLimit),
AllMetadata: is.allMetadata,
Platforms: platformsToProto(is.platforms),
Unpacks: unpackToProto(is.unpacks),
} }
return typeurl.MarshalAny(s) return typeurl.MarshalAny(s)
} }
@ -134,7 +201,64 @@ func (is *Store) UnmarshalAny(ctx context.Context, sm streaming.StreamGetter, a
} }
is.imageName = s.Name is.imageName = s.Name
// TODO: Support other fields is.imageLabels = s.Labels
is.manifestLimit = int(s.ManifestLimit)
is.allMetadata = s.AllMetadata
is.platforms = platformFromProto(s.Platforms)
is.unpacks = unpackFromProto(s.Unpacks)
return nil return nil
} }
func platformsToProto(platforms []ocispec.Platform) []*types.Platform {
ap := make([]*types.Platform, len(platforms))
for i := range platforms {
p := types.Platform{
OS: platforms[i].OS,
Architecture: platforms[i].Architecture,
Variant: platforms[i].Variant,
}
ap[i] = &p
}
return ap
}
func platformFromProto(platforms []*types.Platform) []ocispec.Platform {
op := make([]ocispec.Platform, len(platforms))
for i := range platforms {
op[i].OS = platforms[i].OS
op[i].Architecture = platforms[i].Architecture
op[i].Variant = platforms[i].Variant
}
return op
}
func unpackToProto(uc []UnpackConfiguration) []*transfertypes.UnpackConfiguration {
auc := make([]*transfertypes.UnpackConfiguration, len(uc))
for i := range uc {
p := types.Platform{
OS: uc[i].Platform.OS,
Architecture: uc[i].Platform.Architecture,
Variant: uc[i].Platform.Variant,
}
auc[i] = &transfertypes.UnpackConfiguration{
Platform: &p,
Snapshotter: uc[i].Snapshotter,
}
}
return auc
}
func unpackFromProto(auc []*transfertypes.UnpackConfiguration) []UnpackConfiguration {
uc := make([]UnpackConfiguration, len(auc))
for i := range auc {
uc[i].Snapshotter = auc[i].Snapshotter
if auc[i].Platform != nil {
uc[i].Platform.OS = auc[i].Platform.OS
uc[i].Platform.Architecture = auc[i].Platform.Architecture
uc[i].Platform.Variant = auc[i].Platform.Variant
}
}
return uc
}