podman/lib/src/client/containers/containers.dart

241 lines
6.6 KiB
Dart

part of '../podman_client.dart';
extension PodmanClientContainerApi on PodmanClient {
/// Lists containers.
Future<List<ContainerSummary>> listContainers({
bool all = true,
Duration? timeout,
}) async {
final payload = await _getList(
'/containers/json',
queryParameters: <String, List<String>>{
'all': <String>['$all'],
},
timeout: timeout,
);
return payload.map(ContainerSummary.fromJson).toList(growable: false);
}
/// Creates a container without starting it.
Future<ContainerCreateResult> createContainer(
RunOptions options, {
Duration? timeout,
}) async {
final response = await _send(
method: HttpMethod.post,
path: '/containers/create',
queryParameters: <String, List<String>>{
if (options.name != null && options.name!.isNotEmpty)
'name': <String>[options.name!],
},
body: options.toCreateBody(),
expectedStatusCodes: const <int>{201},
timeout: timeout,
);
return ContainerCreateResult.fromJson(
_decodeObject(response.bodyText, '/containers/create'),
);
}
/// Creates and starts a container, returning the container ID.
Future<String> run(RunOptions options, {Duration? timeout}) async {
final created = await createContainer(options, timeout: timeout);
await start(created.id, timeout: timeout);
return created.id;
}
/// Inspects a single container.
Future<ContainerDetails> inspectContainer(
String container, {
Duration? timeout,
}) async {
final payload = await _getObject(
'/containers/${_encodePath(container)}/json',
timeout: timeout,
);
return ContainerDetails.fromJson(payload);
}
/// Whether a container exists.
Future<bool> containerExists(String container, {Duration? timeout}) async {
final response = await _send(
method: HttpMethod.get,
path: '/containers/${_encodePath(container)}/exists',
expectedStatusCodes: const <int>{204, 404},
timeout: timeout,
);
return response.statusCode == 204;
}
/// Starts a container.
Future<void> start(String container, {Duration? timeout}) async {
await _send(
method: HttpMethod.post,
path: '/containers/${_encodePath(container)}/start',
expectedStatusCodes: const <int>{204, 304},
timeout: timeout,
);
}
/// Stops a container.
Future<void> stop(
String container, {
int? timeoutSeconds,
Duration? timeout,
}) async {
await _send(
method: HttpMethod.post,
path: '/containers/${_encodePath(container)}/stop',
queryParameters: <String, List<String>>{
if (timeoutSeconds != null) 't': <String>['$timeoutSeconds'],
},
expectedStatusCodes: const <int>{204, 304},
timeout: timeout,
);
}
/// Restarts a container.
Future<void> restart(
String container, {
int? timeoutSeconds,
Duration? timeout,
}) async {
await _send(
method: HttpMethod.post,
path: '/containers/${_encodePath(container)}/restart',
queryParameters: <String, List<String>>{
if (timeoutSeconds != null) 't': <String>['$timeoutSeconds'],
},
expectedStatusCodes: const <int>{204, 304},
timeout: timeout,
);
}
/// Removes a container.
Future<void> removeContainer(
String container, {
bool force = false,
bool removeVolumes = false,
bool ignoreMissing = false,
Duration? timeout,
}) async {
await _send(
method: HttpMethod.delete,
path: '/containers/${_encodePath(container)}',
queryParameters: <String, List<String>>{
'force': <String>['$force'],
'v': <String>['$removeVolumes'],
},
expectedStatusCodes: ignoreMissing
? const <int>{200, 202, 204, 404}
: const <int>{200, 202, 204},
timeout: timeout,
);
}
/// Fetches container logs.
Future<String> logs(
String container, {
int? tail,
DateTime? since,
DateTime? until,
bool follow = false,
bool timestamps = false,
bool stdout = true,
bool stderr = true,
Duration? timeout,
}) async {
final response = await _send(
method: HttpMethod.get,
path: '/containers/${_encodePath(container)}/logs',
queryParameters: <String, List<String>>{
'stdout': <String>['$stdout'],
'stderr': <String>['$stderr'],
if (follow) 'follow': const <String>['true'],
'timestamps': <String>['$timestamps'],
if (since != null) 'since': <String>[since.toUtc().toIso8601String()],
if (until != null) 'until': <String>[until.toUtc().toIso8601String()],
if (tail != null) 'tail': <String>['$tail'],
},
expectedStatusCodes: const <int>{200},
timeout: timeout,
);
return response.bodyText;
}
/// Polls container logs with `since` cursors.
Stream<String> watchLogs(
String container, {
int? tail,
bool timestamps = true,
bool stdout = true,
bool stderr = true,
DateTime? since,
Duration pollInterval = const Duration(seconds: 2),
bool reconnect = true,
Duration reconnectDelay = const Duration(seconds: 1),
Duration? timeout,
}) {
final controller = StreamController<String>();
var closed = false;
var firstRequest = true;
var cursorSince = since?.toUtc() ?? DateTime.now().toUtc();
Future<void> loop() async {
while (!closed) {
try {
final nextCursor = DateTime.now().toUtc();
final chunk = await logs(
container,
tail: firstRequest ? tail : null,
since: cursorSince,
timestamps: timestamps,
stdout: stdout,
stderr: stderr,
timeout: timeout,
);
firstRequest = false;
cursorSince = nextCursor;
if (closed) {
break;
}
if (chunk.trim().isNotEmpty) {
controller.add(chunk);
}
await Future<void>.delayed(pollInterval);
} catch (error, stackTrace) {
if (!reconnect || closed) {
controller.addError(error, stackTrace);
await controller.close();
return;
}
await Future<void>.delayed(reconnectDelay);
}
}
}
controller.onListen = loop;
controller.onCancel = () async {
closed = true;
};
return controller.stream;
}
/// Removes stopped containers.
Future<void> pruneContainers({Duration? timeout}) async {
await _send(
method: HttpMethod.post,
path: '/containers/prune',
expectedStatusCodes: const <int>{200},
timeout: timeout,
);
}
}