-
Notifications
You must be signed in to change notification settings - Fork 219
When checking IsDangling make sure image is not in manifest list #2360
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ import ( | |
|
||
"github.com/containers/storage" | ||
storageTypes "github.com/containers/storage/types" | ||
digest "github.com/opencontainers/go-digest" | ||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1" | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
@@ -22,6 +23,10 @@ type layerTree struct { | |
// emptyImages do not have any top-layer so we cannot create a | ||
// *layerNode for them. | ||
emptyImages []*Image | ||
// manifestList keep track of images based on their digest. | ||
// Library will use this map when checking if a image is dangling. | ||
// If an image is used in a manifestList it is NOT dangling | ||
manifestListDigests map[digest.Digest]struct{} | ||
} | ||
|
||
// node returns a layerNode for the specified layerID. | ||
|
@@ -90,20 +95,21 @@ func (l *layerNode) repoTags() ([]string, error) { | |
} | ||
|
||
// newFreshLayerTree extracts a layerTree from consistent layers and images in the local storage. | ||
func (r *Runtime) newFreshLayerTree() (*layerTree, error) { | ||
func (r *Runtime) newFreshLayerTree(ctx context.Context) (*layerTree, error) { | ||
images, layers, err := r.getImagesAndLayers() | ||
if err != nil { | ||
return nil, err | ||
} | ||
return r.newLayerTreeFromData(images, layers) | ||
return r.newLayerTreeFromData(ctx, images, layers, false) | ||
} | ||
|
||
// newLayerTreeFromData extracts a layerTree from the given the layers and images. | ||
// The caller is responsible for (layers, images) being consistent. | ||
func (r *Runtime) newLayerTreeFromData(images []*Image, layers []storage.Layer) (*layerTree, error) { | ||
func (r *Runtime) newLayerTreeFromData(ctx context.Context, images []*Image, layers []storage.Layer, generateManifestDigestList bool) (*layerTree, error) { | ||
tree := layerTree{ | ||
nodes: make(map[string]*layerNode), | ||
ociCache: make(map[string]*ociv1.Image), | ||
nodes: make(map[string]*layerNode), | ||
ociCache: make(map[string]*ociv1.Image), | ||
manifestListDigests: make(map[digest.Digest]struct{}), | ||
} | ||
|
||
// First build a tree purely based on layer information. | ||
|
@@ -124,6 +130,21 @@ func (r *Runtime) newLayerTreeFromData(images []*Image, layers []storage.Layer) | |
topLayer := img.TopLayer() | ||
if topLayer == "" { | ||
tree.emptyImages = append(tree.emptyImages, img) | ||
// When img is a manifest list, cache the lists of | ||
// digests refereenced in manifest list. Digests can | ||
// be used to check for dangling images. | ||
if !generateManifestDigestList { | ||
continue | ||
} | ||
if manifestList, _ := img.IsManifestList(ctx); manifestList { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a comment why we're doing this here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also if we are concerned about performance impact why are we doing this for every layer tree. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see this as a huge cost with the new design, you already read the image into memory and I doubt their is a huge amount of manifestlist in anyone's environment. Not sure if hacking this up for a theoretically performance improvement is worth it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The cost of storage operations is all the file-based locking (incl. writes to the lock files); it’s not purely in-memory. But, also, I haven’t measured anything, and that’s the only answer that counts. (We don’t have a Podman performance regression test, do we?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You would have to setup a test with a Huge amount of manifest list images to see any cost, and I am not sure if this is even hitting the disk at all, is this data already being collected in the list of images? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
… but And then it depends on the caller — the parent/child queries do read manifests of likely images: so, listing parent/child/dangling data about every single image reads all manifests anyway; but, I think, one-image queries like |
||
mlist, err := img.ToManifestList() | ||
if err != nil { | ||
return nil, err | ||
} | ||
for _, digest := range mlist.list.Instances() { | ||
tree.manifestListDigests[digest] = struct{}{} | ||
} | ||
} | ||
continue | ||
} | ||
node, exists := tree.nodes[topLayer] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -634,7 +634,7 @@ func (r *Runtime) ListImages(ctx context.Context, options *ListImagesOptions) ([ | |
|
||
var tree *layerTree | ||
if needsLayerTree { | ||
tree, err = r.newLayerTreeFromData(images, snapshot.Layers) | ||
tree, err = r.newLayerTreeFromData(ctx, images, snapshot.Layers, true) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is only run for options.SetListData which does not seem to be set in RemoveImages() which is called by prune so I don't think the true is correctly populated in that case to fix your issue? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prune calls listimages with a isdangling flag, which ends up calling here. podman images --filter dangling also calls here. |
||
if err != nil { | ||
return nil, err | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That breaks the API, we don't care about it in c/common so that is fine but it will need an update in podman.
I think the other breaking changes are fixed with containers/podman#25635 so you shuld be good to vendor that without any other fixes in podman needed.