299 lines
10 KiB
Dart
299 lines
10 KiB
Dart
import 'package:podman/podman.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
import 'support/fake_podman_transport.dart';
|
|
|
|
void main() {
|
|
group('PodmanClient image API', () {
|
|
test('lists images', () async {
|
|
final transport = FakePodmanTransport()
|
|
..enqueue(
|
|
method: HttpMethod.get,
|
|
path: '/v5.0.0/libpod/images/json',
|
|
queryParameters: const <String, List<String>>{
|
|
'all': <String>['false'],
|
|
},
|
|
responseBody: <Object?>[
|
|
<String, Object?>{
|
|
'Id': 'sha256:abc',
|
|
'Repository': 'hello-world',
|
|
'Tag': 'latest',
|
|
'Digest': 'sha256:def',
|
|
'Size': '123 MB',
|
|
},
|
|
],
|
|
);
|
|
|
|
final client = PodmanClient(transport: transport);
|
|
final images = await client.listImages();
|
|
|
|
expect(images, hasLength(1));
|
|
expect(images.first.id, 'sha256:abc');
|
|
expect(images.first.reference, 'hello-world:latest');
|
|
transport.expectNoPending();
|
|
});
|
|
|
|
test('pull returns response body', () async {
|
|
final transport = FakePodmanTransport()
|
|
..enqueue(
|
|
method: HttpMethod.post,
|
|
path: '/v5.0.0/libpod/images/pull',
|
|
queryParameters: const <String, List<String>>{
|
|
'reference': <String>['hello-world:latest'],
|
|
'quiet': <String>['true'],
|
|
},
|
|
responseBody: 'pull complete',
|
|
);
|
|
|
|
final client = PodmanClient(transport: transport);
|
|
final output = await client.pull('hello-world:latest', quiet: true);
|
|
|
|
expect(output, 'pull complete');
|
|
transport.expectNoPending();
|
|
});
|
|
|
|
test('image exists false on 404', () async {
|
|
final transport = FakePodmanTransport()
|
|
..enqueue(
|
|
method: HttpMethod.get,
|
|
path: '/v5.0.0/libpod/images/missing%3Alatest/exists',
|
|
statusCode: 404,
|
|
);
|
|
|
|
final client = PodmanClient(transport: transport);
|
|
final exists = await client.imageExists('missing:latest');
|
|
|
|
expect(exists, isFalse);
|
|
transport.expectNoPending();
|
|
});
|
|
|
|
test('supports remove, tag, and prune', () async {
|
|
final transport = FakePodmanTransport()
|
|
..enqueue(
|
|
method: HttpMethod.delete,
|
|
path: '/v5.0.0/libpod/images/hello-world%3Alatest',
|
|
queryParameters: const <String, List<String>>{
|
|
'force': <String>['true'],
|
|
},
|
|
statusCode: 200,
|
|
)
|
|
..enqueue(
|
|
method: HttpMethod.post,
|
|
path: '/v5.0.0/libpod/images/hello-world%3Alatest/tag',
|
|
queryParameters: const <String, List<String>>{
|
|
'repo': <String>['local/hello-world'],
|
|
'tag': <String>['test'],
|
|
},
|
|
statusCode: 201,
|
|
)
|
|
..enqueue(
|
|
method: HttpMethod.post,
|
|
path: '/v5.0.0/libpod/images/prune',
|
|
queryParameters: const <String, List<String>>{
|
|
'all': <String>['true'],
|
|
},
|
|
statusCode: 200,
|
|
responseBody: const <Object?>[],
|
|
);
|
|
|
|
final client = PodmanClient(transport: transport);
|
|
await client.removeImage('hello-world:latest', force: true);
|
|
await client.tagImage('hello-world:latest', 'local/hello-world:test');
|
|
await client.pruneImages(all: true);
|
|
|
|
transport.expectNoPending();
|
|
});
|
|
|
|
test(
|
|
'supports inspect, history, tree, push, load/import/export, and batch remove',
|
|
() async {
|
|
final archiveBytes = <int>[1, 2, 3, 4];
|
|
final exportBytes = <int>[9, 8, 7];
|
|
|
|
final transport = FakePodmanTransport()
|
|
..enqueue(
|
|
method: HttpMethod.get,
|
|
path: '/v5.0.0/libpod/images/hello-world%3Alatest/json',
|
|
responseBody: const <String, Object?>{
|
|
'Id': 'sha256:abc',
|
|
'Digest': 'sha256:def',
|
|
'RepoTags': <String>['hello-world:latest'],
|
|
'RepoDigests': <String>['hello-world@sha256:def'],
|
|
'Created': '2026-04-01T00:00:00Z',
|
|
'Size': 1234,
|
|
},
|
|
)
|
|
..enqueue(
|
|
method: HttpMethod.get,
|
|
path: '/v5.0.0/libpod/images/hello-world%3Alatest/history',
|
|
responseBody: <Object?>[
|
|
<String, Object?>{
|
|
'Id': 'sha256:layer1',
|
|
'Created': 1712361600,
|
|
'CreatedBy': '/bin/sh -c echo hi',
|
|
'Tags': <String>['hello-world:latest'],
|
|
'Size': 42,
|
|
'Comment': 'base layer',
|
|
},
|
|
],
|
|
)
|
|
..enqueue(
|
|
method: HttpMethod.get,
|
|
path: '/v5.0.0/libpod/images/hello-world%3Alatest/tree',
|
|
queryParameters: const <String, List<String>>{
|
|
'whatrequires': <String>['true'],
|
|
},
|
|
responseBody: const <String, Object?>{
|
|
'Tree': 'hello-world:latest\n└── scratch',
|
|
},
|
|
)
|
|
..enqueue(
|
|
method: HttpMethod.post,
|
|
path: '/v5.0.0/libpod/images/hello-world%3Alatest/push',
|
|
queryParameters: const <String, List<String>>{
|
|
'destination': <String>['quay.io/groupware/hello-world:latest'],
|
|
'retry': <String>['3'],
|
|
'retryDelay': <String>['1s'],
|
|
'tlsVerify': <String>['false'],
|
|
'quiet': <String>['false'],
|
|
},
|
|
responseBody:
|
|
'{"stream":"copying"}\n'
|
|
'{"manifestdigest":"sha256:push"}\n',
|
|
)
|
|
..enqueue(
|
|
method: HttpMethod.post,
|
|
path: '/v5.0.0/libpod/images/load',
|
|
body: archiveBytes,
|
|
responseBody: const <String, Object?>{
|
|
'Names': <String>['hello-world:latest'],
|
|
},
|
|
)
|
|
..enqueue(
|
|
method: HttpMethod.post,
|
|
path: '/v5.0.0/libpod/images/load',
|
|
queryParameters: const <String, List<String>>{
|
|
'path': <String>['/tmp/image.tar'],
|
|
},
|
|
responseBody: const <String, Object?>{
|
|
'Names': <String>['hello-world:latest'],
|
|
},
|
|
)
|
|
..enqueue(
|
|
method: HttpMethod.post,
|
|
path: '/v5.0.0/libpod/images/import',
|
|
queryParameters: const <String, List<String>>{
|
|
'reference': <String>['quay.io/groupware/imported:latest'],
|
|
'message': <String>['import image'],
|
|
'changes': <String>['CMD ["/bin/sh"]'],
|
|
},
|
|
body: archiveBytes,
|
|
responseBody: const <String, Object?>{'Id': 'sha256:imported'},
|
|
)
|
|
..enqueue(
|
|
method: HttpMethod.get,
|
|
path: '/v5.0.0/libpod/images/hello-world%3Alatest/get',
|
|
queryParameters: const <String, List<String>>{
|
|
'compress': <String>['true'],
|
|
'format': <String>['docker-archive'],
|
|
},
|
|
responseBody: exportBytes,
|
|
)
|
|
..enqueue(
|
|
method: HttpMethod.get,
|
|
path: '/v5.0.0/libpod/images/export',
|
|
queryParameters: const <String, List<String>>{
|
|
'format': <String>['docker-archive'],
|
|
'references': <String>['hello-world:latest', 'busybox:latest'],
|
|
},
|
|
responseBody: exportBytes,
|
|
)
|
|
..enqueue(
|
|
method: HttpMethod.delete,
|
|
path: '/v5.0.0/libpod/images/remove',
|
|
queryParameters: const <String, List<String>>{
|
|
'images': <String>['hello-world:latest'],
|
|
'ignore': <String>['true'],
|
|
},
|
|
responseBody: const <String, Object?>{
|
|
'Deleted': <String>['sha256:def'],
|
|
'Untagged': <String>['hello-world:latest'],
|
|
'ExitCode': 0,
|
|
'Errors': <String>[],
|
|
},
|
|
);
|
|
|
|
final client = PodmanClient(transport: transport);
|
|
|
|
final details = await client.inspectImage('hello-world:latest');
|
|
expect(details.id, 'sha256:abc');
|
|
|
|
final history = await client.imageHistory('hello-world:latest');
|
|
expect(history, hasLength(1));
|
|
expect(history.first.id, 'sha256:layer1');
|
|
|
|
final tree = await client.imageTree(
|
|
'hello-world:latest',
|
|
whatRequires: true,
|
|
);
|
|
expect(tree.tree, contains('hello-world:latest'));
|
|
|
|
final pushEvents = await client.pushImage(
|
|
'hello-world:latest',
|
|
options: const ImagePushOptions(
|
|
destination: 'quay.io/groupware/hello-world:latest',
|
|
retry: 3,
|
|
retryDelay: '1s',
|
|
tlsVerify: false,
|
|
quiet: false,
|
|
),
|
|
);
|
|
expect(pushEvents, hasLength(2));
|
|
expect(pushEvents.last.manifestDigest, 'sha256:push');
|
|
|
|
final loaded = await client.loadImages(archiveBytes);
|
|
expect(loaded.names, contains('hello-world:latest'));
|
|
|
|
final loadedFromPath = await client.loadImagesFromPath(
|
|
'/tmp/image.tar',
|
|
);
|
|
expect(loadedFromPath.names, contains('hello-world:latest'));
|
|
|
|
final imported = await client.importImage(
|
|
options: const ImageImportOptions(
|
|
reference: 'quay.io/groupware/imported:latest',
|
|
message: 'import image',
|
|
changes: <String>['CMD ["/bin/sh"]'],
|
|
),
|
|
archiveBytes: archiveBytes,
|
|
);
|
|
expect(imported.id, 'sha256:imported');
|
|
|
|
final exported = await client.exportImage(
|
|
'hello-world:latest',
|
|
options: const ImageExportOptions(
|
|
compress: true,
|
|
format: 'docker-archive',
|
|
),
|
|
);
|
|
expect(exported, exportBytes);
|
|
|
|
final exportedMulti = await client.exportImages(const <String>[
|
|
'hello-world:latest',
|
|
'busybox:latest',
|
|
], options: const ImageExportOptions(format: 'docker-archive'));
|
|
expect(exportedMulti, exportBytes);
|
|
|
|
final removed = await client.removeImages(
|
|
const ImageRemoveOptions(
|
|
images: <String>['hello-world:latest'],
|
|
ignore: true,
|
|
),
|
|
);
|
|
expect(removed.deleted, contains('sha256:def'));
|
|
|
|
transport.expectNoPending();
|
|
},
|
|
);
|
|
});
|
|
}
|