run method

  1. @override
Future<void> run()
override

Runs this command.

The return value is wrapped in a Future if necessary and returned by CommandRunner.runCommand.

Implementation

@override
Future<void> run() async {
  final args = argResults!;
  validateNodeStartArgs(args);
  final hub = args['hub'] as String;
  final id = args['id'] as String;
  final token = args['token'] as String;
  final ca = args['ca'] as String?;
  final context = ca == null
      ? null
      : (SecurityContext(withTrustedRoots: false)
          ..setTrustedCertificates(ca));

  final registry = FormulaRegistry.standard();
  final formulaService = NodeFormulaService(registry: registry);
  final updateService = const UpdateService();
  const monitor = SystemMonitor();
  final scanner = CapabilityScanner.standard();

  // The agent's log goes to this terminal *and*, unless asked not to, to the
  // Hub — so an operator can read what a node is doing without logging into
  // it. The shipper is late-bound because it needs the agent, and the agent's
  // config needs the logger.
  LogShipper? shipper;
  void log(String message) {
    stdout.writeln(message);
    shipper?.add(message);
  }

  final agentConfig = NodeAgentConfig(
    hubUri: Uri.parse(hub),
    nodeId: id,
    credentials: TokenCredentialProvider(
      principal: args['principal'] as String,
      token: token,
    ),
    securityContext: context,
    onBadCertificate: (args['insecure'] as bool)
        ? (cert, host, port) => true
        : null,
    labels: _parseLabels(args['label'] as List<String>),
    statusProvider: monitor.snapshot,
    capabilityProvider: scanner.scan,
    formulaHandler: formulaService.runFormula,
    presetHandler: formulaService.applyPreset,
    nodeControlHandler: updateService.handle,
    logger: log,
    verbose: args['verbose'] as bool,
  );
  final agent = NodeAgent(agentConfig);
  if (args['ship-logs'] as bool) {
    shipper = LogShipper(send: agent.sendLogs);
  }
  // Say what it is doing before it blocks: start() only returns once the node
  // has registered, so without this a slow — or rejected — connection is a
  // silent hang. The runtime's logger (wired in NodeAgent) then reports any
  // failure and its reason.
  log('Connecting to ${agentConfig.controlUri} …');
  await agent.start();
  log('Node "$id" connected to $hub.');

  // The same machine, also serving shell sessions — one process, one service
  // unit, one supervision target. It is an independent runtime speaking
  // OmnyShell's protocol on the Hub's shell mount; the two share only the
  // credentials and the certificate.
  final shellNode = (args['with-shell'] as bool)
      ? await _startShellNode(
          hubUri: Uri.parse(hub),
          shellPath: args['shell-path'] as String,
          nodeId: id,
          principal: args['principal'] as String,
          token: token,
          securityContext: context,
          insecure: args['insecure'] as bool,
          labels: _parseLabels(args['shell-label'] as List<String>),
        )
      : null;

  stdout.writeln('Press Ctrl-C to stop.');
  await _awaitSignal();
  shipper?.close();
  await shellNode?.shutdown();
  await agent.stop();
}