From 3b0d8a208fbe6c2e6d952c24db6a3ed175c5de8f Mon Sep 17 00:00:00 2001 From: Florent Benoit Date: Mon, 4 Mar 2024 13:28:47 +0400 Subject: [PATCH] chore: handle docker manifests in addition to the OCI manifests some registries/tooling might use old metadata related to https://github.com/containers/podman-desktop/issues/6226 Signed-off-by: Florent Benoit --- .../main/src/plugin/image-registry.spec.ts | 49 ++++++++++++++++++- packages/main/src/plugin/image-registry.ts | 9 +++- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/packages/main/src/plugin/image-registry.spec.ts b/packages/main/src/plugin/image-registry.spec.ts index 36715c713fb..1ead631c468 100644 --- a/packages/main/src/plugin/image-registry.spec.ts +++ b/packages/main/src/plugin/image-registry.spec.ts @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (C) 2022 Red Hat, Inc. + * Copyright (C) 2022-2024 Red Hat, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -661,6 +661,53 @@ test('getManifestFromUrl returns the expected manifest without mediaType but wit expect(spyGetBestManifest).toHaveBeenCalled(); }); +test('getManifestFromUrl returns the expected manifest with docker manifest v2', async () => { + const fakeManifest = { + schemaVersion: 2, + mediaType: 'application/vnd.docker.distribution.manifest.list.v2+json', + manifests: [ + { + name: 'docker-manifest', + mediaType: 'application/vnd.docker.distribution.manifest.v2+json', + }, + { + name: 'unknown-manifest', + mediaType: 'unknown', + }, + ], + }; + + // image index + nock('https://my-podman-desktop-fake-registry.io').get('/v2/foo/bar/manifests/latest').reply(200, fakeManifest); + + // digest + nock('https://my-podman-desktop-fake-registry.io') + .get('/v2/foo/bar/manifests/1234') + .reply(200, JSON.stringify({ endManifest: true })); + + // mock getBestManifest + const spyGetBestManifest = vi.spyOn(imageRegistry, 'getBestManifest'); + spyGetBestManifest.mockReturnValue({ + digest: 1234, + }); + + const manifest = await imageRegistry.getManifest( + { + name: 'foo/bar', + tag: 'latest', + registry: 'my-podman-desktop-fake-registry.io', + registryURL: 'https://my-podman-desktop-fake-registry.io/v2', + }, + 'dummyToken', + ); + + expect(manifest).toBeDefined(); + expect(manifest).toHaveProperty('endManifest', true); + expect(spyGetBestManifest).toHaveBeenCalled(); + // check first item of the call and first element of the array + expect(spyGetBestManifest.mock.calls[0][0][0]).contains({ name: 'docker-manifest' }); +}); + test('getAuthconfigForServer returns the expected authconfig', async () => { imageRegistry.registerRegistry({ serverUrl: 'my-podman-desktop-fake-registry.io', diff --git a/packages/main/src/plugin/image-registry.ts b/packages/main/src/plugin/image-registry.ts index cb08fa0c1f0..f56678df940 100644 --- a/packages/main/src/plugin/image-registry.ts +++ b/packages/main/src/plugin/image-registry.ts @@ -622,6 +622,7 @@ export class ImageRegistry { if ( parsedManifest.schemaVersion === 2 && (parsedManifest.mediaType === 'application/vnd.oci.image.index.v1+json' || + parsedManifest.mediaType === 'application/vnd.docker.distribution.manifest.list.v2+json' || Array.isArray(parsedManifest.manifests)) ) { // need to grab correct manifest from the index corresponding to our platform @@ -642,8 +643,12 @@ export class ImageRegistry { } // find the manifest corresponding to our platform const matchedManifest = this.getBestManifest( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - parsedManifest.manifests.filter((m: any) => m.mediaType === 'application/vnd.oci.image.manifest.v1+json'), + parsedManifest.manifests.filter( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (m: any) => + m.mediaType === 'application/vnd.oci.image.manifest.v1+json' || + m.mediaType === 'application/vnd.docker.distribution.manifest.v2+json', + ), platformArch, platformOs, );