setName('app_api:daemon:register'); $this->setDescription('Register daemon config for ExApp deployment'); $this->addArgument('name', InputArgument::REQUIRED, 'Unique deploy daemon name'); $this->addArgument('display-name', InputArgument::REQUIRED); $this->addArgument('accepts-deploy-id', InputArgument::REQUIRED, 'The deployment method that the daemon accepts. Can be "manual-install" or "docker-install". "docker-install" is for Docker Socket Proxy and HaRP.'); $this->addArgument('protocol', InputArgument::REQUIRED, 'The protocol used to connect to the daemon. Can be "http" or "https".'); $this->addArgument('host', InputArgument::REQUIRED, 'The hostname (and port) or path at which the Docker socket proxy or HaRP or the manual-install app is/would be available. This does not need to be a public host, just a host accessible by the Nextcloud server. It can also be a path to the Docker socket. (e.g. appapi-harp:8780, /var/run/docker.sock)'); $this->addArgument('nextcloud_url', InputArgument::REQUIRED); // daemon-config settings $this->addOption('net', null, InputOption::VALUE_REQUIRED, 'The name of the Docker network the ex-apps installed by this daemon should use. Default is "host".'); $this->addOption('haproxy_password', null, InputOption::VALUE_REQUIRED, 'AppAPI Docker Socket Proxy password for HAProxy Basic auth. Only for Docker Socket Proxy daemons.'); $this->addOption('compute_device', null, InputOption::VALUE_REQUIRED, 'Computation device for GPU support (cpu|cuda|rocm)'); $this->addOption('set-default', null, InputOption::VALUE_NONE, 'Set DaemonConfig as default'); $this->addOption('harp', null, InputOption::VALUE_NONE, 'Set the daemon to use HaRP for all Docker and ExApp communication'); $this->addOption('harp_frp_address', null, InputOption::VALUE_REQUIRED, '[host]:[port] of the HaRP FRP server, the default host is same as the HaRP host, port is 8782'); $this->addOption('harp_shared_key', null, InputOption::VALUE_REQUIRED, 'HaRP shared key for secure communication between HaRP and AppAPI'); $this->addOption('harp_docker_socket_port', null, InputOption::VALUE_REQUIRED, '\'remotePort\' of the FRP client of the remote Docker socket proxy. There is one included in the harp container so this can be skipped for default setups.', '24000'); $this->addOption('harp_exapp_direct', null, InputOption::VALUE_NONE, 'Flag for the advanced setups only. Disables the FRP tunnel between ExApps and HaRP.'); $this->addUsage('harp_proxy_docker "Harp Proxy (Docker)" "docker-install" "http" "appapi-harp:8780" "http://nextcloud.local" --net nextcloud --harp --harp_frp_address "appapi-harp:8782" --harp_shared_key "some_very_secure_password" --set-default --compute_device=cuda'); $this->addUsage('harp_proxy_host "Harp Proxy (Host)" "docker-install" "http" "localhost:8780" "http://nextcloud.local" --harp --harp_frp_address "localhost:8782" --harp_shared_key "some_very_secure_password" --set-default --compute_device=cuda'); $this->addUsage('manual_install_harp "Harp Manual Install" "manual-install" "http" "appapi-harp:8780" "http://nextcloud.local" --net nextcloud --harp --harp_frp_address "appapi-harp:8782" --harp_shared_key "some_very_secure_password"'); $this->addUsage('docker_install "Docker Socket Proxy" "docker-install" "http" "nextcloud-appapi-dsp:2375" "http://nextcloud.local" --net=nextcloud --set-default --compute_device=cuda'); $this->addUsage('manual_install "Manual Install" "manual-install" "http" null "http://nextcloud.local"'); $this->addUsage('local_docker "Docker Local" "docker-install" "http" "/var/run/docker.sock" "http://nextcloud.local" --net=nextcloud'); $this->addUsage('local_docker "Docker Local" "docker-install" "http" "/var/run/docker.sock" "http://nextcloud.local" --net=nextcloud --set-default --compute_device=cuda'); } protected function execute(InputInterface $input, OutputInterface $output): int { $name = $input->getArgument('name'); $displayName = $input->getArgument('display-name'); $acceptsDeployId = $input->getArgument('accepts-deploy-id'); $protocol = $input->getArgument('protocol'); $host = $input->getArgument('host'); $nextcloudUrl = $input->getArgument('nextcloud_url'); $isHarp = $input->getOption('harp'); if (($protocol !== 'http') && ($protocol !== 'https')) { $output->writeln('Value error: The protocol must be `http` or `https`.'); return 1; } if ($isHarp && !$input->getOption('harp_shared_key')) { $output->writeln('Value error: HaRP enabled daemon requires `harp_shared_key` option.'); return 1; } if ($isHarp && !$input->getOption('harp_frp_address')) { $output->writeln('Value error: HaRP enabled daemon requires `harp_frp_address` option.'); return 1; } if ($acceptsDeployId === 'manual-install' && !$isHarp && str_contains($host, ':')) { $output->writeln('Warning: The host contains a port, which will be ignored for manual-install daemons. The ExApp\'s port from --json-info will be used instead.'); } if ($this->daemonConfigService->getDaemonConfigByName($name) !== null) { $output->writeln(sprintf('Registration skipped, as the daemon config `%s` already exists.', $name)); return 0; } $secret = $isHarp ? $input->getOption('harp_shared_key') : $input->getOption('haproxy_password') ?? ''; $deployConfig = [ 'net' => $input->getOption('net') ?? 'host', 'nextcloud_url' => $nextcloudUrl, 'haproxy_password' => $secret, 'computeDevice' => $this->buildComputeDevice($input->getOption('compute_device') ?? 'cpu'), 'harp' => null, ]; if ($isHarp) { $deployConfig['harp'] = [ 'frp_address' => $input->getOption('harp_frp_address') ?? '', 'docker_socket_port' => $input->getOption('harp_docker_socket_port'), 'exapp_direct' => (bool)$input->getOption('harp_exapp_direct'), ]; } $daemonConfig = $this->daemonConfigService->registerDaemonConfig([ 'name' => $name, 'display_name' => $displayName, 'accepts_deploy_id' => $acceptsDeployId, 'protocol' => $protocol, 'host' => $host, 'deploy_config' => $deployConfig, ]); if ($daemonConfig === null) { $output->writeln('Failed to register the daemon config.'); return 1; } if ($input->getOption('set-default')) { $this->config->setValueString(Application::APP_ID, 'default_daemon_config', $daemonConfig->getName(), lazy: true); } $output->writeln('Daemon config successfully registered.'); return 0; } private function buildComputeDevice(string $computeDevice): array { switch ($computeDevice) { case 'cpu': return [ 'id' => 'cpu', 'label' => 'CPU', ]; case 'cuda': return [ 'id' => 'cuda', 'label' => 'CUDA (NVIDIA)', ]; case 'rocm': return [ 'id' => 'rocm', 'label' => 'ROCm (AMD)', ]; default: throw new \InvalidArgumentException('Invalid compute device value.'); } } }