chore: add isManifest function to determine a manifest

### What does this PR do?

Adds a guessIsManifest function to try and determine if an image is a
manifest or not:

- By checking the repotags and digests
- Checking labels does not exist, as labels are not added when creating
  a manifest
- VirtualSize is under 1MB as a manifest list is just text that points
  to other images

We are unable to check the history (manifest commonly has no history) as
Dockerode API does not support getting the history of the image.

### Screenshot / video of UI

<!-- If this PR is changing UI, please include
screenshots or screencasts showing the difference -->

N/A

### What issues does this PR fix or reference?

<!-- Include any related issues from Podman Desktop
repository (or from another issue tracker). -->

Closes  containers#6690

### How to test this PR?

<!-- Please explain steps to verify the functionality,
do not forget to provide unit/component tests -->

- [X] Tests are covering the bug fix or the new feature

Signed-off-by: Charlie Drage <charlie@charliedrage.com>
This commit is contained in:
Charlie Drage 2024-04-08 17:47:01 -04:00 committed by Florent BENOIT
parent b003d15ce7
commit 006e65bfb6
3 changed files with 76 additions and 0 deletions

View file

@ -25,6 +25,7 @@ export interface ImageInfo extends Dockerode.ImageInfo {
engineId: string;
engineName: string;
History?: string[];
isManifest?: boolean;
}
export interface BuildImageOptions {

View file

@ -4391,3 +4391,73 @@ describe('loadImages', () => {
);
});
});
test('manifest is listed as true with podmanListImages correctly', async () => {
const manifestImage: ImageInfo = {
Id: 'manifestImage',
Labels: {},
engineId: 'engine1',
engineName: 'podman',
ParentId: '',
RepoTags: ['manifestTag'],
RepoDigests: ['manifestDigest'],
Created: 0,
Size: 0,
VirtualSize: 40 * 1024, // 40KB (less than 50KB threshold)
SharedSize: 0,
Containers: 0,
};
const regularImage: ImageInfo = {
Id: 'ee301c921b8aadc002973b2e0c3da17d701dcd994b606769a7e6eaa100b81d44',
Labels: {},
engineId: 'engine5', // Assuming 'engineId' and 'engineName' are part of your ImageInfo but not relevant here
engineName: 'podman',
ParentId: '',
RepoTags: ['testdomain.io/library/hello:latest'],
RepoDigests: [
'testdomain.io/library/hello@sha256:2d4e459f4ecb5329407ae3e47cbc107a2fbace221354ca75960af4c047b3cb13',
'testdomain.io/library/hello@sha256:53641cd209a4fecfc68e21a99871ce8c6920b2e7502df0a20671c6fccc73a7c6',
],
Created: 1683046167,
Size: 23301,
VirtualSize: 23301, // Directly matches Size in this case
SharedSize: 0,
Containers: 0,
History: ['testdomain.io/library/hello:latest'],
};
const imagesList = [manifestImage, regularImage];
nock('http://localhost').get('/v4.2.0/libpod/images/json').reply(200, imagesList);
const api = new Dockerode({ protocol: 'http', host: 'localhost' });
// set provider
containerRegistry.addInternalProvider('podman', {
name: 'podman',
id: 'podman1',
api,
libpodApi: api,
connection: {
type: 'podman',
},
} as unknown as InternalContainerProvider);
const images = await containerRegistry.podmanListImages();
// ensure the field are correct
expect(images).toBeDefined();
expect(images).toHaveLength(2);
// Check the first image
const image = images[0];
expect(image.engineId).toBe('podman1');
expect(image.engineName).toBe('podman');
expect(image.Id).toBe('manifestImage');
expect(image.isManifest).toBe(true);
// Check the second image
const image2 = images[1];
expect(image2.engineId).toBe('podman1');
expect(image2.engineName).toBe('podman');
expect(image2.Id).toBe('ee301c921b8aadc002973b2e0c3da17d701dcd994b606769a7e6eaa100b81d44');
expect(image2.isManifest).toBe(false);
});

View file

@ -75,6 +75,7 @@ import { Emitter } from './events/emitter.js';
import type { ImageRegistry } from './image-registry.js';
import type { Telemetry } from './telemetry/telemetry.js';
import { Disposable } from './types/disposable.js';
import { guessIsManifest } from './util/manifest.js';
const tar: { pack: (dir: string) => NodeJS.ReadableStream } = require('tar-fs');
@ -626,6 +627,10 @@ export class ContainerProviderRegistry {
...image,
engineName: provider.name,
engineId: provider.id,
// Using guessIsManifest, determine if the image is a manifest and set isManifest accordingly
// NOTE: This is a workaround until we have a better way to determine if an image is a manifest
// and may result in false positives until issue: https://github.com/containers/podman/issues/22184 is resolved
isManifest: guessIsManifest(image),
}));
}),
);