start static method

Future<DockerProcess> start({
  1. required String image,
  2. required String name,
  3. String? dockerExecutable,
  4. String? dockerCommand,
  5. List<String>? dockerArgs,
  6. String? network,
  7. String? hostname,
  8. Map<String, String>? environment,
  9. List<String>? ports,
  10. List<String>? imageArgs,
  11. bool sudo = false,
  12. bool verbose = false,
  13. bool? cleanup,
  14. FutureOr<bool> readySignal(
    1. String line
    )?,
  15. Duration? timeout,
})

Starts a docker container.

Implementation

static Future<DockerProcess> start({
  required String image,
  required String name,
  String? dockerExecutable,
  String? dockerCommand,
  List<String>? dockerArgs,
  String? network,
  String? hostname,
  Map<String, String>? environment,
  List<String>? ports,
  List<String>? imageArgs,
  bool sudo = false,
  bool verbose = false,
  bool? cleanup,
  FutureOr<bool> Function(String line)? readySignal,
  Duration? timeout,
}) async {
  dockerExecutable ??= 'docker';
  cleanup ??= false;
  var command = dockerExecutable;
  final args = <String>[];
  final lines = <String>[];

  if (sudo) {
    args.add(command);
    command = 'sudo';
  }
  dockerCommand ??= readySignal == null ? 'start' : 'run';

  args.add(dockerCommand);
  if (cleanup) {
    args.add('--rm');
  }

  args.addAll(<String>[
    '--name',
    name,
  ]);

  if (network != null) {
    args.add('--net');
    args.add(network);
  }
  if (hostname != null) {
    args.add('-h');
    args.add(hostname);
  }
  ports?.forEach((p) {
    args.add('-p');
    args.add(p);
  });
  environment?.forEach((k, v) {
    args.add('-e');
    args.add('$k=$v');
  });
  if (dockerArgs != null && dockerArgs.isNotEmpty) {
    args.addAll(dockerArgs);
  }
  args.add(image);
  if (imageArgs != null) {
    args.addAll(imageArgs);
  }

  _logger.info({
    'starting': {'name': name, 'command': command, 'args': args},
  });
  if (readySignal != null) {
    final process = await Process.start(command, args);

    final c = Completer<void>();
    final timer = Timer(timeout ?? const Duration(minutes: 1), () {
      if (c.isCompleted) return;
      stderr.writeAll(lines, '\n');
      c.completeError('timeout');
    });
    StreamSubscription<void>? subs1;
    StreamSubscription<void>? subs2;
    StreamSubscription<void> subs(Stream<List<int>> stream) {
      return stream
          .transform(utf8.decoder)
          .transform(LineSplitter())
          .listen((String line) async {
        if (verbose) {
          stdout.writeln(line);
        }
        lines.add(line);
        if (await readySignal(line)) {
          await subs1?.cancel();
          subs1 = null;
          await subs2?.cancel();
          subs2 = null;
          if (c.isCompleted) return;
          c.complete();
        }
      });
    }

    subs1 = subs(process.stdout);
    subs2 = subs(process.stderr);

    final dp = DockerProcess._(dockerExecutable, name);

    try {
      await c.future;
    } catch (_) {
      await dp.kill();
      rethrow;
    } finally {
      timer.cancel();
    }
    return dp;
  } else {
    final pr = await Process.run(command, args);
    if (pr.exitCode != 0) {
      stderr.writeAll(lines, '\n');
      throw Exception(
        'exitCode: ${pr.exitCode}\n\n'
        'stdout: ${pr.stdout}\n\n'
        'stderr: ${pr.stderr}',
      );
    }
    return DockerProcess._(dockerExecutable, name);
  }
}