241 lines
6.6 KiB
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,
|
|
);
|
|
}
|
|
}
|