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
SetContainerConfigReferences(name string, refs []*swarm.ConfigReference) 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
DaemonJoinsCluster(provider cluster.Provider)
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
// time.
type byCreated []*imagetypes.Summary
type byCreated []imagetypes.Summary
func (r byCreated) Len() int { return len(r) }
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 "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 {
return nil, err
}
@@ -150,7 +150,7 @@ func (i *ImageService) Images(ctx context.Context, opts imagebackend.ListOptions
eg.SetLimit(runtime.NumCPU() * 2)
var (
summaries = make([]*imagetypes.Summary, 0, len(imgs))
summaries = make([]imagetypes.Summary, 0, len(imgs))
root []*[]digest.Digest
layers map[digest.Digest]int
)
@@ -174,7 +174,7 @@ func (i *ImageService) Images(ctx context.Context, opts imagebackend.ListOptions
image.Manifests = nil
}
resultsMut.Lock()
summaries = append(summaries, image)
summaries = append(summaries, *image)
if opts.SharedSize {
root = append(root, &multiSummary.AllChainIDs)
@@ -452,11 +452,12 @@ func (i *ImageService) singlePlatformImage(ctx context.Context, contentStore con
return nil, err
}
snapshotUsage, err := imageManifest.SnapshotUsage(ctx, i.snapshotterService(i.snapshotter))
if err != nil {
var unpackedSize int64
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")
} else {
unpackedSize = snapshotUsage.Size
}
unpackedSize := snapshotUsage.Size
contentSize, err := imageManifest.PresentContentSize(ctx)
if err != nil {

View File

@@ -242,12 +242,12 @@ func TestImageList(t *testing.T) {
name string
images []c8dimages.Image
check func(*testing.T, []*imagetypes.Summary)
check func(*testing.T, []imagetypes.Summary)
}{
{
name: "one multi-layer image",
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))
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",
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))
if assert.Check(t, all[0].Descriptor != nil) {
@@ -290,7 +290,7 @@ func TestImageList(t *testing.T) {
{
name: "two images are two entries",
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))
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",
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))
},
},
{
name: "one good image, second has config as a target",
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))
sort.Slice(all, func(i, j int) bool {
@@ -349,7 +349,7 @@ func TestImageList(t *testing.T) {
{
name: "a non-container image manifest",
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))
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",
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.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/server/backend"
"github.com/moby/moby/v2/daemon/server/imagebackend"
"github.com/moby/moby/v2/internal/sliceutil"
"github.com/pkg/errors"
"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)
}
isActive := func(ctr *container.Summary) bool {
return ctr.State == container.StateRunning ||
ctr.State == container.StatePaused ||
ctr.State == container.StateRestarting
}
du := &backend.ContainerDiskUsage{
ActiveCount: int64(len(containers)),
TotalCount: int64(len(containers)),
}
for _, ctr := range containers {
du.TotalSize += ctr.SizeRw
if !isActive(ctr) {
du.Reclaimable += ctr.SizeRw
for i := range containers {
du.TotalSize += containers[i].SizeRw
switch containers[i].State {
case container.StateRunning, container.StatePaused, container.StateRestarting:
// active
default:
du.Reclaimable += containers[i].SizeRw
du.ActiveCount--
}
// Remove image manifest descriptor from the result as it should not be included.
// https://github.com/moby/moby/pull/49407#discussion_r1954396666
ctr.ImageManifestDescriptor = nil
containers[i].ImageManifestDescriptor = nil
}
if verbose {
du.Items = sliceutil.Deref(containers)
du.Items = containers
}
return du, nil
@@ -94,7 +90,7 @@ func (daemon *Daemon) imageDiskUsage(ctx context.Context, verbose bool) (*backen
}
if verbose {
du.Items = sliceutil.Deref(images)
du.Items = images
}
return du, nil

View File

@@ -33,7 +33,7 @@ type ImageService interface {
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
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)
CountImages(ctx context.Context) int
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
// time.
type byCreated []*imagetypes.Summary
type byCreated []imagetypes.Summary
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) Less(i, j int) bool { return r[i].Created < r[j].Created }
// 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 {
return nil, err
}
@@ -103,8 +103,8 @@ func (i *ImageService) Images(ctx context.Context, opts imagebackend.ListOptions
}
var (
summaries = make([]*imagetypes.Summary, 0, len(selectedImages))
summaryMap = make(map[*image.Image]*imagetypes.Summary, len(selectedImages))
summaries = make([]imagetypes.Summary, 0, len(selectedImages))
summaryMap = make(map[*image.Image]imagetypes.Summary, len(selectedImages))
allContainers []*container.Container
)
for id, img := range selectedImages {
@@ -210,8 +210,8 @@ func (i *ImageService) Images(ctx context.Context, opts imagebackend.ListOptions
}
}
summary.Containers = containersCount
summaryMap[img] = summary
summaries = append(summaries, summary)
summaryMap[img] = *summary
summaries = append(summaries, *summary)
}
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.
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 {
return nil, err
}
@@ -127,7 +127,7 @@ func (daemon *Daemon) Containers(ctx context.Context, config *backend.ContainerL
// shortcut if there are no containers
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
@@ -140,7 +140,7 @@ func (daemon *Daemon) Containers(ctx context.Context, config *backend.ContainerL
}
resultsMut := sync.Mutex{}
results := make([]*containertypes.Summary, numContainers)
results := make([]containertypes.Summary, numContainers)
g, ctx := errgroup.WithContext(ctx)
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
// same order as containerList above).
resultsMut.Lock()
results[idx] = newC
results[idx] = *newC
resultsMut.Unlock()
return nil

View File

@@ -70,7 +70,7 @@ func setupContainerWithName(t *testing.T, name string, daemon *Daemon) *containe
return c
}
func containerListContainsName(containers []*containertypes.Summary, name string) bool {
func containerListContainsName(containers []containertypes.Summary, name string) bool {
for _, ctr := range containers {
for _, containerName := range ctr.Names {
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)
ContainerStats(ctx context.Context, name string, config *backend.ContainerStatsConfig) 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.

View File

@@ -101,23 +101,23 @@ func (c *containerRouter) getContainersJSON(ctx context.Context, w http.Response
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"),
Size: httputils.BoolValue(r, "size"),
Since: r.Form.Get("since"),
Before: r.Form.Get("before"),
Limit: limit,
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 {
return err
}
@@ -125,22 +125,22 @@ func (c *containerRouter) getContainersJSON(ctx context.Context, w http.Response
version := httputils.VersionFromContext(ctx)
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.
c.HostConfig.Annotations = nil
containers[i].HostConfig.Annotations = nil
}
}
if versions.LessThan(version, "1.48") {
// ImageManifestDescriptor information was added in API 1.48
for _, c := range containers {
c.ImageManifestDescriptor = nil
for i := range containers {
containers[i].ImageManifestDescriptor = nil
}
}
if versions.LessThan(version, "1.52") {
for _, c := range containers {
c.Health = nil
for i := range containers {
containers[i].Health = nil
}
}

View File

@@ -24,7 +24,7 @@ type Backend interface {
type imageBackend interface {
ImageDelete(ctx context.Context, imageRef string, options imagebackend.RemoveOptions) ([]image.DeleteResponse, 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)
ImageInspect(ctx context.Context, refOrID string, options imagebackend.ImageInspectOpts) (*imagebackend.InspectData, 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")
noDescriptor := versions.LessThan(version, "1.48")
noContainers := versions.LessThan(version, "1.51")
for _, img := range images {
for i := range images {
if useNone {
if len(img.RepoTags) == 0 && len(img.RepoDigests) == 0 {
img.RepoTags = append(img.RepoTags, "<none>:<none>")
img.RepoDigests = append(img.RepoDigests, "<none>@<none>")
if len(images[i].RepoTags) == 0 && len(images[i].RepoDigests) == 0 {
images[i].RepoTags = append(images[i].RepoTags, "<none>:<none>")
images[i].RepoDigests = append(images[i].RepoDigests, "<none>@<none>")
}
} else {
if img.RepoTags == nil {
img.RepoTags = []string{}
if images[i].RepoTags == nil {
images[i].RepoTags = []string{}
}
if img.RepoDigests == nil {
img.RepoDigests = []string{}
if images[i].RepoDigests == nil {
images[i].RepoDigests = []string{}
}
}
if noDescriptor {
img.Descriptor = nil
images[i].Descriptor = nil
}
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) {
v := &volumeRouter{
backend: &fakeVolumeBackend{
volumes: map[string]*volume.Volume{
volumes: map[string]volume.Volume{
"volume1": {
Name: "volume1",
},
@@ -99,7 +99,7 @@ func TestGetVolumeByNameFoundSwarm(t *testing.T) {
cluster: &fakeClusterBackend{
swarm: true,
manager: true,
volumes: map[string]*volume.Volume{
volumes: map[string]volume.Volume{
"volume1": {
Name: "volume1",
},
@@ -114,7 +114,7 @@ func TestGetVolumeByNameFoundSwarm(t *testing.T) {
func TestListVolumes(t *testing.T) {
v := &volumeRouter{
backend: &fakeVolumeBackend{
volumes: map[string]*volume.Volume{
volumes: map[string]volume.Volume{
"v1": {Name: "v1"},
"v2": {Name: "v2"},
},
@@ -122,7 +122,7 @@ func TestListVolumes(t *testing.T) {
cluster: &fakeClusterBackend{
swarm: true,
manager: true,
volumes: map[string]*volume.Volume{
volumes: map[string]volume.Volume{
"v3": {Name: "v3"},
"v4": {Name: "v4"},
},
@@ -142,7 +142,7 @@ func TestListVolumes(t *testing.T) {
func TestListVolumesNoSwarm(t *testing.T) {
v := &volumeRouter{
backend: &fakeVolumeBackend{
volumes: map[string]*volume.Volume{
volumes: map[string]volume.Volume{
"v1": {Name: "v1"},
"v2": {Name: "v2"},
},
@@ -157,7 +157,7 @@ func TestListVolumesNoSwarm(t *testing.T) {
func TestListVolumesNoManager(t *testing.T) {
v := &volumeRouter{
backend: &fakeVolumeBackend{
volumes: map[string]*volume.Volume{
volumes: map[string]volume.Volume{
"v1": {Name: "v1"},
"v2": {Name: "v2"},
},
@@ -319,7 +319,7 @@ func TestUpdateVolume(t *testing.T) {
c := &fakeClusterBackend{
swarm: true,
manager: true,
volumes: map[string]*volume.Volume{
volumes: map[string]volume.Volume{
"vol1": {
Name: "vo1",
ClusterVolume: &volume.ClusterVolume{
@@ -387,7 +387,7 @@ func TestUpdateVolumeNotFound(t *testing.T) {
c := &fakeClusterBackend{
swarm: true,
manager: true,
volumes: map[string]*volume.Volume{},
volumes: map[string]volume.Volume{},
}
v := &volumeRouter{
@@ -416,7 +416,7 @@ func TestUpdateVolumeNotFound(t *testing.T) {
func TestVolumeRemove(t *testing.T) {
b := &fakeVolumeBackend{
volumes: map[string]*volume.Volume{
volumes: map[string]volume.Volume{
"vol1": {
Name: "vol1",
},
@@ -443,7 +443,7 @@ func TestVolumeRemoveSwarm(t *testing.T) {
c := &fakeClusterBackend{
swarm: true,
manager: true,
volumes: map[string]*volume.Volume{
volumes: map[string]volume.Volume{
"vol1": {
Name: "vol1",
ClusterVolume: &volume.ClusterVolume{},
@@ -501,7 +501,7 @@ func TestVolumeRemoveNotFoundNoManager(t *testing.T) {
func TestVolumeRemoveFoundNoSwarm(t *testing.T) {
b := &fakeVolumeBackend{
volumes: map[string]*volume.Volume{
volumes: map[string]volume.Volume{
"vol1": {
Name: "vol1",
},
@@ -525,7 +525,7 @@ func TestVolumeRemoveFoundNoSwarm(t *testing.T) {
func TestVolumeRemoveNoSwarmInUse(t *testing.T) {
b := &fakeVolumeBackend{
volumes: map[string]*volume.Volume{
volumes: map[string]volume.Volume{
"inuse": {
Name: "inuse",
},
@@ -551,7 +551,7 @@ func TestVolumeRemoveSwarmForce(t *testing.T) {
c := &fakeClusterBackend{
swarm: true,
manager: true,
volumes: map[string]*volume.Volume{
volumes: map[string]volume.Volume{
"vol1": {
Name: "vol1",
ClusterVolume: &volume.ClusterVolume{},
@@ -586,20 +586,20 @@ func TestVolumeRemoveSwarmForce(t *testing.T) {
}
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) {
var volumes []volume.Volume
for _, v := range b.volumes {
volumes = append(volumes, *v)
volumes = append(volumes, v)
}
return volumes, nil, nil
}
func (b *fakeVolumeBackend) Get(_ context.Context, name string, _ ...opts.GetOption) (*volume.Volume, error) {
if v, ok := b.volumes[name]; ok {
return v, nil
return &v, nil
}
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")
}
v := &volume.Volume{
v := volume.Volume{
Name: name,
Driver: driverName,
}
if b.volumes == nil {
b.volumes = map[string]*volume.Volume{
b.volumes = map[string]volume.Volume{
name: v,
}
} else {
b.volumes[name] = v
}
return v, nil
return &v, nil
}
func (b *fakeVolumeBackend) Remove(_ context.Context, name string, o ...opts.RemoveOption) error {
@@ -652,7 +652,7 @@ type fakeClusterBackend struct {
swarm bool
manager bool
idCount int
volumes map[string]*volume.Volume
volumes map[string]volume.Volume
}
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 {
return *v, nil
return v, nil
}
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
for _, v := range c.volumes {
volumes = append(volumes, *v)
volumes = append(volumes, v)
}
return volumes, nil
}
@@ -702,29 +702,28 @@ func (c *fakeClusterBackend) CreateVolume(volumeCreate volume.CreateRequest) (*v
return nil, errors.New("already exists")
}
v := &volume.Volume{
v := volume.Volume{
Name: volumeCreate.Name,
Driver: volumeCreate.Driver,
Labels: volumeCreate.Labels,
Options: volumeCreate.DriverOpts,
Scope: "global",
}
v.ClusterVolume = &volume.ClusterVolume{
ID: fmt.Sprintf("cluster_%d", c.idCount),
Spec: *volumeCreate.ClusterVolumeSpec,
ClusterVolume: &volume.ClusterVolume{
ID: fmt.Sprintf("cluster_%d", c.idCount),
Spec: *volumeCreate.ClusterVolumeSpec,
},
}
c.idCount = c.idCount + 1
if c.volumes == nil {
c.volumes = map[string]*volume.Volume{
c.volumes = map[string]volume.Volume{
v.Name: v,
}
} else {
c.volumes[v.Name] = v
}
return v, nil
return &v, nil
}
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) {
ctx := setupTest(t)
annotations := map[string]string{
"foo": "bar",
"io.kubernetes.docker.type": "container",
}
testcases := []struct {
apiVersion 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 {
t.Run(fmt.Sprintf("run with version v%s", tc.apiVersion), func(t *testing.T) {
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})
list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{