Repository: moby/moby
File: daemon/containerd/service.go (Go)

Code before:
		return 0, err
	}

	visitedImages := make(map[digest.Digest]struct{})
	for _, img := range imgs {
		if _, ok := visitedImages[img.Target.Digest]; ok {
			continue
		}

		if err = i.walkReachableImageManifests(ctx, img, func(imageManifest *ImageManifest) error {
			dgst := imageManifest.Target().Digest
			if _, ok := visitedImages[dgst]; ok {
				return nil
			}
			visitedImages[dgst] = struct{}{}

			size, err := imageManifest.PresentContentSize(ctx)
			if err != nil {
				return err
			}
			diskUsage += size
			return nil
		}); err != nil {
			return 0, err
		}
		visitedImages[img.Target.Digest] = struct{}{}
	}
	if err != nil {
		return 0, err
	}
	return diskUsage, nil
}

// LayerDiskUsage returns the number of bytes used by layer stores
// called from disk_usage.go
func (i *ImageService) layerDiskUsage(ctx context.Context) (allLayersSize int64, err error) {
	// TODO(thaJeztah): do we need to take multiple snapshotters into account? See https://github.com/moby/moby/issues/45273
	snapshotter := i.client.SnapshotService(i.snapshotter)
	err = snapshotter.Walk(ctx, func(ctx context.Context, info snapshots.Info) error {
		usage, err := snapshotter.Usage(ctx, info.Name)
		if err != nil {
			return err
		}
		allLayersSize += usage.Size
		return nil
	})
	return allLayersSize, err
}

// UpdateConfig values
//

Review comment (suggestion):
This will still count the same content twice if it's shared by multiple images.

For example, let's say I have `ubuntu` image and `my-ubuntu` built off:
```dockerfile
FROM ubuntu
RUN apt-get update && apt-get -y install fish
```

`my-ubuntu` will have two layers - one that is shared with `ubuntu` (let's call it `X`) and the second one that would be the delta produced by the `RUN apt-get ...` (let's call it `Y`).

Currently the code would count the `X` layer twice, which would not correspond to the actual disk usage because the layer blob is actually stored only once.

This needs to traverse each child content of the image and then only count the usage of the unique blobs.

Code after:
	if err != nil {
		return 0, err
	}

	visitedImages := make(map[digest.Digest]struct{})
	for _, img := range imgs {
		if err := i.walkPresentChildren(ctx, img.Target, func(ctx context.Context, desc ocispec.Descriptor) error {
			if _, ok := visitedImages[desc.Digest]; ok {
				return nil
			}
			visitedImages[desc.Digest] = struct{}{}

			diskUsage += desc.Size
			return nil
		}); err != nil {
			return 0, err
		}
	}
	return diskUsage, nil
}

// LayerDiskUsage returns the number of bytes used by layer stores
// called from disk_usage.go
func (i *ImageService) layerDiskUsage(ctx context.Context) (allLayersSize int64, err error) {
	// TODO(thaJeztah): do we need to take multiple snapshotters into account? See https://github.com/moby/moby/issues/45273
	snapshotter := i.client.SnapshotService(i.snapshotter)
	err = snapshotter.Walk(ctx, func(ctx context.Context, info snapshots.Info) error {
		usage, err := snapshotter.Usage(ctx, info.Name)
		if err != nil {
			return err
		}
		allLayersSize += usage.Size
		return nil
	})
	return allLayersSize, err
}

// UpdateConfig values
//
// called from reload.go
func (i *ImageService) UpdateConfig(maxDownloads, maxUploads int) {
	log.G(context.TODO()).Warn("max downloads and uploads is not yet implemented with the containerd store")
}

// GetContainerLayerSize returns the real size & virtual size of the container.
func (i *ImageService) GetContainerLayerSize(ctx context.Context, containerID string) (int64, int64, error) {
	ctr := i.containers.Get(containerID)
	if ctr == nil {
		return 0, 0, nil
	}