github.com/moby/docker@v26.1.3+incompatible/integration/image/list_test.go (about)

     1  package image // import "github.com/docker/docker/integration/image"
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  	"time"
     8  
     9  	containertypes "github.com/docker/docker/api/types/container"
    10  	"github.com/docker/docker/api/types/filters"
    11  	"github.com/docker/docker/api/types/image"
    12  	"github.com/docker/docker/integration/internal/container"
    13  	"github.com/docker/docker/internal/testutils/specialimage"
    14  	"github.com/docker/docker/testutil"
    15  	"github.com/docker/docker/testutil/daemon"
    16  	"github.com/google/go-cmp/cmp/cmpopts"
    17  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    18  	"gotest.tools/v3/assert"
    19  	is "gotest.tools/v3/assert/cmp"
    20  	"gotest.tools/v3/skip"
    21  )
    22  
    23  // Regression : #38171
    24  func TestImagesFilterMultiReference(t *testing.T) {
    25  	ctx := setupTest(t)
    26  
    27  	client := testEnv.APIClient()
    28  
    29  	name := strings.ToLower(t.Name())
    30  	repoTags := []string{
    31  		name + ":v1",
    32  		name + ":v2",
    33  		name + ":v3",
    34  		name + ":v4",
    35  	}
    36  
    37  	for _, repoTag := range repoTags {
    38  		err := client.ImageTag(ctx, "busybox:latest", repoTag)
    39  		assert.NilError(t, err)
    40  	}
    41  
    42  	filter := filters.NewArgs()
    43  	filter.Add("reference", repoTags[0])
    44  	filter.Add("reference", repoTags[1])
    45  	filter.Add("reference", repoTags[2])
    46  	options := image.ListOptions{
    47  		Filters: filter,
    48  	}
    49  	images, err := client.ImageList(ctx, options)
    50  	assert.NilError(t, err)
    51  
    52  	assert.Assert(t, is.Len(images, 1))
    53  	assert.Check(t, is.Len(images[0].RepoTags, 3))
    54  	for _, repoTag := range images[0].RepoTags {
    55  		if repoTag != repoTags[0] && repoTag != repoTags[1] && repoTag != repoTags[2] {
    56  			t.Errorf("list images doesn't match any repoTag we expected, repoTag: %s", repoTag)
    57  		}
    58  	}
    59  }
    60  
    61  func TestImagesFilterUntil(t *testing.T) {
    62  	ctx := setupTest(t)
    63  
    64  	client := testEnv.APIClient()
    65  
    66  	name := strings.ToLower(t.Name())
    67  	ctr := container.Create(ctx, t, client, container.WithName(name))
    68  
    69  	imgs := make([]string, 5)
    70  	for i := range imgs {
    71  		if i > 0 {
    72  			// Make sure each image has a distinct timestamp.
    73  			time.Sleep(time.Millisecond)
    74  		}
    75  		id, err := client.ContainerCommit(ctx, ctr, containertypes.CommitOptions{Reference: fmt.Sprintf("%s:v%d", name, i)})
    76  		assert.NilError(t, err)
    77  		imgs[i] = id.ID
    78  	}
    79  
    80  	olderImage, _, err := client.ImageInspectWithRaw(ctx, imgs[2])
    81  	assert.NilError(t, err)
    82  	olderUntil := olderImage.Created
    83  
    84  	laterImage, _, err := client.ImageInspectWithRaw(ctx, imgs[3])
    85  	assert.NilError(t, err)
    86  	laterUntil := laterImage.Created
    87  
    88  	filter := filters.NewArgs(
    89  		filters.Arg("since", imgs[0]),
    90  		filters.Arg("until", olderUntil),
    91  		filters.Arg("until", laterUntil),
    92  		filters.Arg("before", imgs[len(imgs)-1]),
    93  	)
    94  	list, err := client.ImageList(ctx, image.ListOptions{Filters: filter})
    95  	assert.NilError(t, err)
    96  
    97  	var listedIDs []string
    98  	for _, i := range list {
    99  		t.Logf("ImageList: ID=%v RepoTags=%v", i.ID, i.RepoTags)
   100  		listedIDs = append(listedIDs, i.ID)
   101  	}
   102  	assert.DeepEqual(t, listedIDs, imgs[1:2], cmpopts.SortSlices(func(a, b string) bool { return a < b }))
   103  }
   104  
   105  func TestImagesFilterBeforeSince(t *testing.T) {
   106  	ctx := setupTest(t)
   107  
   108  	client := testEnv.APIClient()
   109  
   110  	name := strings.ToLower(t.Name())
   111  	ctr := container.Create(ctx, t, client, container.WithName(name))
   112  
   113  	imgs := make([]string, 5)
   114  	for i := range imgs {
   115  		if i > 0 {
   116  			// Make sure each image has a distinct timestamp.
   117  			time.Sleep(time.Millisecond)
   118  		}
   119  		id, err := client.ContainerCommit(ctx, ctr, containertypes.CommitOptions{Reference: fmt.Sprintf("%s:v%d", name, i)})
   120  		assert.NilError(t, err)
   121  		imgs[i] = id.ID
   122  	}
   123  
   124  	filter := filters.NewArgs(
   125  		filters.Arg("since", imgs[0]),
   126  		filters.Arg("before", imgs[len(imgs)-1]),
   127  	)
   128  	list, err := client.ImageList(ctx, image.ListOptions{Filters: filter})
   129  	assert.NilError(t, err)
   130  
   131  	var listedIDs []string
   132  	for _, i := range list {
   133  		t.Logf("ImageList: ID=%v RepoTags=%v", i.ID, i.RepoTags)
   134  		listedIDs = append(listedIDs, i.ID)
   135  	}
   136  	// The ImageList API sorts the list by created timestamp... truncated to
   137  	// 1-second precision. Since all the images were created within
   138  	// milliseconds of each other, listedIDs is effectively unordered and
   139  	// the assertion must therefore be order-independent.
   140  	assert.DeepEqual(t, listedIDs, imgs[1:len(imgs)-1], cmpopts.SortSlices(func(a, b string) bool { return a < b }))
   141  }
   142  
   143  func TestAPIImagesFilters(t *testing.T) {
   144  	ctx := setupTest(t)
   145  	client := testEnv.APIClient()
   146  
   147  	for _, n := range []string{"utest:tag1", "utest/docker:tag2", "utest:5000/docker:tag3"} {
   148  		err := client.ImageTag(ctx, "busybox:latest", n)
   149  		assert.NilError(t, err)
   150  	}
   151  
   152  	testcases := []struct {
   153  		name             string
   154  		filters          []filters.KeyValuePair
   155  		expectedImages   int
   156  		expectedRepoTags int
   157  	}{
   158  		{
   159  			name:             "repository regex",
   160  			filters:          []filters.KeyValuePair{filters.Arg("reference", "utest*/*")},
   161  			expectedImages:   1,
   162  			expectedRepoTags: 2,
   163  		},
   164  		{
   165  			name:             "image name regex",
   166  			filters:          []filters.KeyValuePair{filters.Arg("reference", "utest*")},
   167  			expectedImages:   1,
   168  			expectedRepoTags: 1,
   169  		},
   170  		{
   171  			name:             "image name without a tag",
   172  			filters:          []filters.KeyValuePair{filters.Arg("reference", "utest")},
   173  			expectedImages:   1,
   174  			expectedRepoTags: 1,
   175  		},
   176  		{
   177  			name:             "registry port regex",
   178  			filters:          []filters.KeyValuePair{filters.Arg("reference", "*5000*/*")},
   179  			expectedImages:   1,
   180  			expectedRepoTags: 1,
   181  		},
   182  	}
   183  
   184  	for _, tc := range testcases {
   185  		tc := tc
   186  		t.Run(tc.name, func(t *testing.T) {
   187  			t.Parallel()
   188  
   189  			ctx := testutil.StartSpan(ctx, t)
   190  			images, err := client.ImageList(ctx, image.ListOptions{
   191  				Filters: filters.NewArgs(tc.filters...),
   192  			})
   193  			assert.Check(t, err)
   194  			assert.Assert(t, is.Len(images, tc.expectedImages))
   195  			assert.Check(t, is.Len(images[0].RepoTags, tc.expectedRepoTags))
   196  		})
   197  	}
   198  }
   199  
   200  // Verify that the size calculation operates on ChainIDs and not DiffIDs.
   201  // This test calls an image list with two images that share one, top layer.
   202  func TestAPIImagesListSizeShared(t *testing.T) {
   203  	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
   204  
   205  	ctx := setupTest(t)
   206  
   207  	daemon := daemon.New(t)
   208  	daemon.Start(t)
   209  	defer daemon.Stop(t)
   210  
   211  	client := daemon.NewClientT(t)
   212  
   213  	specialimage.Load(ctx, t, client, func(dir string) (*ocispec.Index, error) {
   214  		return specialimage.MultiLayerCustom(dir, "multilayer:latest", []specialimage.SingleFileLayer{
   215  			{Name: "bar", Content: []byte("2")},
   216  			{Name: "foo", Content: []byte("1")},
   217  		})
   218  	})
   219  
   220  	specialimage.Load(ctx, t, client, func(dir string) (*ocispec.Index, error) {
   221  		return specialimage.MultiLayerCustom(dir, "multilayer2:latest", []specialimage.SingleFileLayer{
   222  			{Name: "asdf", Content: []byte("3")},
   223  			{Name: "foo", Content: []byte("1")},
   224  		})
   225  	})
   226  
   227  	_, err := client.ImageList(ctx, image.ListOptions{SharedSize: true})
   228  	assert.NilError(t, err)
   229  }