2022-07-18 10:51:49 +00:00
package containerd
import (
"context"
2022-07-06 07:58:10 +00:00
"github.com/containerd/containerd"
"github.com/containerd/containerd/snapshots"
2022-07-06 15:46:39 +00:00
"github.com/docker/distribution/reference"
2022-07-18 10:51:49 +00:00
"github.com/docker/docker/api/types"
2022-07-06 15:46:39 +00:00
"github.com/docker/docker/api/types/filters"
2022-07-06 07:58:10 +00:00
"github.com/opencontainers/image-spec/identity"
2022-07-18 10:51:49 +00:00
)
2022-07-21 09:01:10 +00:00
var acceptedImageFilterTags = map [ string ] bool {
"dangling" : false , // TODO(thaJeztah): implement "dangling" filter: see https://github.com/moby/moby/issues/43846
"label" : true ,
"before" : true ,
"since" : true ,
"reference" : false , // TODO(thaJeztah): implement "reference" filter: see https://github.com/moby/moby/issues/43847
}
2022-07-18 10:51:49 +00:00
// Images returns a filtered list of images.
2022-07-06 15:46:39 +00:00
//
// TODO(thaJeztah): sort the results by created (descending); see https://github.com/moby/moby/issues/43848
2022-07-25 10:17:23 +00:00
// TODO(thaJeztah): implement opts.ContainerCount (used for docker system df); see https://github.com/moby/moby/issues/43853
// TODO(thaJeztah): add labels to results; see https://github.com/moby/moby/issues/43852
// TODO(thaJeztah): verify behavior of `RepoDigests` and `RepoTags` for images without (untagged) or multiple tags; see https://github.com/moby/moby/issues/43861
// TODO(thaJeztah): verify "Size" vs "VirtualSize" in images; see https://github.com/moby/moby/issues/43862
2022-07-18 10:51:49 +00:00
func ( i * ImageService ) Images ( ctx context . Context , opts types . ImageListOptions ) ( [ ] * types . ImageSummary , error ) {
2022-07-21 09:01:10 +00:00
if err := opts . Filters . Validate ( acceptedImageFilterTags ) ; err != nil {
return nil , err
}
2022-07-06 15:46:39 +00:00
filter , err := i . setupFilters ( ctx , opts . Filters )
if err != nil {
return nil , err
}
2022-07-18 10:51:49 +00:00
imgs , err := i . client . ListImages ( ctx )
if err != nil {
return nil , err
}
2022-07-06 07:58:10 +00:00
snapshotter := i . client . SnapshotService ( containerd . DefaultSnapshotter )
2022-07-25 10:17:23 +00:00
var summaries [ ] * types . ImageSummary
2022-07-18 10:51:49 +00:00
for _ , img := range imgs {
2022-07-06 15:46:39 +00:00
if ! filter ( img ) {
continue
}
2022-07-18 10:51:49 +00:00
size , err := img . Size ( ctx )
if err != nil {
return nil , err
}
2022-07-06 07:58:10 +00:00
virtualSize , err := computeVirtualSize ( ctx , img , snapshotter )
if err != nil {
return nil , err
}
2022-07-25 10:17:23 +00:00
summaries = append ( summaries , & types . ImageSummary {
2022-07-18 10:51:49 +00:00
ParentID : "" ,
ID : img . Target ( ) . Digest . String ( ) ,
Created : img . Metadata ( ) . CreatedAt . Unix ( ) ,
2022-07-25 10:17:23 +00:00
RepoDigests : [ ] string { img . Name ( ) + "@" + img . Target ( ) . Digest . String ( ) } , // "hello-world@sha256:bfea6278a0a267fad2634554f4f0c6f31981eea41c553fdf5a83e95a41d40c38"},
RepoTags : [ ] string { img . Name ( ) } ,
2022-07-18 10:51:49 +00:00
Size : size ,
2022-07-25 10:17:23 +00:00
VirtualSize : virtualSize ,
// -1 indicates that the value has not been set (avoids ambiguity
// between 0 (default) and "not set". We cannot use a pointer (nil)
// for this, as the JSON representation uses "omitempty", which would
// consider both "0" and "nil" to be "empty".
SharedSize : - 1 ,
Containers : - 1 ,
2022-07-18 10:51:49 +00:00
} )
}
2022-07-25 10:17:23 +00:00
return summaries , nil
2022-07-18 10:51:49 +00:00
}
2022-07-06 07:58:10 +00:00
2022-07-06 15:46:39 +00:00
type imageFilterFunc func ( image containerd . Image ) bool
// setupFilters constructs an imageFilterFunc from the given imageFilters.
//
// TODO(thaJeztah): reimplement filters using containerd filters: see https://github.com/moby/moby/issues/43845
func ( i * ImageService ) setupFilters ( ctx context . Context , imageFilters filters . Args ) ( imageFilterFunc , error ) {
var fltrs [ ] imageFilterFunc
err := imageFilters . WalkValues ( "before" , func ( value string ) error {
ref , err := reference . ParseDockerRef ( value )
if err != nil {
return err
}
img , err := i . client . GetImage ( ctx , ref . String ( ) )
if img != nil {
t := img . Metadata ( ) . CreatedAt
fltrs = append ( fltrs , func ( image containerd . Image ) bool {
created := image . Metadata ( ) . CreatedAt
return created . Equal ( t ) || created . After ( t )
} )
}
return err
} )
if err != nil {
return nil , err
}
err = imageFilters . WalkValues ( "since" , func ( value string ) error {
ref , err := reference . ParseDockerRef ( value )
if err != nil {
return err
}
img , err := i . client . GetImage ( ctx , ref . String ( ) )
if img != nil {
t := img . Metadata ( ) . CreatedAt
fltrs = append ( fltrs , func ( image containerd . Image ) bool {
created := image . Metadata ( ) . CreatedAt
return created . Equal ( t ) || created . Before ( t )
} )
}
return err
} )
if err != nil {
return nil , err
}
if imageFilters . Contains ( "label" ) {
fltrs = append ( fltrs , func ( image containerd . Image ) bool {
return imageFilters . MatchKVList ( "label" , image . Labels ( ) )
} )
}
return func ( image containerd . Image ) bool {
for _ , filter := range fltrs {
if ! filter ( image ) {
return false
}
}
return true
} , nil
}
2022-07-06 07:58:10 +00:00
func computeVirtualSize ( ctx context . Context , image containerd . Image , snapshotter snapshots . Snapshotter ) ( int64 , error ) {
var virtualSize int64
diffIDs , err := image . RootFS ( ctx )
if err != nil {
return virtualSize , err
}
for _ , chainID := range identity . ChainIDs ( diffIDs ) {
usage , err := snapshotter . Usage ( ctx , chainID . String ( ) )
if err != nil {
return virtualSize , err
}
virtualSize += usage . Size
}
return virtualSize , nil
}