1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
|
//go:build !remote
package libimage
import (
"context"
"errors"
"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"
)
// layerTree is an internal representation of local layers.
type layerTree struct {
// nodes is the actual layer tree with layer IDs being keys.
nodes map[string]*layerNode
// ociCache is a cache for Image.ID -> OCI Image. Translations are done
// on-demand.
ociCache map[string]*ociv1.Image
// 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.
func (t *layerTree) node(layerID string) *layerNode {
node, exists := t.nodes[layerID]
if !exists {
node = &layerNode{}
t.nodes[layerID] = node
}
return node
}
// ErrorIsImageUnknown returns true if the specified error indicates that an
// image is unknown or has been partially removed (e.g., a missing layer).
func ErrorIsImageUnknown(err error) bool {
return errors.Is(err, storage.ErrImageUnknown) ||
errors.Is(err, storageTypes.ErrLayerUnknown) ||
errors.Is(err, storageTypes.ErrSizeUnknown) ||
errors.Is(err, storage.ErrNotAnImage)
}
// toOCI returns an OCI image for the specified image.
//
// WARNING: callers are responsible for handling cases where the target image
// has been (partially) removed and can use `ErrorIsImageUnknown` to detect it.
func (t *layerTree) toOCI(ctx context.Context, i *Image) (*ociv1.Image, error) {
var err error
oci, exists := t.ociCache[i.ID()]
if !exists {
oci, err = i.toOCI(ctx)
if err == nil {
t.ociCache[i.ID()] = oci
}
}
return oci, err
}
// layerNode is a node in a layerTree. It's ID is the key in a layerTree.
type layerNode struct {
children []*layerNode
images []*Image
parent *layerNode
layer *storage.Layer
}
// repoTags assemble all repo tags all of images of the layer node.
func (l *layerNode) repoTags() ([]string, error) {
orderedTags := []string{}
visitedTags := make(map[string]bool)
for _, image := range l.images {
repoTags, err := image.RepoTags()
if err != nil {
return nil, err
}
for _, tag := range repoTags {
if _, visited := visitedTags[tag]; visited {
continue
}
visitedTags[tag] = true
orderedTags = append(orderedTags, tag)
}
}
return orderedTags, nil
}
// newFreshLayerTree extracts a layerTree from consistent layers and images in the local storage.
func (r *Runtime) newFreshLayerTree() (*layerTree, error) {
images, layers, err := r.getImagesAndLayers()
if err != nil {
return nil, err
}
return r.newLayerTreeFromData(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, generateManifestDigestList bool) (*layerTree, error) {
tree := layerTree{
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.
for i := range layers {
node := tree.node(layers[i].ID)
node.layer = &layers[i]
if layers[i].Parent == "" {
continue
}
parent := tree.node(layers[i].Parent)
node.parent = parent
parent.children = append(parent.children, node)
}
// Now assign the images to each (top) layer.
for i := range images {
img := images[i] // do not leak loop variable outside the scope
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
}
// ignore errors, common errors are
// - image is not manifest
// - image has been removed from the store in the meantime
// In all cases we should ensure image listing still works and not error out.
mlist, err := img.ToManifestList()
if err != nil {
// If it is not a manifest it likely is a regular image so just ignore it.
// If the image is unknown that likely means there was a race where the image/manifest
// was removed after out MultiList() call so we ignore that as well.
if errors.Is(err, ErrNotAManifestList) || errors.Is(err, storageTypes.ErrImageUnknown) {
continue
}
return nil, err
}
for _, digest := range mlist.list.Instances() {
tree.manifestListDigests[digest] = struct{}{}
}
continue
}
node, exists := tree.nodes[topLayer]
if !exists {
// Note: erroring out in this case has turned out having been a
// mistake. Users may not be able to recover, so we're now
// throwing a warning to guide them to resolve the issue and
// turn the errors non-fatal.
logrus.Warnf("Top layer %s of image %s not found in layer tree. The storage may be corrupted, consider running `podman system check`.", topLayer, img.ID())
continue
}
node.images = append(node.images, img)
}
return &tree, nil
}
// children returns the child images of parent. Child images are images with
// either the same top layer as parent or parent being the true parent layer.
// Furthermore, the history of the parent and child images must match with the
// parent having one history item less. If all is true, all images are
// returned. Otherwise, the first image is returned. Note that manifest lists
// do not have children.
func (t *layerTree) children(ctx context.Context, parent *Image, all bool) ([]*Image, error) {
if parent.TopLayer() == "" {
if isManifestList, _ := parent.IsManifestList(ctx); isManifestList {
return nil, nil
}
}
parentID := parent.ID()
parentOCI, err := t.toOCI(ctx, parent)
if err != nil {
if ErrorIsImageUnknown(err) {
return nil, nil
}
return nil, err
}
// checkParent returns true if child and parent are in such a relation.
checkParent := func(child *Image) (bool, error) {
if parentID == child.ID() {
return false, nil
}
childOCI, err := t.toOCI(ctx, child)
if err != nil {
if ErrorIsImageUnknown(err) {
return false, nil
}
return false, err
}
// History check.
return areParentAndChild(parentOCI, childOCI), nil
}
var children []*Image
// Empty images are special in that they do not have any physical layer
// but yet can have a parent-child relation. Hence, compare the
// "parent" image to all other known empty images.
if parent.TopLayer() == "" {
for i := range t.emptyImages {
empty := t.emptyImages[i]
isManifest, err := empty.IsManifestList(ctx)
if err != nil {
return nil, err
}
if isManifest {
// If this is a manifest list and is already
// marked as empty then no instance can be
// selected from this list therefore its
// better to skip this.
continue
}
isParent, err := checkParent(empty)
if err != nil {
return nil, err
}
if isParent {
children = append(children, empty)
if !all {
break
}
}
}
return children, nil
}
parentNode, exists := t.nodes[parent.TopLayer()]
if !exists {
// Note: erroring out in this case has turned out having been a
// mistake. Users may not be able to recover, so we're now
// throwing a warning to guide them to resolve the issue and
// turn the errors non-fatal.
logrus.Warnf("Layer %s not found in layer tree. The storage may be corrupted, consider running `podman system check`.", parent.TopLayer())
return children, nil
}
// addChildrenFrom adds child images of parent to children. Returns
// true if any image is a child of parent.
addChildrenFromNode := func(node *layerNode) (bool, error) {
foundChildren := false
for i, childImage := range node.images {
isChild, err := checkParent(childImage)
if err != nil {
return foundChildren, err
}
if isChild {
foundChildren = true
children = append(children, node.images[i])
if all {
return foundChildren, nil
}
}
}
return foundChildren, nil
}
// First check images where parent's top layer is also the parent
// layer.
for _, childNode := range parentNode.children {
found, err := addChildrenFromNode(childNode)
if err != nil {
return nil, err
}
if found && all {
return children, nil
}
}
// Now check images with the same top layer.
if _, err := addChildrenFromNode(parentNode); err != nil {
return nil, err
}
return children, nil
}
// parent returns the parent image or nil if no parent image could be found.
// Note that manifest lists do not have parents.
func (t *layerTree) parent(ctx context.Context, child *Image) (*Image, error) {
if child.TopLayer() == "" {
if isManifestList, _ := child.IsManifestList(ctx); isManifestList {
return nil, nil
}
}
childID := child.ID()
childOCI, err := t.toOCI(ctx, child)
if err != nil {
if ErrorIsImageUnknown(err) {
return nil, nil
}
return nil, err
}
// Empty images are special in that they do not have any physical layer
// but yet can have a parent-child relation. Hence, compare the
// "child" image to all other known empty images.
if child.TopLayer() == "" {
for _, empty := range t.emptyImages {
if childID == empty.ID() {
continue
}
isManifest, err := empty.IsManifestList(ctx)
if err != nil {
return nil, err
}
if isManifest {
// If this is a manifest list and is already
// marked as empty then no instance can be
// selected from this list therefore its
// better to skip this.
continue
}
emptyOCI, err := t.toOCI(ctx, empty)
if err != nil {
if ErrorIsImageUnknown(err) {
return nil, nil
}
return nil, err
}
// History check.
if areParentAndChild(emptyOCI, childOCI) {
return empty, nil
}
}
return nil, nil
}
node, exists := t.nodes[child.TopLayer()]
if !exists {
// Note: erroring out in this case has turned out having been a
// mistake. Users may not be able to recover, so we're now
// throwing a warning to guide them to resolve the issue and
// turn the errors non-fatal.
logrus.Warnf("Layer %s not found in layer tree. The storage may be corrupted, consider running `podman system check`.", child.TopLayer())
return nil, nil
}
// Check images from the parent node (i.e., parent layer) and images
// with the same layer (i.e., same top layer).
images := node.images
if node.parent != nil {
images = append(images, node.parent.images...)
}
for _, parent := range images {
if parent.ID() == childID {
continue
}
parentOCI, err := t.toOCI(ctx, parent)
if err != nil {
if ErrorIsImageUnknown(err) {
return nil, nil
}
return nil, err
}
// History check.
if areParentAndChild(parentOCI, childOCI) {
return parent, nil
}
}
return nil, nil
}
|