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