Skip to content

Commit

Permalink
small general adjustments for AIO compatibility (#63)
Browse files Browse the repository at this point in the history
Signed-off-by: Andrey Borysenko <andrey18106x@gmail.com>
  • Loading branch information
andrey18106 authored Sep 4, 2023
1 parent 337eb2f commit 8e94d8a
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 27 deletions.
20 changes: 10 additions & 10 deletions .github/workflows/tests-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ jobs:
- name: Save container ingo & logs
if: always()
run: |
docker inspect skeleton | json_pp > container.json
docker logs skeleton > container.log 2>&1
docker inspect nc_app_skeleton | json_pp > container.json
docker logs nc_app_skeleton > container.log 2>&1
- name: Upload Container info
if: always()
Expand Down Expand Up @@ -188,8 +188,8 @@ jobs:
- name: Save container ingo & logs
if: always()
run: |
docker inspect skeleton | json_pp > container.json
docker logs skeleton > container.log 2>&1
docker inspect nc_app_skeleton | json_pp > container.json
docker logs nc_app_skeleton > container.log 2>&1
- name: Upload Container info
if: always()
Expand Down Expand Up @@ -266,8 +266,8 @@ jobs:
- name: Save container ingo & logs
if: always()
run: |
docker inspect skeleton | json_pp > container.json
docker logs skeleton > container.log 2>&1
docker inspect nc_app_skeleton | json_pp > container.json
docker logs nc_app_skeleton > container.log 2>&1
- name: Upload Container info
if: always()
Expand Down Expand Up @@ -343,8 +343,8 @@ jobs:
- name: Save container ingo & logs
if: always()
run: |
docker inspect skeleton | json_pp > container.json
docker logs skeleton > container.log 2>&1
docker inspect nc_app_skeleton | json_pp > container.json
docker logs nc_app_skeleton > container.log 2>&1
- name: Upload Container info
if: always()
Expand Down Expand Up @@ -482,8 +482,8 @@ jobs:
- name: Save container ingo & logs
if: always()
run: |
docker inspect skeleton | json_pp > container.json
docker logs skeleton > container.log 2>&1
docker inspect nc_app_skeleton | json_pp > container.json
docker logs nc_app_skeleton > container.log 2>&1
- name: Check redis keys
run: |
Expand Down
42 changes: 42 additions & 0 deletions docs/DeployConfigurations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,45 @@ In this scenario, Nextcloud is installed within a container, and a separate Daem
class ExApp3 python

In this case, the AppEcosystem (Nextcloud) uses ``socket`` to interact with Docker.

Nextcloud in Docker AIO (all-in-one)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

In case of AppEcosystemV2 is in Docker AIO setup (installed in Nextcloud container).

.. note::

AIO Docker Socket Proxy container must be enabled.

.. mermaid::

stateDiagram-v2
classDef docker fill: #1f97ee, color: transparent, font-size: 34px, stroke: #364c53, stroke-width: 1px, background: url(https://raw.githubusercontent.com/cloud-py-api/app_ecosystem_v2/main/docs/img/docker.png) no-repeat center center / contain
classDef docker2 fill: #1f97ee, color: transparent, font-size: 20px, stroke: #364c53, stroke-width: 1px, background: url(https://raw.githubusercontent.com/cloud-py-api/app_ecosystem_v2/main/docs/img/docker.png) no-repeat center center / contain
classDef nextcloud fill: #006aa3, color: transparent, font-size: 34px, stroke: #045987, stroke-width: 1px, background: url(https://raw.githubusercontent.com/cloud-py-api/app_ecosystem_v2/main/docs/img/nextcloud.svg) no-repeat center center / contain
classDef python fill: #1e415f, color: white, stroke: #364c53, stroke-width: 1px

Host

state Host {
Daemon --> Containers

state Containers {
[*] --> NextcloudAIOMasterContainer : /var/run/docker.sock
NextcloudAIOMasterContainer --> Nextcloud
AppEcosystemV2 --> Nextcloud : installed in
Nextcloud --> NextcloudAIOMasterContainer
NextcloudAIOMasterContainer --> ExApp1
NextcloudAIOMasterContainer --> ExApp2
NextcloudAIOMasterContainer --> ExApp3
}
}

class Nextcloud nextcloud
class Daemon docker
class Daemon2 docker2
class ExApp1 python
class ExApp2 python
class ExApp3 python

AppEcosystemV2 will automatically create default default DaemonConfig to use AIO Docker Socket Proxy as orchestrator to create ExApp containers.
2 changes: 1 addition & 1 deletion lib/Command/ExApp/Deploy.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}

