Skip to content

Commit 9a6426d

Browse files
committed
fix: save multiple images for the same image id to a tar archive
Suppose we try to save multiple container images with the same image ID but different image names into a tar archive using the nerdctl save command. When we then try to load container images from this tar archive using the nerdctl load command, not all container images will be loaded. This behavior is reported and the details are described in the following: - containerd#3806 Therefore, this PR resolves this issue. Signed-off-by: Hayato Kiwata <[email protected]>
1 parent 1259a55 commit 9a6426d

File tree

2 files changed

+59
-3
lines changed

2 files changed

+59
-3
lines changed

cmd/nerdctl/image/image_save_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,60 @@ func TestSave(t *testing.T) {
138138

139139
testCase.Run(t)
140140
}
141+
142+
// TestSaveMultipleImagesWithSameIDAndLoad tests https://github.com/containerd/nerdctl/issues/3806
143+
func TestSaveMultipleImagesWithSameIDAndLoad(t *testing.T) {
144+
testCase := nerdtest.Setup()
145+
146+
// This test relies on the fact that we can remove the common image, which definitely conflicts with others,
147+
// hence the private mode.
148+
// Further note though, that this will hide the fact this the save command could fail if some layers are missing.
149+
// See https://github.com/containerd/nerdctl/issues/3425 and others for details.
150+
testCase.Require = nerdtest.Private
151+
152+
if runtime.GOOS == "windows" {
153+
testCase.Require = nerdtest.IsFlaky("https://github.com/containerd/nerdctl/issues/3524")
154+
}
155+
156+
testCase.SubTests = []*test.Case{
157+
{
158+
Description: "Issue #3568 - Save multiple container images with the same image ID but different image names",
159+
NoParallel: true,
160+
Cleanup: func(data test.Data, helpers test.Helpers) {
161+
if data.Get("id") != "" {
162+
helpers.Anyhow("rmi", "-f", data.Get("id"))
163+
}
164+
},
165+
Setup: func(data test.Data, helpers test.Helpers) {
166+
helpers.Ensure("pull", "--quiet", testutil.CommonImage)
167+
img := nerdtest.InspectImage(helpers, testutil.CommonImage)
168+
var id string
169+
if nerdtest.IsDocker() {
170+
id = img.ID
171+
} else {
172+
id = strings.Split(img.RepoDigests[0], ":")[1]
173+
}
174+
helpers.Ensure("tag", testutil.CommonImage, data.Identifier())
175+
tarPath := filepath.Join(data.TempDir(), "out.tar")
176+
helpers.Ensure("save", "-o", tarPath, testutil.CommonImage, data.Identifier())
177+
helpers.Ensure("rmi", "-f", id)
178+
helpers.Ensure("load", "-i", tarPath)
179+
data.Set("id", id)
180+
},
181+
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
182+
return helpers.Command("images", "--no-trunc")
183+
},
184+
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
185+
return &test.Expected{
186+
ExitCode: 0,
187+
Errors: []error{},
188+
Output: func(stdout string, info string, t *testing.T) {
189+
assert.Equal(t, strings.Count(stdout, data.Get("id")), 2)
190+
},
191+
}
192+
},
193+
},
194+
}
195+
196+
testCase.Run(t)
197+
}

pkg/cmd/image/save.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,8 @@ func Save(ctx context.Context, client *containerd.Client, images []string, optio
5656
}
5757

5858
imgName := found.Image.Name
59-
imgDigest := found.Image.Target.Digest.String()
60-
if _, ok := savedImages[imgDigest]; !ok {
61-
savedImages[imgDigest] = struct{}{}
59+
if _, ok := savedImages[imgName]; !ok {
60+
savedImages[imgName] = struct{}{}
6261
exportOpts = append(exportOpts, archive.WithImage(imageStore, imgName))
6362
}
6463
return nil

0 commit comments

Comments
 (0)