This change reworks to build up the reclaimable image disk uage instead of setting it to total size and subtracting active images. This change also includes the image index size as reclaimable if it is included with the image summary. Signed-off-by: Austin Vazquez <austin.vazquez@docker.com>
277 lines
7.6 KiB
Go
277 lines
7.6 KiB
Go
package client
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
|
|
cerrdefs "github.com/containerd/errdefs"
|
|
"github.com/moby/moby/api/types/image"
|
|
"github.com/moby/moby/api/types/system"
|
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"gotest.tools/v3/assert"
|
|
is "gotest.tools/v3/assert/cmp"
|
|
)
|
|
|
|
func TestDiskUsageError(t *testing.T) {
|
|
client, err := New(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
|
|
assert.NilError(t, err)
|
|
_, err = client.DiskUsage(t.Context(), DiskUsageOptions{})
|
|
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
|
|
}
|
|
|
|
func TestDiskUsage(t *testing.T) {
|
|
const expectedURL = "/system/df"
|
|
client, err := New(WithMockClient(func(req *http.Request) (*http.Response, error) {
|
|
if err := assertRequest(req, http.MethodGet, expectedURL); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return mockJSONResponse(http.StatusOK, nil, system.DiskUsage{
|
|
ImageUsage: &image.DiskUsage{
|
|
ActiveCount: 0,
|
|
TotalCount: 0,
|
|
Reclaimable: 0,
|
|
TotalSize: 4096,
|
|
Items: []image.Summary{},
|
|
},
|
|
})(req)
|
|
}))
|
|
assert.NilError(t, err)
|
|
|
|
du, err := client.DiskUsage(t.Context(), DiskUsageOptions{})
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, du.Images.ActiveCount, int64(0))
|
|
assert.Equal(t, du.Images.TotalCount, int64(0))
|
|
assert.Equal(t, du.Images.Reclaimable, int64(0))
|
|
assert.Equal(t, du.Images.TotalSize, int64(4096))
|
|
assert.Equal(t, len(du.Images.Items), 0)
|
|
}
|
|
|
|
func TestDiskUsageWithOptions(t *testing.T) {
|
|
const expectedURL = "/system/df"
|
|
|
|
tests := []struct {
|
|
options DiskUsageOptions
|
|
expectedQuery string
|
|
}{
|
|
{
|
|
options: DiskUsageOptions{
|
|
Containers: true,
|
|
},
|
|
expectedQuery: "type=container",
|
|
},
|
|
{
|
|
options: DiskUsageOptions{
|
|
Images: true,
|
|
},
|
|
expectedQuery: "type=image",
|
|
},
|
|
{
|
|
options: DiskUsageOptions{
|
|
Volumes: true,
|
|
},
|
|
expectedQuery: "type=volume",
|
|
},
|
|
{
|
|
options: DiskUsageOptions{
|
|
BuildCache: true,
|
|
},
|
|
expectedQuery: "type=build-cache",
|
|
},
|
|
{
|
|
options: DiskUsageOptions{
|
|
Containers: true,
|
|
Images: true,
|
|
},
|
|
expectedQuery: "type=container&type=image",
|
|
},
|
|
{
|
|
options: DiskUsageOptions{
|
|
Containers: true,
|
|
Images: true,
|
|
Volumes: true,
|
|
BuildCache: true,
|
|
},
|
|
expectedQuery: "type=container&type=image&type=volume&type=build-cache",
|
|
},
|
|
{
|
|
options: DiskUsageOptions{
|
|
Containers: true,
|
|
Verbose: true,
|
|
},
|
|
expectedQuery: "type=container&verbose=1",
|
|
},
|
|
{
|
|
options: DiskUsageOptions{
|
|
Containers: true,
|
|
Images: true,
|
|
Volumes: true,
|
|
BuildCache: true,
|
|
Verbose: true,
|
|
},
|
|
expectedQuery: "type=container&type=image&type=volume&type=build-cache&verbose=1",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(fmt.Sprintf("options=%+v", tt.options), func(t *testing.T) {
|
|
client, err := New(WithMockClient(func(req *http.Request) (*http.Response, error) {
|
|
if err := assertRequestWithQuery(req, http.MethodGet, expectedURL, tt.expectedQuery); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return mockJSONResponse(http.StatusOK, nil, system.DiskUsage{})(req)
|
|
}))
|
|
assert.NilError(t, err)
|
|
_, err = client.DiskUsage(t.Context(), tt.options)
|
|
assert.NilError(t, err)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLegacyDiskUsage(t *testing.T) {
|
|
const legacyVersion = "1.51"
|
|
const expectedURL = "/system/df"
|
|
client, err := New(
|
|
WithAPIVersion(legacyVersion),
|
|
WithMockClient(func(req *http.Request) (*http.Response, error) {
|
|
if err := assertRequest(req, http.MethodGet, "/v"+legacyVersion+expectedURL); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return mockJSONResponse(http.StatusOK, nil, &legacyDiskUsage{
|
|
LayersSize: 4096,
|
|
Images: []image.Summary{},
|
|
})(req)
|
|
}))
|
|
assert.NilError(t, err)
|
|
|
|
du, err := client.DiskUsage(t.Context(), DiskUsageOptions{})
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, du.Images.ActiveCount, int64(0))
|
|
assert.Equal(t, du.Images.TotalCount, int64(0))
|
|
assert.Equal(t, du.Images.Reclaimable, int64(0))
|
|
assert.Equal(t, du.Images.TotalSize, int64(4096))
|
|
assert.Equal(t, len(du.Images.Items), 0)
|
|
}
|
|
|
|
func TestImageDiskUsageFromLegacyAPI(t *testing.T) {
|
|
const legacyVersion = "1.51"
|
|
const expectedURL = "/system/df"
|
|
|
|
tests := []struct {
|
|
name string
|
|
mockResponse *legacyDiskUsage
|
|
expectedActiveCount int64
|
|
expectedTotalCount int64
|
|
expectedReclaimable int64
|
|
expectedTotalSize int64
|
|
}{
|
|
{
|
|
name: "no images",
|
|
mockResponse: &legacyDiskUsage{
|
|
LayersSize: 0,
|
|
Images: []image.Summary{},
|
|
},
|
|
expectedActiveCount: 0,
|
|
expectedTotalCount: 0,
|
|
expectedReclaimable: 0,
|
|
expectedTotalSize: 0,
|
|
},
|
|
{
|
|
name: "images with no containers",
|
|
mockResponse: &legacyDiskUsage{
|
|
LayersSize: 8192,
|
|
Images: []image.Summary{
|
|
{ID: "image1", Size: 4096, SharedSize: 0, Containers: 0},
|
|
{ID: "image2", Size: 4096, SharedSize: 0, Containers: 0},
|
|
},
|
|
},
|
|
expectedActiveCount: 0,
|
|
expectedTotalCount: 2,
|
|
expectedReclaimable: 8192,
|
|
expectedTotalSize: 8192,
|
|
},
|
|
{
|
|
name: "images with containers",
|
|
mockResponse: &legacyDiskUsage{
|
|
LayersSize: 12288,
|
|
Images: []image.Summary{
|
|
{ID: "image1", Size: 4096, SharedSize: 0, Containers: 2},
|
|
{ID: "image2", Size: 2048, SharedSize: 0, Containers: 0},
|
|
{ID: "image3", Size: 6144, SharedSize: 0, Containers: 1},
|
|
},
|
|
},
|
|
expectedActiveCount: 2,
|
|
expectedTotalCount: 3,
|
|
expectedReclaimable: 2048,
|
|
expectedTotalSize: 12288,
|
|
},
|
|
{
|
|
name: "images with shared size",
|
|
mockResponse: &legacyDiskUsage{
|
|
LayersSize: 15360,
|
|
Images: []image.Summary{
|
|
{ID: "image1", Size: 4096, SharedSize: 1024, Containers: 1},
|
|
{ID: "image2", Size: 8192, SharedSize: 2048, Containers: 0},
|
|
{ID: "image3", Size: 3072, SharedSize: 1024, Containers: 0},
|
|
},
|
|
},
|
|
expectedActiveCount: 1,
|
|
expectedTotalCount: 3,
|
|
expectedReclaimable: 8192, // (8192-2048) + (3072-1024)
|
|
expectedTotalSize: 15360,
|
|
},
|
|
{
|
|
name: "multiplatform image with an image index",
|
|
mockResponse: &legacyDiskUsage{
|
|
LayersSize: 4096,
|
|
Images: []image.Summary{
|
|
{ID: "image1", Size: 4096, SharedSize: 0, Containers: 0, Descriptor: &ocispec.Descriptor{MediaType: ocispec.MediaTypeImageIndex, Size: 512}},
|
|
},
|
|
},
|
|
expectedActiveCount: 0,
|
|
expectedTotalCount: 1,
|
|
expectedReclaimable: 4096, // (4096 - 0)
|
|
expectedTotalSize: 4096,
|
|
},
|
|
{
|
|
name: "multiplatform image with a Docker distribution manifest",
|
|
mockResponse: &legacyDiskUsage{
|
|
LayersSize: 4096,
|
|
Images: []image.Summary{
|
|
{ID: "image1", Size: 4096, SharedSize: 0, Containers: 0, Descriptor: &ocispec.Descriptor{MediaType: "application/vnd.docker.distribution.manifest.v2+json", Size: 427}},
|
|
},
|
|
},
|
|
expectedActiveCount: 0,
|
|
expectedTotalCount: 1,
|
|
expectedReclaimable: 4096, // (4096 - 0)
|
|
expectedTotalSize: 4096,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
client, err := New(
|
|
WithAPIVersion(legacyVersion),
|
|
WithMockClient(func(req *http.Request) (*http.Response, error) {
|
|
if err := assertRequest(req, http.MethodGet, "/v"+legacyVersion+expectedURL); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return mockJSONResponse(http.StatusOK, nil, tt.mockResponse)(req)
|
|
}))
|
|
assert.NilError(t, err)
|
|
|
|
du, err := client.DiskUsage(t.Context(), DiskUsageOptions{Images: true})
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, du.Images.ActiveCount, tt.expectedActiveCount, tt.name)
|
|
assert.Equal(t, du.Images.TotalCount, tt.expectedTotalCount, tt.name)
|
|
assert.Equal(t, du.Images.Reclaimable, tt.expectedReclaimable, tt.name)
|
|
assert.Equal(t, du.Images.TotalSize, tt.expectedTotalSize, tt.name)
|
|
assert.Equal(t, len(du.Images.Items), len(tt.mockResponse.Images), tt.name)
|
|
})
|
|
}
|
|
}
|