if (!isset($startResult['error']) && isset($createResult['Id'])) {
if (!$this->dockerActions->healthcheckContainer($createResult['Id'], $daemonConfig)) {
if (!$this->dockerActions->healthcheckContainer($this->dockerActions->buildExAppContainerName($appId), $daemonConfig)) {
$output->writeln(sprintf('ExApp %s deployment failed. Error: %s', $appId, 'Container healthcheck failed.'));
return 1;
}
Expand Down
4 changes: 2 additions & 2 deletions lib/Command/ExApp/Unregister.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
if ($daemonConfig->getAcceptsDeployId() === $this->dockerActions->getAcceptsDeployId()) {
$this->dockerActions->initGuzzleClient($daemonConfig);
[$stopResult, $removeResult] = $this->dockerActions->removePrevExAppContainer($this->dockerActions->buildDockerUrl($daemonConfig), $appId);
[$stopResult, $removeResult] = $this->dockerActions->removePrevExAppContainer($this->dockerActions->buildDockerUrl($daemonConfig), $this->dockerActions->buildExAppContainerName($appId));
if (isset($stopResult['error']) || isset($removeResult['error'])) {
$output->writeln(sprintf('Failed to remove ExApp %s container', $appId));
} else {
$rmData = $input->getOption('rm-data');
if ($rmData) {
$removeVolumeResult = $this->dockerActions->removeVolume($this->dockerActions->buildDockerUrl($daemonConfig), $appId . '_data');
$removeVolumeResult = $this->dockerActions->removeVolume($this->dockerActions->buildDockerUrl($daemonConfig), $this->dockerActions->buildExAppVolumeName($appId));
if (isset($removeVolumeResult['error'])) {
$output->writeln(sprintf('Failed to remove ExApp %s volume %s', $appId, $appId . '_data'));
}
Expand Down
1 change: 0 additions & 1 deletion lib/Command/ExApp/Update.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$deployParams = $this->dockerActions->buildDeployParams($daemonConfig, $infoXml, [
'container_info' => $containerInfo,
]);
$deployParams['prev_container_id'] = $containerInfo['Id'];
[$pullResult, $stopResult, $removeResult, $createResult, $startResult] = $this->dockerActions->updateExApp($daemonConfig, $deployParams);

if (isset($pullResult['error'])) {
Expand Down
88 changes: 88 additions & 0 deletions lib/DeployActions/AIODockerActions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

declare(strict_types=1);

namespace OCA\AppEcosystemV2\DeployActions;

use OCA\AppEcosystemV2\Db\DaemonConfig;
use OCA\AppEcosystemV2\Service\DaemonConfigService;
use OCP\IConfig;

/**
* Class with utils methods for AIO setup
*/
class AIODockerActions {
public const AIO_DAEMON_CONFIG_NAME = 'docker_aio';
public const AIO_DOCKER_SOCKET_PROXY_HOST = 'nextcloud-aio-docker-socket-proxy:2375';
private IConfig $config;
private DaemonConfigService $daemonConfigService;

public function __construct(
IConfig $config,
DaemonConfigService $daemonConfigService
) {
$this->config = $config;
$this->daemonConfigService = $daemonConfigService;
}

/**
* Detecting AIO instance by config setting or AIO_TOKEN env as fallback
*
* @return bool
*/
public function isAIO(): bool {
return $this->config->getSystemValue('one-click-instance', false);
}

/**
* Registers DaemonConfig with default params to use AIO Docker Socket Proxy
*
* @return DaemonConfig|null
*/
public function registerAIODaemonConfig(): ?DaemonConfig {
$daemonConfig = $this->daemonConfigService->getDaemonConfigByName(self::AIO_DAEMON_CONFIG_NAME);
if ($daemonConfig !== null) {
return null;
}

$deployConfig = [
'net' => 'nextcloud-aio', // using the same host as default network for Nextcloud AIO containers
'host' => null,
'nextcloud_url' => 'https://' . getenv('NC_DOMAIN'),
'ssl_key' => null,
'ssl_key_password' => null,
'ssl_cert' => null,
'ssl_cert_password' => null,
'gpus' => [],
];

if ($this->isGPUsEnabled()) {
$deployConfig['gpus'] = ['/dev/dri'];
}

$daemonConfigParams = [
'name' => self::AIO_DAEMON_CONFIG_NAME,
'display_name' => 'AIO Docker Socket Proxy',
'accepts_deploy_id' => 'docker-install',
'protocol' => 'http',
'host' => self::AIO_DOCKER_SOCKET_PROXY_HOST,
'deploy_config' => $deployConfig,
];

return $this->daemonConfigService->registerDaemonConfig($daemonConfigParams);
}

/**
* Check if /dev/dri folder mounted to the container.
* In AIO this means that NEXTCLOUD_ENABLE_DRI_DEVICE=true
*
* @return bool
*/
private function isGPUsEnabled(): bool {
$devDri = '/dev/dri';
if (is_dir($devDri)) {
return true;
}
return false;
}
}
38 changes: 26 additions & 12 deletions lib/DeployActions/DockerActions.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class DockerActions implements IDeployActions {
'IS_SYSTEM_APP',
'NEXTCLOUD_URL',
];
public const EX_APP_CONTAINER_PREFIX = 'nc_app_';
private LoggerInterface $logger;
private Client $guzzleClient;
private ICertificateManager $certificateManager;
Expand Down Expand Up @@ -96,9 +97,9 @@ public function deployExApp(DaemonConfig $daemonConfig, array $params = []): arr
return [$pullResult, null, null];
}

$containerInfo = $this->inspectContainer($dockerUrl, $params['container_params']['name']);
$containerInfo = $this->inspectContainer($dockerUrl, $this->buildExAppContainerName($params['container_params']['name']));
if (isset($containerInfo['Id'])) {
[$stopResult, $removeResult] = $this->removePrevExAppContainer($dockerUrl, $containerInfo['Id']);
[$stopResult, $removeResult] = $this->removePrevExAppContainer($dockerUrl, $this->buildExAppContainerName($params['container_params']['name']));
if (isset($stopResult['error']) || isset($removeResult['error'])) {
return [$pullResult, $stopResult, $removeResult];
}
Expand All @@ -109,7 +110,7 @@ public function deployExApp(DaemonConfig $daemonConfig, array $params = []): arr
return [null, $createResult, null];
}

$startResult = $this->startContainer($dockerUrl, $createResult['Id']);
$startResult = $this->startContainer($dockerUrl, $this->buildExAppContainerName($params['container_params']['name']));
return [$pullResult, $createResult, $startResult];
}

Expand All @@ -122,7 +123,7 @@ public function buildImageName(array $imageParams): string {
}

public function createContainer(string $dockerUrl, array $imageParams, array $params = []): array {
$createVolumeResult = $this->createVolume($dockerUrl, $params['name'] . '_data');
$createVolumeResult = $this->createVolume($dockerUrl, $this->buildExAppVolumeName($params['name']));
if (isset($createVolumeResult['error'])) {
return $createVolumeResult;
}
Expand Down Expand Up @@ -154,7 +155,7 @@ public function createContainer(string $dockerUrl, array $imageParams, array $pa
$containerParams['HostConfig']['Devices'] = $this->buildDevicesParams($params['devices']);
}

$url = $this->buildApiUrl($dockerUrl, sprintf('containers/create?name=%s', urlencode($params['name'])));
$url = $this->buildApiUrl($dockerUrl, sprintf('containers/create?name=%s', urlencode($this->buildExAppContainerName($params['name']))));
try {
$options['json'] = $containerParams;
$response = $this->guzzleClient->post($url, $options);
Expand Down Expand Up @@ -227,8 +228,6 @@ public function inspectContainer(string $dockerUrl, string $containerId): array
$response = $this->guzzleClient->get($url);
return json_decode((string) $response->getBody(), true);
} catch (GuzzleException $e) {
//$this->logger->error('Failed to inspect container', ['exception' => $e]);
//error_log($e->getMessage());
return ['error' => $e->getMessage(), 'exception' => $e];
}
}
Expand Down Expand Up @@ -298,7 +297,7 @@ public function updateExApp(DaemonConfig $daemonConfig, array $params = []): arr
return [$pullResult, null, null, null, null];
}

[$stopResult, $removeResult] = $this->removePrevExAppContainer($dockerUrl, $params['prev_container_id']);
[$stopResult, $removeResult] = $this->removePrevExAppContainer($dockerUrl, $this->buildExAppContainerName($params['container_params']['name']));
if (isset($stopResult['error'])) {
return [$pullResult, $stopResult, null, null, null];
}
Expand All @@ -311,7 +310,7 @@ public function updateExApp(DaemonConfig $daemonConfig, array $params = []): arr
return [$pullResult, $stopResult, $removeResult, $createResult, null];
}

$startResult = $this->startContainer($dockerUrl, $createResult['Id']);
$startResult = $this->startContainer($dockerUrl, $this->buildExAppContainerName($params['container_params']['name']));
return [$pullResult, $stopResult, $removeResult, $createResult, $startResult];
}

Expand Down Expand Up @@ -421,7 +420,7 @@ public function buildDeployEnvs(array $params, array $envOptions, array $deployC
*/
public function loadExAppInfo(string $appId, DaemonConfig $daemonConfig, array $params = []): array {
$this->initGuzzleClient($daemonConfig);
$containerInfo = $this->inspectContainer($this->buildDockerUrl($daemonConfig), $appId);
$containerInfo = $this->inspectContainer($this->buildDockerUrl($daemonConfig), $this->buildExAppContainerName($appId));
if (isset($containerInfo['error'])) {
return ['error' => sprintf('Failed to inspect ExApp %s container: %s', $appId, $containerInfo['error'])];
}
Expand Down Expand Up @@ -545,10 +544,25 @@ private function buildDefaultExAppVolume(string $appId): array {
return [
[
'Type' => 'volume',
'Source' => $appId . '_data',
'Target' => '/' . $appId . '_data',
'Source' => $this->buildExAppVolumeName($appId),
'Target' => '/' . $this->buildExAppVolumeName($appId),
'ReadOnly' => false
],
];
}

/**
* Build ExApp container name (prefix + appid)
*
* @param string $appId
*
* @return string
*/
public function buildExAppContainerName(string $appId): string {
return self::EX_APP_CONTAINER_PREFIX . $appId;
}

public function buildExAppVolumeName(string $appId): string {
return self::EX_APP_CONTAINER_PREFIX . $appId . '_data';
}
}
16 changes: 15 additions & 1 deletion lib/Migration/DataInitializationStep.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,22 @@

namespace OCA\AppEcosystemV2\Migration;

use OCA\AppEcosystemV2\DeployActions\AIODockerActions;
use OCA\AppEcosystemV2\Service\ExAppApiScopeService;

use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;

class DataInitializationStep implements IRepairStep {
private ExAppApiScopeService $service;
private AIODockerActions $AIODockerActions;

public function __construct(ExAppApiScopeService $service) {
public function __construct(
ExAppApiScopeService $service,
AIODockerActions $AIODockerActions
) {
$this->service = $service;
$this->AIODockerActions = $AIODockerActions;
}

public function getName(): string {
Expand All @@ -26,5 +32,13 @@ public function run(IOutput $output): void {
} else {
$output->warning('Failed to initialize data for App Ecosystem V2');
}

// If in AIO - automatically register default DaemonConfig
if ($this->AIODockerActions->isAIO()) {
$output->info('AIO installation detected. Registering default daemon');
if ($this->AIODockerActions->registerAIODaemonConfig() !== null) {
$output->info('AIO DaemonConfig successfully registered');
}
}
}
}

0 comments on commit 8e94d8a

Please sign in to comment.