Skip to content

Commit 20e2caa

Browse files
committed
pull,load: use *Image instead of re-resolving via name
Following commit fixes a `race` condition in `libimage` because in `Pull(` after performing `copy` from remote sources it agains attempts to resolve image via `LookupImage`, any operation between `copy` and `LookupImage` can remove `name` from the recently pulled image. Causing race in builds. This issue was discoverd while working on PR containers/buildah#5971 ``` buildah build -t test --jobs=2 --skip-unused-stages=false . ``` Containerfile ``` FROM quay.io/jitesoft/alpine RUN arch FROM --platform=linux/arm64 quay.io/jitesoft/alpine AS foreign ``` Following commit also addresses the commit e2324dd by performing the neccessary refactor. No functional change in public exposed API, exisiting tests should pass as-is. [NO NEW TESTS NEEDED] Signed-off-by: flouthoc <[email protected]>
1 parent a56094c commit 20e2caa

File tree

4 files changed

+108
-77
lines changed

4 files changed

+108
-77
lines changed

libimage/copier.go

+29
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ import (
2121
"github.com/containers/image/v5/signature"
2222
"github.com/containers/image/v5/signature/signer"
2323
storageTransport "github.com/containers/image/v5/storage"
24+
"github.com/containers/image/v5/transports"
2425
"github.com/containers/image/v5/types"
2526
encconfig "github.com/containers/ocicrypt/config"
27+
"github.com/containers/storage"
2628
"github.com/sirupsen/logrus"
2729
)
2830

@@ -350,6 +352,12 @@ func (c *Copier) Close() error {
350352
// Copy the source to the destination. Returns the bytes of the copied
351353
// manifest which may be used for digest computation.
352354
func (c *Copier) Copy(ctx context.Context, source, destination types.ImageReference) ([]byte, error) {
355+
return c.copyInternal(ctx, source, destination, nil)
356+
}
357+
358+
// Copy the source to the destination. Returns the bytes of the copied
359+
// manifest which may be used for digest computation.
360+
func (c *Copier) copyInternal(ctx context.Context, source, destination types.ImageReference, reportResolvedReference *types.ImageReference) ([]byte, error) {
353361
logrus.Debugf("Copying source image %s to destination image %s", source.StringWithinTransport(), destination.StringWithinTransport())
354362

355363
// Avoid running out of time when running inside a systemd unit by
@@ -381,6 +389,12 @@ func (c *Copier) Copy(ctx context.Context, source, destination types.ImageRefere
381389
)
382390
}
383391

392+
// This is already set when `newCopier` was called but there is an option
393+
// to override it by callers if needed.
394+
if reportResolvedReference != nil {
395+
c.imageCopyOptions.ReportResolvedReference = reportResolvedReference
396+
}
397+
384398
// From `man systemd.service(5)`:
385399
//
386400
// "If a service of Type=notify/Type=notify-reload sends "EXTEND_TIMEOUT_USEC=...", this may cause
@@ -472,6 +486,21 @@ func (c *Copier) Copy(ctx context.Context, source, destination types.ImageRefere
472486
return returnManifest, retry.IfNecessary(ctx, f, &c.retryOptions)
473487
}
474488

489+
func (c *Copier) copyToStorage(ctx context.Context, source, destination types.ImageReference, reportResolvedReference *types.ImageReference) (*storage.Image, error) {
490+
_, err := c.copyInternal(ctx, source, destination, reportResolvedReference)
491+
if err != nil {
492+
return nil, fmt.Errorf("internal error: unable to copy from source %s: %w", source, err)
493+
}
494+
if reportResolvedReference == nil {
495+
return nil, fmt.Errorf("internal error: After attempting to copy %s, resolvedReference is nil", source)
496+
}
497+
_, image, err := storageTransport.ResolveReference(*reportResolvedReference)
498+
if err != nil {
499+
return nil, fmt.Errorf("resolving an already-resolved reference %q to the pulled image: %w", transports.ImageName(*reportResolvedReference), err)
500+
}
501+
return image, nil
502+
}
503+
475504
// checkRegistrySourcesAllows checks the $BUILD_REGISTRY_SOURCES environment
476505
// variable, if it's set. The contents are expected to be a JSON-encoded
477506
// github.com/openshift/api/config/v1.Image, set by an OpenShift build

libimage/image_test.go

+1-5
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,11 @@ func TestImageFunctions(t *testing.T) {
6161
// Just make sure that the ID has 64 characters.
6262
require.True(t, len(image.ID()) == 64, "ID should be 64 characters long")
6363

64-
// Make sure that the image we pulled by digest is the same one we
65-
// pulled by tag.
66-
require.Equal(t, origDigest.String(), image.Digest().String(), "digests of pulled images should match")
67-
6864
// NOTE: we're recording two digests. One for the image and one for the
6965
// manifest list we chose it from.
7066
digests := image.Digests()
7167
require.Len(t, digests, 2)
72-
require.Equal(t, origDigest.String(), digests[0].String(), "first recorded digest should be the one of the image")
68+
require.Contains(t, digests, origDigest, "original digest string should be present in the digests array of new pulled image")
7369

7470
// containers/podman/issues/12729: make sure manifest lookup returns
7571
// the correct error for both digests.

libimage/load.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func (r *Runtime) doLoadReference(ctx context.Context, ref types.ImageReference,
3030
case dockerArchiveTransport.Transport.Name():
3131
images, err = r.loadMultiImageDockerArchive(ctx, ref, &options.CopyOptions)
3232
default:
33-
images, err = r.copyFromDefault(ctx, ref, &options.CopyOptions)
33+
_, images, err = r.copyFromDefault(ctx, ref, &options.CopyOptions)
3434
}
3535
return images, ref.Transport().Name(), err
3636
}
@@ -49,6 +49,9 @@ func (r *Runtime) LoadReference(ctx context.Context, ref types.ImageReference, o
4949
// Load loads one or more images (depending on the transport) from the
5050
// specified path. The path may point to an image the following transports:
5151
// oci, oci-archive, dir, docker-archive.
52+
//
53+
// Load returns a string slice with names of recently loaded images.
54+
// If images are unnamed in the source, it returns a string slice of image IDs instead.
5255
func (r *Runtime) Load(ctx context.Context, path string, options *LoadOptions) ([]string, error) {
5356
logrus.Debugf("Loading image from %q", path)
5457

@@ -142,7 +145,8 @@ func (r *Runtime) loadMultiImageDockerArchive(ctx context.Context, ref types.Ima
142145
// should.
143146
path := ref.StringWithinTransport()
144147
if err := fileutils.Exists(path); err != nil {
145-
return r.copyFromDockerArchive(ctx, ref, options)
148+
_, names, err := r.copyFromDockerArchive(ctx, ref, options)
149+
return names, err
146150
}
147151

148152
reader, err := dockerArchiveTransport.NewReader(r.systemContextCopy(), path)
@@ -163,7 +167,7 @@ func (r *Runtime) loadMultiImageDockerArchive(ctx context.Context, ref types.Ima
163167
var copiedImages []string
164168
for _, list := range refLists {
165169
for _, listRef := range list {
166-
names, err := r.copyFromDockerArchiveReaderReference(ctx, reader, listRef, options)
170+
_, names, err := r.copyFromDockerArchiveReaderReference(ctx, reader, listRef, options)
167171
if err != nil {
168172
return nil, err
169173
}

0 commit comments

Comments
 (0)