File: pull_test.go

package info (click to toggle)
golang-github-containers-common 0.64.1%2Bds1-2
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 5,932 kB
  • sloc: makefile: 132; sh: 111
file content (268 lines) | stat: -rw-r--r-- 10,519 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
//go:build !remote

package libimage

import (
	"context"
	"fmt"
	"os"
	goruntime "runtime"
	"testing"

	"github.com/containers/common/pkg/config"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestPull(t *testing.T) {
	runtime := testNewRuntime(t)
	ctx := context.Background()
	pullOptions := &PullOptions{}
	pullOptions.Writer = os.Stdout

	// Make sure that parsing errors of the daemon transport are returned
	// and that we do not fallthrough attempting to pull the specified
	// string as an image from a registry.
	_, err := runtime.Pull(ctx, "docker-daemon:alpine", config.PullPolicyAlways, pullOptions)
	require.Error(t, err, "return parsing error from daemon transport")

	for _, test := range []struct {
		input       string
		expectError bool
		numImages   int
		names       []string
	}{
		// DOCKER ARCHIVE
		{"docker-archive:testdata/docker-name-only.tar.xz", false, 1, []string{"localhost/pretty-empty:latest"}},
		{"docker-archive:testdata/docker-registry-name.tar.xz", false, 1, []string{"example.com/empty:latest"}},
		{"docker-archive:testdata/docker-two-names.tar.xz", false, 2, []string{"example.com/empty:latest", "localhost/pretty-empty:latest"}},
		{"docker-archive:testdata/docker-two-images.tar.xz", true, 0, nil}, // LOAD must be used here
		{"docker-archive:testdata/docker-unnamed.tar.xz", false, 1, []string{"ec9293436c2e66da44edb9efb8d41f6b13baf62283ebe846468bc992d76d7951"}},

		// OCI ARCHIVE
		{"oci-archive:testdata/oci-name-only.tar.gz", false, 1, []string{"localhost/pretty-empty:latest"}},
		{"oci-archive:testdata/oci-non-docker-name.tar.gz", true, 0, nil},
		{"oci-archive:testdata/oci-registry-name.tar.gz", false, 1, []string{"example.com/empty:latest"}},
		{"oci-archive:testdata/oci-unnamed.tar.gz", false, 1, []string{"5c8aca8137ac47e84c69ae93ce650ce967917cc001ba7aad5494073fac75b8b6"}},

		// REGISTRY
		{"alpine", false, 1, []string{"docker.io/library/alpine:latest"}},
		{"docker://alpine", false, 1, []string{"docker.io/library/alpine:latest"}},
		{"docker.io/library/alpine", false, 1, []string{"docker.io/library/alpine:latest"}},
		{"docker://docker.io/library/alpine", false, 1, []string{"docker.io/library/alpine:latest"}},
		{"quay.io/libpod/alpine@sha256:634a8f35b5f16dcf4aaa0822adc0b1964bb786fca12f6831de8ddc45e5986a00", false, 1, []string{"quay.io/libpod/alpine@sha256:634a8f35b5f16dcf4aaa0822adc0b1964bb786fca12f6831de8ddc45e5986a00"}},
		{"quay.io/libpod/alpine:pleaseignorethistag@sha256:634a8f35b5f16dcf4aaa0822adc0b1964bb786fca12f6831de8ddc45e5986a00", false, 1, []string{"quay.io/libpod/alpine@sha256:634a8f35b5f16dcf4aaa0822adc0b1964bb786fca12f6831de8ddc45e5986a00"}},

		// DIR
		{"dir:testdata/scratch-dir-5pec!@L", false, 1, []string{"61e17f84d763cc086d43c67dcf4cdbd69f9224c74e961c53b589b70499eac443"}},
	} {
		pulledImages, err := runtime.Pull(ctx, test.input, config.PullPolicyAlways, pullOptions)
		if test.expectError {
			require.Error(t, err, test.input)
			continue
		}
		require.NoError(t, err, test.input)
		require.Len(t, pulledImages, test.numImages)

		// Now lookup an image with the expected name and compare IDs.
		image, resolvedName, err := runtime.LookupImage(test.names[0], nil)
		require.NoError(t, err, test.input)
		require.Equal(t, test.names[0], resolvedName, fmt.Sprintf("%v", image.Names()))
		require.Equal(t, pulledImages[0].ID(), image.ID(), test.input)

		// Now remove the image.
		rmReports, rmErrors := runtime.RemoveImages(ctx, test.names, &RemoveImagesOptions{Force: true})
		require.Len(t, rmErrors, 0)
		require.Len(t, rmReports, 1)
		assert.Equal(t, image.ID(), rmReports[0].ID)
		assert.True(t, rmReports[0].Removed)
	}
}

func TestPullPlatforms(t *testing.T) {
	runtime := testNewRuntime(t)

	ctx := context.Background()
	pullOptions := &PullOptions{}
	pullOptions.Writer = os.Stdout

	localArch := goruntime.GOARCH
	localOS := goruntime.GOOS

	withTag := "quay.io/libpod/busybox:musl"

	pulledImages, err := runtime.Pull(ctx, withTag, config.PullPolicyAlways, pullOptions)
	require.NoError(t, err, "pull busybox")
	require.Len(t, pulledImages, 1)

	// Repulling with a bogus architecture should yield an error and not
	// choose the local image.
	pullOptions.Architecture = "bogus"
	_, err = runtime.Pull(ctx, withTag, config.PullPolicyNewer, pullOptions)
	require.Error(t, err, "pulling with a bogus architecture must fail even if there is a local image of another architecture")
	require.Contains(t, err.Error(), `no image found in manifest list for architecture "bogus"`)

	image, _, err := runtime.LookupImage(withTag, nil)
	require.NoError(t, err, "lookup busybox")
	require.NotNil(t, image, "lookup busybox")

	_, _, err = runtime.LookupImage("busybox", nil)
	require.Error(t, err, "untagged image resolves to non-existent :latest")

	image, _, err = runtime.LookupImage(withTag, &LookupImageOptions{Architecture: localArch})
	require.NoError(t, err, "lookup busybox - by local arch")
	require.NotNil(t, image, "lookup busybox - by local arch")

	image, _, err = runtime.LookupImage(withTag, &LookupImageOptions{OS: localOS})
	require.NoError(t, err, "lookup busybox - by local arch")
	require.NotNil(t, image, "lookup busybox - by local arch")

	_, _, err = runtime.LookupImage(withTag, &LookupImageOptions{Architecture: "bogus"})
	require.Error(t, err, "lookup busybox - bogus arch")

	_, _, err = runtime.LookupImage(withTag, &LookupImageOptions{OS: "bogus"})
	require.Error(t, err, "lookup busybox - bogus OS")

	pullOptions.Architecture = "arm"
	pulledImages, err = runtime.Pull(ctx, withTag, config.PullPolicyAlways, pullOptions)
	require.NoError(t, err, "pull busybox - arm")
	require.Len(t, pulledImages, 1)
	pullOptions.Architecture = ""

	image, _, err = runtime.LookupImage(withTag, &LookupImageOptions{Architecture: "arm"})
	require.NoError(t, err, "lookup busybox - by arm")
	require.NotNil(t, image, "lookup busybox - by arm")

	pullOptions.Architecture = "aarch64"
	pulledImages, err = runtime.Pull(ctx, withTag, config.PullPolicyAlways, pullOptions)
	require.NoError(t, err, "pull busybox - aarch64")
	require.Len(t, pulledImages, 1)
}

func TestPullPlatformsWithEmptyRegistriesConf(t *testing.T) {
	runtime := testNewRuntime(t, testNewRuntimeOptions{registriesConfPath: "/dev/null"})
	ctx := context.Background()
	pullOptions := &PullOptions{}
	pullOptions.Writer = os.Stdout

	localArch := goruntime.GOARCH
	localOS := goruntime.GOOS

	imageName := "quay.io/libpod/busybox"
	newTag := "crazy:train"

	pulledImages, err := runtime.Pull(ctx, imageName, config.PullPolicyAlways, pullOptions)
	require.NoError(t, err, "pull "+imageName)
	require.Len(t, pulledImages, 1)

	err = pulledImages[0].Tag(newTag)
	require.NoError(t, err, "tag")

	// See containers/podman/issues/12707: a custom platform will enforce
	// pulling via newer. Older versions enforced always which can lead to
	// errors.
	pullOptions.OS = localOS
	pullOptions.Architecture = localArch
	pulledImages, err = runtime.Pull(ctx, newTag, config.PullPolicyMissing, pullOptions)
	require.NoError(t, err, "pull "+newTag)
	require.Len(t, pulledImages, 1)
}

func TestPullPolicy(t *testing.T) {
	runtime := testNewRuntime(t)
	ctx := context.Background()
	pullOptions := &PullOptions{}

	pulledImages, err := runtime.Pull(ctx, "alpine", config.PullPolicyNever, pullOptions)
	require.Error(t, err, "Never pull different arch alpine")
	require.Nil(t, pulledImages, "lookup alpine")

	pulledImages, err = runtime.Pull(ctx, "alpine", config.PullPolicyNewer, pullOptions)
	require.NoError(t, err, "Newer pull different arch alpine")
	require.NotNil(t, pulledImages, "lookup alpine")

	pulledImages, err = runtime.Pull(ctx, "alpine", config.PullPolicyNever, pullOptions)
	require.NoError(t, err, "Never pull different arch alpine")
	require.NotNil(t, pulledImages, "lookup alpine")
}

func TestShortNameAndIDconflict(t *testing.T) {
	// Regression test for https://github.com/containers/podman/issues/12761
	runtime := testNewRuntime(t)
	ctx := context.Background()
	pullOptions := &PullOptions{}
	pullOptions.Writer = os.Stdout

	busybox, err := runtime.Pull(ctx, "busybox", config.PullPolicyAlways, pullOptions)
	require.NoError(t, err)
	require.Len(t, busybox, 1)

	alpine, err := runtime.Pull(ctx, "alpine", config.PullPolicyAlways, pullOptions)
	require.NoError(t, err)
	require.Len(t, alpine, 1)

	// Tag the alpine image with the first character of busybox's ID to
	// cause a conflict when looking up the image. The expected outcome is
	// that short names always have precedence of IDs.
	c := busybox[0].ID()[0:1]
	err = alpine[0].Tag(c)
	require.NoError(t, err, "tag")

	// Short name is selected over ID.
	img, _, err := runtime.LookupImage(c, nil)
	require.NoError(t, err)
	require.Equal(t, alpine[0].ID(), img.ID())

	// Not matching short name, so ID is selected.
	img, _, err = runtime.LookupImage(busybox[0].ID()[0:2], nil)
	require.NoError(t, err)
	require.Equal(t, busybox[0].ID(), img.ID())
}

func TestPullOCINoReference(t *testing.T) {
	// Exercise pulling from the OCI transport and make sure that a
	// specified reference is preserved in the image name.

	busybox := "quay.io/libpod/busybox:latest"
	runtime := testNewRuntime(t)
	ctx := context.Background()
	pullOptions := &PullOptions{}
	pullOptions.Writer = os.Stdout

	images, err := runtime.Pull(ctx, busybox, config.PullPolicyAlways, pullOptions)
	require.NoError(t, err)
	require.Len(t, images, 1)

	// Push one image without the optional reference
	ociPathNoRef := "oci:" + t.TempDir() + "noRef"
	_, err = runtime.Push(ctx, busybox, ociPathNoRef, nil)
	require.NoError(t, err)

	// Push another image _with_ the optional reference which allows for
	// preserving the name.
	ociPathWithRef := "oci:" + t.TempDir() + "withRef:" + busybox
	_, err = runtime.Push(ctx, busybox, ociPathWithRef, nil)
	require.NoError(t, err)

	_, errors := runtime.RemoveImages(ctx, []string{busybox}, nil)
	require.Nil(t, errors)

	images, err = runtime.Pull(ctx, ociPathNoRef, config.PullPolicyAlways, pullOptions)
	require.NoError(t, err)
	require.Len(t, images, 1)

	exists, err := runtime.Exists(busybox) // busybox does not exist
	require.NoError(t, err)
	require.False(t, exists)

	names := images[0].Names() // The image has no names (i.e., <none>)
	require.Nil(t, names)

	images, err = runtime.Pull(ctx, ociPathWithRef, config.PullPolicyAlways, pullOptions)
	require.NoError(t, err)
	require.Len(t, images, 1)

	exists, err = runtime.Exists(busybox) // busybox does exist now
	require.NoError(t, err)
	require.True(t, exists)
}