daemon: reduce use of pointer-slices in backend

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2025-11-06 17:48:55 +01:00
parent feba59eccd
commit 0af2962fdd
14 changed files with 110 additions and 109 deletions

View File

@@ -51,7 +51,7 @@ type Backend interface {
SetContainerSecretReferences(name string, refs []*swarm.SecretReference) error SetContainerSecretReferences(name string, refs []*swarm.SecretReference) error
SetContainerConfigReferences(name string, refs []*swarm.ConfigReference) error SetContainerConfigReferences(name string, refs []*swarm.ConfigReference) error
SystemInfo(context.Context) (*system.Info, error) SystemInfo(context.Context) (*system.Info, error)
Containers(ctx context.Context, config *backend.ContainerListOptions) ([]*container.Summary, error) Containers(ctx context.Context, config *backend.ContainerListOptions) ([]container.Summary, error)
SetNetworkBootstrapKeys([]*networktypes.EncryptionKey) error SetNetworkBootstrapKeys([]*networktypes.EncryptionKey) error
DaemonJoinsCluster(provider cluster.Provider) DaemonJoinsCluster(provider cluster.Provider)
DaemonLeavesCluster() DaemonLeavesCluster()

View File

@@ -52,7 +52,7 @@ var acceptedImageFilterTags = map[string]bool{
// byCreated is a temporary type used to sort a list of images by creation // byCreated is a temporary type used to sort a list of images by creation
// time. // time.
type byCreated []*imagetypes.Summary type byCreated []imagetypes.Summary
func (r byCreated) Len() int { return len(r) } func (r byCreated) Len() int { return len(r) }
func (r byCreated) Swap(i, j int) { r[i], r[j] = r[j], r[i] } func (r byCreated) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
@@ -62,7 +62,7 @@ func (r byCreated) Less(i, j int) bool { return r[i].Created < r[j].Created }
// //
// 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 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 // TODO(thaJeztah): verify "Size" vs "VirtualSize" in images; see https://github.com/moby/moby/issues/43862
func (i *ImageService) Images(ctx context.Context, opts imagebackend.ListOptions) ([]*imagetypes.Summary, error) { func (i *ImageService) Images(ctx context.Context, opts imagebackend.ListOptions) ([]imagetypes.Summary, error) {
if err := opts.Filters.Validate(acceptedImageFilterTags); err != nil { if err := opts.Filters.Validate(acceptedImageFilterTags); err != nil {
return nil, err return nil, err
} }
@@ -150,7 +150,7 @@ func (i *ImageService) Images(ctx context.Context, opts imagebackend.ListOptions
eg.SetLimit(runtime.NumCPU() * 2) eg.SetLimit(runtime.NumCPU() * 2)
var ( var (
summaries = make([]*imagetypes.Summary, 0, len(imgs)) summaries = make([]imagetypes.Summary, 0, len(imgs))
root []*[]digest.Digest root []*[]digest.Digest
layers map[digest.Digest]int layers map[digest.Digest]int
) )
@@ -174,7 +174,7 @@ func (i *ImageService) Images(ctx context.Context, opts imagebackend.ListOptions
image.Manifests = nil image.Manifests = nil
} }
resultsMut.Lock() resultsMut.Lock()
summaries = append(summaries, image) summaries = append(summaries, *image)
if opts.SharedSize { if opts.SharedSize {
root = append(root, &multiSummary.AllChainIDs) root = append(root, &multiSummary.AllChainIDs)
@@ -452,11 +452,12 @@ func (i *ImageService) singlePlatformImage(ctx context.Context, contentStore con
return nil, err return nil, err
} }
snapshotUsage, err := imageManifest.SnapshotUsage(ctx, i.snapshotterService(i.snapshotter)) var unpackedSize int64
if err != nil { if snapshotUsage, err := imageManifest.SnapshotUsage(ctx, i.snapshotterService(i.snapshotter)); err != nil {
log.G(ctx).WithFields(log.Fields{"image": imageManifest.Name(), "error": err}).Warn("failed to calculate unpacked size of image") log.G(ctx).WithFields(log.Fields{"image": imageManifest.Name(), "error": err}).Warn("failed to calculate unpacked size of image")
} else {
unpackedSize = snapshotUsage.Size
} }
unpackedSize := snapshotUsage.Size
contentSize, err := imageManifest.PresentContentSize(ctx) contentSize, err := imageManifest.PresentContentSize(ctx)
if err != nil { if err != nil {

View File

@@ -242,12 +242,12 @@ func TestImageList(t *testing.T) {
name string name string
images []c8dimages.Image images []c8dimages.Image
check func(*testing.T, []*imagetypes.Summary) check func(*testing.T, []imagetypes.Summary)
}{ }{
{ {
name: "one multi-layer image", name: "one multi-layer image",
images: []c8dimages.Image{multilayer}, images: []c8dimages.Image{multilayer},
check: func(t *testing.T, all []*imagetypes.Summary) { check: func(t *testing.T, all []imagetypes.Summary) {
assert.Check(t, is.Len(all, 1)) assert.Check(t, is.Len(all, 1))
if assert.Check(t, all[0].Descriptor != nil) { if assert.Check(t, all[0].Descriptor != nil) {
@@ -264,7 +264,7 @@ func TestImageList(t *testing.T) {
{ {
name: "one image with two platforms is still one entry", name: "one image with two platforms is still one entry",
images: []c8dimages.Image{twoplatform}, images: []c8dimages.Image{twoplatform},
check: func(t *testing.T, all []*imagetypes.Summary) { check: func(t *testing.T, all []imagetypes.Summary) {
assert.Check(t, is.Len(all, 1)) assert.Check(t, is.Len(all, 1))
if assert.Check(t, all[0].Descriptor != nil) { if assert.Check(t, all[0].Descriptor != nil) {
@@ -290,7 +290,7 @@ func TestImageList(t *testing.T) {
{ {
name: "two images are two entries", name: "two images are two entries",
images: []c8dimages.Image{multilayer, twoplatform}, images: []c8dimages.Image{multilayer, twoplatform},
check: func(t *testing.T, all []*imagetypes.Summary) { check: func(t *testing.T, all []imagetypes.Summary) {
assert.Check(t, is.Len(all, 2)) assert.Check(t, is.Len(all, 2))
if assert.Check(t, all[0].Descriptor != nil) { if assert.Check(t, all[0].Descriptor != nil) {
@@ -319,14 +319,14 @@ func TestImageList(t *testing.T) {
{ {
name: "three images, one is an empty index", name: "three images, one is an empty index",
images: []c8dimages.Image{multilayer, emptyIndex, twoplatform}, images: []c8dimages.Image{multilayer, emptyIndex, twoplatform},
check: func(t *testing.T, all []*imagetypes.Summary) { check: func(t *testing.T, all []imagetypes.Summary) {
assert.Check(t, is.Len(all, 3)) assert.Check(t, is.Len(all, 3))
}, },
}, },
{ {
name: "one good image, second has config as a target", name: "one good image, second has config as a target",
images: []c8dimages.Image{multilayer, configTarget}, images: []c8dimages.Image{multilayer, configTarget},
check: func(t *testing.T, all []*imagetypes.Summary) { check: func(t *testing.T, all []imagetypes.Summary) {
assert.Check(t, is.Len(all, 2)) assert.Check(t, is.Len(all, 2))
sort.Slice(all, func(i, j int) bool { sort.Slice(all, func(i, j int) bool {
@@ -349,7 +349,7 @@ func TestImageList(t *testing.T) {
{ {
name: "a non-container image manifest", name: "a non-container image manifest",
images: []c8dimages.Image{textplain}, images: []c8dimages.Image{textplain},
check: func(t *testing.T, all []*imagetypes.Summary) { check: func(t *testing.T, all []imagetypes.Summary) {
assert.Check(t, is.Len(all, 1)) assert.Check(t, is.Len(all, 1))
if assert.Check(t, all[0].Descriptor != nil) { if assert.Check(t, all[0].Descriptor != nil) {
@@ -363,7 +363,7 @@ func TestImageList(t *testing.T) {
{ {
name: "multi-platform with no platforms available locally", name: "multi-platform with no platforms available locally",
images: []c8dimages.Image{missingMultiPlatform}, images: []c8dimages.Image{missingMultiPlatform},
check: func(t *testing.T, all []*imagetypes.Summary) { check: func(t *testing.T, all []imagetypes.Summary) {
assert.Assert(t, is.Len(all, 1)) assert.Assert(t, is.Len(all, 1))
assert.Check(t, is.Len(all[0].Manifests, 2)) assert.Check(t, is.Len(all[0].Manifests, 2))
}, },

View File

@@ -8,7 +8,6 @@ import (
"github.com/moby/moby/v2/daemon/internal/filters" "github.com/moby/moby/v2/daemon/internal/filters"
"github.com/moby/moby/v2/daemon/server/backend" "github.com/moby/moby/v2/daemon/server/backend"
"github.com/moby/moby/v2/daemon/server/imagebackend" "github.com/moby/moby/v2/daemon/server/imagebackend"
"github.com/moby/moby/v2/internal/sliceutil"
"github.com/pkg/errors" "github.com/pkg/errors"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
) )
@@ -26,30 +25,27 @@ func (daemon *Daemon) containerDiskUsage(ctx context.Context, verbose bool) (*ba
return nil, fmt.Errorf("failed to retrieve container list: %v", err) return nil, fmt.Errorf("failed to retrieve container list: %v", err)
} }
isActive := func(ctr *container.Summary) bool {
return ctr.State == container.StateRunning ||
ctr.State == container.StatePaused ||
ctr.State == container.StateRestarting
}
du := &backend.ContainerDiskUsage{ du := &backend.ContainerDiskUsage{
ActiveCount: int64(len(containers)), ActiveCount: int64(len(containers)),
TotalCount: int64(len(containers)), TotalCount: int64(len(containers)),
} }
for _, ctr := range containers { for i := range containers {
du.TotalSize += ctr.SizeRw du.TotalSize += containers[i].SizeRw
if !isActive(ctr) { switch containers[i].State {
du.Reclaimable += ctr.SizeRw case container.StateRunning, container.StatePaused, container.StateRestarting:
// active
default:
du.Reclaimable += containers[i].SizeRw
du.ActiveCount-- du.ActiveCount--
} }
// Remove image manifest descriptor from the result as it should not be included. // Remove image manifest descriptor from the result as it should not be included.
// https://github.com/moby/moby/pull/49407#discussion_r1954396666 // https://github.com/moby/moby/pull/49407#discussion_r1954396666
ctr.ImageManifestDescriptor = nil containers[i].ImageManifestDescriptor = nil
} }
if verbose { if verbose {
du.Items = sliceutil.Deref(containers) du.Items = containers
} }
return du, nil return du, nil
@@ -94,7 +90,7 @@ func (daemon *Daemon) imageDiskUsage(ctx context.Context, verbose bool) (*backen
} }
if verbose { if verbose {
du.Items = sliceutil.Deref(images) du.Items = images
} }
return du, nil return du, nil

View File

@@ -33,7 +33,7 @@ type ImageService interface {
ImageDelete(ctx context.Context, imageRef string, options imagebackend.RemoveOptions) ([]imagetype.DeleteResponse, error) ImageDelete(ctx context.Context, imageRef string, options imagebackend.RemoveOptions) ([]imagetype.DeleteResponse, error)
ExportImage(ctx context.Context, names []string, platformList []ocispec.Platform, outStream io.Writer) error ExportImage(ctx context.Context, names []string, platformList []ocispec.Platform, outStream io.Writer) error
LoadImage(ctx context.Context, inTar io.ReadCloser, platformList []ocispec.Platform, outStream io.Writer, quiet bool) error LoadImage(ctx context.Context, inTar io.ReadCloser, platformList []ocispec.Platform, outStream io.Writer, quiet bool) error
Images(ctx context.Context, opts imagebackend.ListOptions) ([]*imagetype.Summary, error) Images(ctx context.Context, opts imagebackend.ListOptions) ([]imagetype.Summary, error)
LogImageEvent(ctx context.Context, imageID, refName string, action events.Action) LogImageEvent(ctx context.Context, imageID, refName string, action events.Action)
CountImages(ctx context.Context) int CountImages(ctx context.Context) int
ImagePrune(ctx context.Context, pruneFilters filters.Args) (*imagetype.PruneReport, error) ImagePrune(ctx context.Context, pruneFilters filters.Args) (*imagetype.PruneReport, error)

View File

@@ -27,14 +27,14 @@ var acceptedImageFilterTags = map[string]bool{
// byCreated is a temporary type used to sort a list of images by creation // byCreated is a temporary type used to sort a list of images by creation
// time. // time.
type byCreated []*imagetypes.Summary type byCreated []imagetypes.Summary
func (r byCreated) Len() int { return len(r) } func (r byCreated) Len() int { return len(r) }
func (r byCreated) Swap(i, j int) { r[i], r[j] = r[j], r[i] } func (r byCreated) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r byCreated) Less(i, j int) bool { return r[i].Created < r[j].Created } func (r byCreated) Less(i, j int) bool { return r[i].Created < r[j].Created }
// Images returns a filtered list of images. // Images returns a filtered list of images.
func (i *ImageService) Images(ctx context.Context, opts imagebackend.ListOptions) ([]*imagetypes.Summary, error) { func (i *ImageService) Images(ctx context.Context, opts imagebackend.ListOptions) ([]imagetypes.Summary, error) {
if err := opts.Filters.Validate(acceptedImageFilterTags); err != nil { if err := opts.Filters.Validate(acceptedImageFilterTags); err != nil {
return nil, err return nil, err
} }
@@ -103,8 +103,8 @@ func (i *ImageService) Images(ctx context.Context, opts imagebackend.ListOptions
} }
var ( var (
summaries = make([]*imagetypes.Summary, 0, len(selectedImages)) summaries = make([]imagetypes.Summary, 0, len(selectedImages))
summaryMap = make(map[*image.Image]*imagetypes.Summary, len(selectedImages)) summaryMap = make(map[*image.Image]imagetypes.Summary, len(selectedImages))
allContainers []*container.Container allContainers []*container.Container
) )
for id, img := range selectedImages { for id, img := range selectedImages {
@@ -210,8 +210,8 @@ func (i *ImageService) Images(ctx context.Context, opts imagebackend.ListOptions
} }
} }
summary.Containers = containersCount summary.Containers = containersCount
summaryMap[img] = summary summaryMap[img] = *summary
summaries = append(summaries, summary) summaries = append(summaries, *summary)
} }
if opts.SharedSize { if opts.SharedSize {

View File

@@ -103,7 +103,7 @@ func (r byCreatedDescending) Less(i, j int) bool {
} }
// Containers returns the list of containers to show given the user's filtering. // Containers returns the list of containers to show given the user's filtering.
func (daemon *Daemon) Containers(ctx context.Context, config *backend.ContainerListOptions) ([]*containertypes.Summary, error) { func (daemon *Daemon) Containers(ctx context.Context, config *backend.ContainerListOptions) ([]containertypes.Summary, error) {
if err := config.Filters.Validate(acceptedPsFilterTags); err != nil { if err := config.Filters.Validate(acceptedPsFilterTags); err != nil {
return nil, err return nil, err
} }
@@ -127,7 +127,7 @@ func (daemon *Daemon) Containers(ctx context.Context, config *backend.ContainerL
// shortcut if there are no containers // shortcut if there are no containers
if numContainers == 0 { if numContainers == 0 {
return []*containertypes.Summary{}, nil return []containertypes.Summary{}, nil
} }
// Get the info for each container in the list; this can be slow so we // Get the info for each container in the list; this can be slow so we
@@ -140,7 +140,7 @@ func (daemon *Daemon) Containers(ctx context.Context, config *backend.ContainerL
} }
resultsMut := sync.Mutex{} resultsMut := sync.Mutex{}
results := make([]*containertypes.Summary, numContainers) results := make([]containertypes.Summary, numContainers)
g, ctx := errgroup.WithContext(ctx) g, ctx := errgroup.WithContext(ctx)
g.SetLimit(numWorkers) g.SetLimit(numWorkers)
@@ -174,7 +174,7 @@ func (daemon *Daemon) Containers(ctx context.Context, config *backend.ContainerL
// insert the result at the given index (so the output is in the // insert the result at the given index (so the output is in the
// same order as containerList above). // same order as containerList above).
resultsMut.Lock() resultsMut.Lock()
results[idx] = newC results[idx] = *newC
resultsMut.Unlock() resultsMut.Unlock()
return nil return nil

View File

@@ -70,7 +70,7 @@ func setupContainerWithName(t *testing.T, name string, daemon *Daemon) *containe
return c return c
} }
func containerListContainsName(containers []*containertypes.Summary, name string) bool { func containerListContainsName(containers []containertypes.Summary, name string) bool {
for _, ctr := range containers { for _, ctr := range containers {
for _, containerName := range ctr.Names { for _, containerName := range ctr.Names {
if containerName == name { if containerName == name {

View File

@@ -53,7 +53,7 @@ type monitorBackend interface {
ContainerLogs(ctx context.Context, name string, config *backend.ContainerLogsOptions) (msgs <-chan *backend.LogMessage, tty bool, err error) ContainerLogs(ctx context.Context, name string, config *backend.ContainerLogsOptions) (msgs <-chan *backend.LogMessage, tty bool, err error)
ContainerStats(ctx context.Context, name string, config *backend.ContainerStatsConfig) error ContainerStats(ctx context.Context, name string, config *backend.ContainerStatsConfig) error
ContainerTop(name string, psArgs string) (*container.TopResponse, error) ContainerTop(name string, psArgs string) (*container.TopResponse, error)
Containers(ctx context.Context, config *backend.ContainerListOptions) ([]*container.Summary, error) Containers(ctx context.Context, config *backend.ContainerListOptions) ([]container.Summary, error)
} }
// attachBackend includes function to implement to provide container attaching functionality. // attachBackend includes function to implement to provide container attaching functionality.

View File

@@ -101,23 +101,23 @@ func (c *containerRouter) getContainersJSON(ctx context.Context, w http.Response
return err return err
} }
config := &backend.ContainerListOptions{ var limit int
if tmpLimit := r.Form.Get("limit"); tmpLimit != "" {
val, err := strconv.Atoi(tmpLimit)
if err != nil {
return err
}
limit = val
}
containers, err := c.backend.Containers(ctx, &backend.ContainerListOptions{
All: httputils.BoolValue(r, "all"), All: httputils.BoolValue(r, "all"),
Size: httputils.BoolValue(r, "size"), Size: httputils.BoolValue(r, "size"),
Since: r.Form.Get("since"), Since: r.Form.Get("since"),
Before: r.Form.Get("before"), Before: r.Form.Get("before"),
Limit: limit,
Filters: filter, Filters: filter,
} })
if tmpLimit := r.Form.Get("limit"); tmpLimit != "" {
limit, err := strconv.Atoi(tmpLimit)
if err != nil {
return err
}
config.Limit = limit
}
containers, err := c.backend.Containers(ctx, config)
if err != nil { if err != nil {
return err return err
} }
@@ -125,22 +125,22 @@ func (c *containerRouter) getContainersJSON(ctx context.Context, w http.Response
version := httputils.VersionFromContext(ctx) version := httputils.VersionFromContext(ctx)
if versions.LessThan(version, "1.46") { if versions.LessThan(version, "1.46") {
for _, c := range containers { for i := range containers {
// Ignore HostConfig.Annotations because it was added in API v1.46. // Ignore HostConfig.Annotations because it was added in API v1.46.
c.HostConfig.Annotations = nil containers[i].HostConfig.Annotations = nil
} }
} }
if versions.LessThan(version, "1.48") { if versions.LessThan(version, "1.48") {
// ImageManifestDescriptor information was added in API 1.48 // ImageManifestDescriptor information was added in API 1.48
for _, c := range containers { for i := range containers {
c.ImageManifestDescriptor = nil containers[i].ImageManifestDescriptor = nil
} }
} }
if versions.LessThan(version, "1.52") { if versions.LessThan(version, "1.52") {
for _, c := range containers { for i := range containers {
c.Health = nil containers[i].Health = nil
} }
} }

View File

@@ -24,7 +24,7 @@ type Backend interface {
type imageBackend interface { type imageBackend interface {
ImageDelete(ctx context.Context, imageRef string, options imagebackend.RemoveOptions) ([]image.DeleteResponse, error) ImageDelete(ctx context.Context, imageRef string, options imagebackend.RemoveOptions) ([]image.DeleteResponse, error)
ImageHistory(ctx context.Context, imageName string, platform *ocispec.Platform) ([]*image.HistoryResponseItem, error) ImageHistory(ctx context.Context, imageName string, platform *ocispec.Platform) ([]*image.HistoryResponseItem, error)
Images(ctx context.Context, opts imagebackend.ListOptions) ([]*image.Summary, error) Images(ctx context.Context, opts imagebackend.ListOptions) ([]image.Summary, error)
GetImage(ctx context.Context, refOrID string, options imagebackend.GetImageOpts) (*dockerimage.Image, error) GetImage(ctx context.Context, refOrID string, options imagebackend.GetImageOpts) (*dockerimage.Image, error)
ImageInspect(ctx context.Context, refOrID string, options imagebackend.ImageInspectOpts) (*imagebackend.InspectData, error) ImageInspect(ctx context.Context, refOrID string, options imagebackend.ImageInspectOpts) (*imagebackend.InspectData, error)
TagImage(ctx context.Context, id dockerimage.ID, newRef reference.Named) error TagImage(ctx context.Context, id dockerimage.ID, newRef reference.Named) error

View File

@@ -508,25 +508,25 @@ func (ir *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter,
useNone := versions.LessThan(version, "1.43") useNone := versions.LessThan(version, "1.43")
noDescriptor := versions.LessThan(version, "1.48") noDescriptor := versions.LessThan(version, "1.48")
noContainers := versions.LessThan(version, "1.51") noContainers := versions.LessThan(version, "1.51")
for _, img := range images { for i := range images {
if useNone { if useNone {
if len(img.RepoTags) == 0 && len(img.RepoDigests) == 0 { if len(images[i].RepoTags) == 0 && len(images[i].RepoDigests) == 0 {
img.RepoTags = append(img.RepoTags, "<none>:<none>") images[i].RepoTags = append(images[i].RepoTags, "<none>:<none>")
img.RepoDigests = append(img.RepoDigests, "<none>@<none>") images[i].RepoDigests = append(images[i].RepoDigests, "<none>@<none>")
} }
} else { } else {
if img.RepoTags == nil { if images[i].RepoTags == nil {
img.RepoTags = []string{} images[i].RepoTags = []string{}
} }
if img.RepoDigests == nil { if images[i].RepoDigests == nil {
img.RepoDigests = []string{} images[i].RepoDigests = []string{}
} }
} }
if noDescriptor { if noDescriptor {
img.Descriptor = nil images[i].Descriptor = nil
} }
if noContainers { if noContainers {
img.Containers = -1 images[i].Containers = -1
} }
} }

View File

@@ -80,7 +80,7 @@ func TestGetVolumeByNameNotFound(t *testing.T) {
func TestGetVolumeByNameFoundRegular(t *testing.T) { func TestGetVolumeByNameFoundRegular(t *testing.T) {
v := &volumeRouter{ v := &volumeRouter{
backend: &fakeVolumeBackend{ backend: &fakeVolumeBackend{
volumes: map[string]*volume.Volume{ volumes: map[string]volume.Volume{
"volume1": { "volume1": {
Name: "volume1", Name: "volume1",
}, },
@@ -99,7 +99,7 @@ func TestGetVolumeByNameFoundSwarm(t *testing.T) {
cluster: &fakeClusterBackend{ cluster: &fakeClusterBackend{
swarm: true, swarm: true,
manager: true, manager: true,
volumes: map[string]*volume.Volume{ volumes: map[string]volume.Volume{
"volume1": { "volume1": {
Name: "volume1", Name: "volume1",
}, },
@@ -114,7 +114,7 @@ func TestGetVolumeByNameFoundSwarm(t *testing.T) {
func TestListVolumes(t *testing.T) { func TestListVolumes(t *testing.T) {
v := &volumeRouter{ v := &volumeRouter{
backend: &fakeVolumeBackend{ backend: &fakeVolumeBackend{
volumes: map[string]*volume.Volume{ volumes: map[string]volume.Volume{
"v1": {Name: "v1"}, "v1": {Name: "v1"},
"v2": {Name: "v2"}, "v2": {Name: "v2"},
}, },
@@ -122,7 +122,7 @@ func TestListVolumes(t *testing.T) {
cluster: &fakeClusterBackend{ cluster: &fakeClusterBackend{
swarm: true, swarm: true,
manager: true, manager: true,
volumes: map[string]*volume.Volume{ volumes: map[string]volume.Volume{
"v3": {Name: "v3"}, "v3": {Name: "v3"},
"v4": {Name: "v4"}, "v4": {Name: "v4"},
}, },
@@ -142,7 +142,7 @@ func TestListVolumes(t *testing.T) {
func TestListVolumesNoSwarm(t *testing.T) { func TestListVolumesNoSwarm(t *testing.T) {
v := &volumeRouter{ v := &volumeRouter{
backend: &fakeVolumeBackend{ backend: &fakeVolumeBackend{
volumes: map[string]*volume.Volume{ volumes: map[string]volume.Volume{
"v1": {Name: "v1"}, "v1": {Name: "v1"},
"v2": {Name: "v2"}, "v2": {Name: "v2"},
}, },
@@ -157,7 +157,7 @@ func TestListVolumesNoSwarm(t *testing.T) {
func TestListVolumesNoManager(t *testing.T) { func TestListVolumesNoManager(t *testing.T) {
v := &volumeRouter{ v := &volumeRouter{
backend: &fakeVolumeBackend{ backend: &fakeVolumeBackend{
volumes: map[string]*volume.Volume{ volumes: map[string]volume.Volume{
"v1": {Name: "v1"}, "v1": {Name: "v1"},
"v2": {Name: "v2"}, "v2": {Name: "v2"},
}, },
@@ -319,7 +319,7 @@ func TestUpdateVolume(t *testing.T) {
c := &fakeClusterBackend{ c := &fakeClusterBackend{
swarm: true, swarm: true,
manager: true, manager: true,
volumes: map[string]*volume.Volume{ volumes: map[string]volume.Volume{
"vol1": { "vol1": {
Name: "vo1", Name: "vo1",
ClusterVolume: &volume.ClusterVolume{ ClusterVolume: &volume.ClusterVolume{
@@ -387,7 +387,7 @@ func TestUpdateVolumeNotFound(t *testing.T) {
c := &fakeClusterBackend{ c := &fakeClusterBackend{
swarm: true, swarm: true,
manager: true, manager: true,
volumes: map[string]*volume.Volume{}, volumes: map[string]volume.Volume{},
} }
v := &volumeRouter{ v := &volumeRouter{
@@ -416,7 +416,7 @@ func TestUpdateVolumeNotFound(t *testing.T) {
func TestVolumeRemove(t *testing.T) { func TestVolumeRemove(t *testing.T) {
b := &fakeVolumeBackend{ b := &fakeVolumeBackend{
volumes: map[string]*volume.Volume{ volumes: map[string]volume.Volume{
"vol1": { "vol1": {
Name: "vol1", Name: "vol1",
}, },
@@ -443,7 +443,7 @@ func TestVolumeRemoveSwarm(t *testing.T) {
c := &fakeClusterBackend{ c := &fakeClusterBackend{
swarm: true, swarm: true,
manager: true, manager: true,
volumes: map[string]*volume.Volume{ volumes: map[string]volume.Volume{
"vol1": { "vol1": {
Name: "vol1", Name: "vol1",
ClusterVolume: &volume.ClusterVolume{}, ClusterVolume: &volume.ClusterVolume{},
@@ -501,7 +501,7 @@ func TestVolumeRemoveNotFoundNoManager(t *testing.T) {
func TestVolumeRemoveFoundNoSwarm(t *testing.T) { func TestVolumeRemoveFoundNoSwarm(t *testing.T) {
b := &fakeVolumeBackend{ b := &fakeVolumeBackend{
volumes: map[string]*volume.Volume{ volumes: map[string]volume.Volume{
"vol1": { "vol1": {
Name: "vol1", Name: "vol1",
}, },
@@ -525,7 +525,7 @@ func TestVolumeRemoveFoundNoSwarm(t *testing.T) {
func TestVolumeRemoveNoSwarmInUse(t *testing.T) { func TestVolumeRemoveNoSwarmInUse(t *testing.T) {
b := &fakeVolumeBackend{ b := &fakeVolumeBackend{
volumes: map[string]*volume.Volume{ volumes: map[string]volume.Volume{
"inuse": { "inuse": {
Name: "inuse", Name: "inuse",
}, },
@@ -551,7 +551,7 @@ func TestVolumeRemoveSwarmForce(t *testing.T) {
c := &fakeClusterBackend{ c := &fakeClusterBackend{
swarm: true, swarm: true,
manager: true, manager: true,
volumes: map[string]*volume.Volume{ volumes: map[string]volume.Volume{
"vol1": { "vol1": {
Name: "vol1", Name: "vol1",
ClusterVolume: &volume.ClusterVolume{}, ClusterVolume: &volume.ClusterVolume{},
@@ -586,20 +586,20 @@ func TestVolumeRemoveSwarmForce(t *testing.T) {
} }
type fakeVolumeBackend struct { type fakeVolumeBackend struct {
volumes map[string]*volume.Volume volumes map[string]volume.Volume
} }
func (b *fakeVolumeBackend) List(_ context.Context, _ filters.Args) ([]volume.Volume, []string, error) { func (b *fakeVolumeBackend) List(_ context.Context, _ filters.Args) ([]volume.Volume, []string, error) {
var volumes []volume.Volume var volumes []volume.Volume
for _, v := range b.volumes { for _, v := range b.volumes {
volumes = append(volumes, *v) volumes = append(volumes, v)
} }
return volumes, nil, nil return volumes, nil, nil
} }
func (b *fakeVolumeBackend) Get(_ context.Context, name string, _ ...opts.GetOption) (*volume.Volume, error) { func (b *fakeVolumeBackend) Get(_ context.Context, name string, _ ...opts.GetOption) (*volume.Volume, error) {
if v, ok := b.volumes[name]; ok { if v, ok := b.volumes[name]; ok {
return v, nil return &v, nil
} }
return nil, errdefs.NotFound(fmt.Errorf("volume %s not found", name)) return nil, errdefs.NotFound(fmt.Errorf("volume %s not found", name))
} }
@@ -610,19 +610,19 @@ func (b *fakeVolumeBackend) Create(_ context.Context, name, driverName string, _
return nil, errors.New("already exists") return nil, errors.New("already exists")
} }
v := &volume.Volume{ v := volume.Volume{
Name: name, Name: name,
Driver: driverName, Driver: driverName,
} }
if b.volumes == nil { if b.volumes == nil {
b.volumes = map[string]*volume.Volume{ b.volumes = map[string]volume.Volume{
name: v, name: v,
} }
} else { } else {
b.volumes[name] = v b.volumes[name] = v
} }
return v, nil return &v, nil
} }
func (b *fakeVolumeBackend) Remove(_ context.Context, name string, o ...opts.RemoveOption) error { func (b *fakeVolumeBackend) Remove(_ context.Context, name string, o ...opts.RemoveOption) error {
@@ -652,7 +652,7 @@ type fakeClusterBackend struct {
swarm bool swarm bool
manager bool manager bool
idCount int idCount int
volumes map[string]*volume.Volume volumes map[string]volume.Volume
} }
func (c *fakeClusterBackend) checkSwarm() error { func (c *fakeClusterBackend) checkSwarm() error {
@@ -675,7 +675,7 @@ func (c *fakeClusterBackend) GetVolume(nameOrID string) (volume.Volume, error) {
} }
if v, ok := c.volumes[nameOrID]; ok { if v, ok := c.volumes[nameOrID]; ok {
return *v, nil return v, nil
} }
return volume.Volume{}, errdefs.NotFound(fmt.Errorf("volume %s not found", nameOrID)) return volume.Volume{}, errdefs.NotFound(fmt.Errorf("volume %s not found", nameOrID))
} }
@@ -687,7 +687,7 @@ func (c *fakeClusterBackend) GetVolumes(_ volumebackend.ListOptions) ([]volume.V
var volumes []volume.Volume var volumes []volume.Volume
for _, v := range c.volumes { for _, v := range c.volumes {
volumes = append(volumes, *v) volumes = append(volumes, v)
} }
return volumes, nil return volumes, nil
} }
@@ -702,29 +702,28 @@ func (c *fakeClusterBackend) CreateVolume(volumeCreate volume.CreateRequest) (*v
return nil, errors.New("already exists") return nil, errors.New("already exists")
} }
v := &volume.Volume{ v := volume.Volume{
Name: volumeCreate.Name, Name: volumeCreate.Name,
Driver: volumeCreate.Driver, Driver: volumeCreate.Driver,
Labels: volumeCreate.Labels, Labels: volumeCreate.Labels,
Options: volumeCreate.DriverOpts, Options: volumeCreate.DriverOpts,
Scope: "global", Scope: "global",
} ClusterVolume: &volume.ClusterVolume{
v.ClusterVolume = &volume.ClusterVolume{
ID: fmt.Sprintf("cluster_%d", c.idCount), ID: fmt.Sprintf("cluster_%d", c.idCount),
Spec: *volumeCreate.ClusterVolumeSpec, Spec: *volumeCreate.ClusterVolumeSpec,
},
} }
c.idCount = c.idCount + 1 c.idCount = c.idCount + 1
if c.volumes == nil { if c.volumes == nil {
c.volumes = map[string]*volume.Volume{ c.volumes = map[string]volume.Volume{
v.Name: v, v.Name: v,
} }
} else { } else {
c.volumes[v.Name] = v c.volumes[v.Name] = v
} }
return v, nil return &v, nil
} }
func (c *fakeClusterBackend) RemoveVolume(nameOrID string, force bool) error { func (c *fakeClusterBackend) RemoveVolume(nameOrID string, force bool) error {

View File

@@ -47,22 +47,27 @@ func TestContainerList(t *testing.T) {
func TestContainerList_Annotations(t *testing.T) { func TestContainerList_Annotations(t *testing.T) {
ctx := setupTest(t) ctx := setupTest(t)
annotations := map[string]string{
"foo": "bar",
"io.kubernetes.docker.type": "container",
}
testcases := []struct { testcases := []struct {
apiVersion string apiVersion string
expectedAnnotations map[string]string expectedAnnotations map[string]string
}{ }{
{apiVersion: "1.44", expectedAnnotations: nil}, {
{apiVersion: "1.46", expectedAnnotations: annotations}, apiVersion: "1.44",
expectedAnnotations: nil,
},
{
apiVersion: "1.46",
expectedAnnotations: map[string]string{
"foo": "bar",
"io.kubernetes.docker.type": "container",
},
},
} }
for _, tc := range testcases { for _, tc := range testcases {
t.Run(fmt.Sprintf("run with version v%s", tc.apiVersion), func(t *testing.T) { t.Run(fmt.Sprintf("run with version v%s", tc.apiVersion), func(t *testing.T) {
apiClient := request.NewAPIClient(t, client.WithAPIVersion(tc.apiVersion)) apiClient := request.NewAPIClient(t, client.WithAPIVersion(tc.apiVersion))
id := container.Create(ctx, t, apiClient, container.WithAnnotations(annotations)) id := container.Create(ctx, t, apiClient, container.WithAnnotations(tc.expectedAnnotations))
defer container.Remove(ctx, t, apiClient, id, client.ContainerRemoveOptions{Force: true}) defer container.Remove(ctx, t, apiClient, id, client.ContainerRemoveOptions{Force: true})
list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{ list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{