diff --git a/remotes/fixup.go b/remotes/fixup.go index edac4be..773031a 100644 --- a/remotes/fixup.go +++ b/remotes/fixup.go @@ -47,7 +47,7 @@ func FixupBundle(ctx context.Context, b *bundle.Bundle, ref reference.Named, res return nil, fmt.Errorf("only one invocation image supported for bundle %q", ref) } - relocationMap := relocation.ImageRelocationMap{} + relocationMap := cfg.relocationMap if err := fixupImage(ctx, "InvocationImage", &b.InvocationImages[0].BaseImage, relocationMap, cfg, events, cfg.invocationImagePlatformFilter); err != nil { return nil, err } @@ -72,13 +72,18 @@ func fixupImage( events chan<- FixupEvent, platformFilter platforms.Matcher) error { + // Fixup the base image, using the relocated base image if available + sourceImage := *baseImage + if relocatedBaseImage, ok := relocationMap[baseImage.Image]; ok { + sourceImage.Image = relocatedBaseImage + } + log.G(ctx).Debugf("Updating entry in relocation map for %q", baseImage.Image) ctx = withMutedContext(ctx) - notifyEvent, progress := makeEventNotifier(events, baseImage.Image, cfg.targetRef) + notifyEvent, progress := makeEventNotifier(events, sourceImage.Image, cfg.targetRef) notifyEvent(FixupEventTypeCopyImageStart, "", nil) - // Fixup Base image - fixupInfo, pushed, err := fixupBaseImage(ctx, name, baseImage, cfg) + fixupInfo, pushed, err := fixupBaseImage(ctx, name, &sourceImage, cfg) if err != nil { return notifyError(notifyEvent, err) } diff --git a/remotes/fixup_test.go b/remotes/fixup_test.go index ac39a1a..1305752 100644 --- a/remotes/fixup_test.go +++ b/remotes/fixup_test.go @@ -17,6 +17,7 @@ import ( "github.com/opencontainers/go-digest" ocischemav1 "github.com/opencontainers/image-spec/specs-go/v1" "gotest.tools/assert" + "gotest.tools/assert/cmp" ) func TestFixupBundleWithAutoUpdate(t *testing.T) { @@ -162,6 +163,7 @@ func TestFixupBundlePushImages(t *testing.T) { imageClient := newMockImageClient() ref, err := reference.ParseNamed("my.registry/namespace/my-app") assert.NilError(t, err) + _, err = FixupBundle(context.TODO(), b, ref, resolver, WithAutoBundleUpdate(), WithPushImages(imageClient, os.Stdout)) assert.NilError(t, err) // 2 images has been pushed @@ -196,7 +198,7 @@ func TestFixupBundlePushImages(t *testing.T) { assert.DeepEqual(t, b, expectedBundle) } -func TestFixupBundleCheckResolveOrder(t *testing.T) { +func TestFixupRelocatedBundle(t *testing.T) { index := ocischemav1.Manifest{} bufManifest, err := json.Marshal(index) assert.NilError(t, err) @@ -205,6 +207,82 @@ func TestFixupBundleCheckResolveOrder(t *testing.T) { bytes.NewBuffer(bufManifest), }} pusher := &mockPusher{} + resolver := &mockResolver{ + pusher: pusher, + fetcher: fetcher, + resolvedDescriptors: []ocischemav1.Descriptor{ + // Invocation image will not be resolved first, so push will occurs + { + // just a code to raise an error in the mock + Size: -1, + }, + // Invocation image is resolved after push + { + MediaType: ocischemav1.MediaTypeImageManifest, + Size: 65, + Digest: "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343", + }, + { // Trigger a push for the referenced image + Size: -1, + }, + // Referenced image will be resolved after push based on Digest + { + MediaType: ocischemav1.MediaTypeImageManifest, + Size: 65, + Digest: "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0344", + }, + }, + } + b := &bundle.Bundle{ + SchemaVersion: "v1.0.0", + InvocationImages: []bundle.InvocationImage{ + { + BaseImage: bundle.BaseImage{ + Image: "my.registry/namespace/my-app-invoc", + ImageType: "docker", + }, + }, + }, + Images: map[string]bundle.Image{ + "my-service": { + BaseImage: bundle.BaseImage{ + Digest: "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0344", + Image: "my.registry/namespace/my-service", + ImageType: "docker", + }, + }, + }, + Name: "my-app", + Version: "0.1.0", + } + imageClient := newMockImageClient() + ref, err := reference.ParseNamed("my.registry/namespace/my-app") + assert.NilError(t, err) + + rm := relocation.ImageRelocationMap{ + "my.registry/namespace/my-app-invoc":"localhost:5000/my-app-invoc", + "my.registry/namespace/my-service":"localhost:5000/my-service", + } + _, err = FixupBundle(context.TODO(), b, ref, resolver, WithRelocationMap(rm), WithAutoBundleUpdate(), WithPushImages(imageClient, os.Stdout)) + assert.NilError(t, err) + + // Check that the relocated image was pushed and not the original + assert.Equal(t, 2, len(imageClient.taggedImages), "expected 2 images pushed") + assert.Assert(t, cmp.Contains(imageClient.taggedImages, "localhost:5000/my-app-invoc"), "expected the relocated invocation image to be pushed, not the original") + assert.Assert(t, cmp.Contains(imageClient.taggedImages, "localhost:5000/my-service"), "expected the relocated referenced image to be pushed, not the original") +} + +func TestFixupBundleCheckResolveOrder(t *testing.T) { + index := ocischemav1.Manifest{} + bufManifest, err := json.Marshal(index) + assert.NilError(t, err) + fetcher := &mockFetcher{indexBuffers: []*bytes.Buffer{ + // Manifest index for each relocated image + bytes.NewBuffer(bufManifest), + bytes.NewBuffer(bufManifest), + bytes.NewBuffer(bufManifest), + }} + pusher := &mockPusher{} // Construct a test case // - invocation map will be found in relocation map, resolved and copy @@ -276,13 +354,13 @@ func TestFixupBundleCheckResolveOrder(t *testing.T) { // Resolvable { MediaType: ocischemav1.MediaTypeImageManifest, - Size: 42, + Size: 65, Digest: "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343", }, // This one is from the copy task { MediaType: ocischemav1.MediaTypeImageManifest, - Size: 42, + Size: 65, Digest: "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343", }, @@ -294,13 +372,13 @@ func TestFixupBundleCheckResolveOrder(t *testing.T) { // resolved by second pass, from the bundle { MediaType: ocischemav1.MediaTypeImageManifest, - Size: 42, + Size: 65, Digest: "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343", }, // copy task { MediaType: ocischemav1.MediaTypeImageManifest, - Size: 42, + Size: 65, Digest: "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343", }, @@ -316,7 +394,7 @@ func TestFixupBundleCheckResolveOrder(t *testing.T) { // image is pushed, resolve is called at the end { MediaType: ocischemav1.MediaTypeImageManifest, - Size: 42, + Size: 65, Digest: "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343", }, @@ -324,13 +402,13 @@ func TestFixupBundleCheckResolveOrder(t *testing.T) { // not in relocation map but resolvable { MediaType: ocischemav1.MediaTypeImageManifest, - Size: 42, + Size: 65, Digest: "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343", }, // copy task { MediaType: ocischemav1.MediaTypeImageManifest, - Size: 42, + Size: 65, Digest: "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343", }, @@ -342,7 +420,7 @@ func TestFixupBundleCheckResolveOrder(t *testing.T) { // image is pushed, resolve is called at the end { MediaType: ocischemav1.MediaTypeImageManifest, - Size: 42, + Size: 65, Digest: "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343", }, }, diff --git a/remotes/mocks_test.go b/remotes/mocks_test.go index 07a4c3f..a81b242 100644 --- a/remotes/mocks_test.go +++ b/remotes/mocks_test.go @@ -109,10 +109,11 @@ func (rc mockReadCloser) Close() error { type mockImageClient struct { pushedImages int + taggedImages map[string]string } func newMockImageClient() *mockImageClient { - return &mockImageClient{} + return &mockImageClient{ taggedImages: map[string]string{}} } func (c *mockImageClient) ImagePush(ctx context.Context, ref string, options types.ImagePushOptions) (io.ReadCloser, error) { @@ -120,5 +121,6 @@ func (c *mockImageClient) ImagePush(ctx context.Context, ref string, options typ return mockReadCloser{}, nil } func (c *mockImageClient) ImageTag(ctx context.Context, image, ref string) error { + c.taggedImages[image] = ref return nil }