run method
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();
}