Skip to content

Commit

Permalink
Additional options for daemon deploy config (#269)
Browse files Browse the repository at this point in the history
Signed-off-by: Andrey Borysenko <andrey18106x@gmail.com>
Signed-off-by: Alexander Piskun <bigcat88@icloud.com>
Co-authored-by: Alexander Piskun <13381981+bigcat88@users.noreply.github.com>
  • Loading branch information
andrey18106 and bigcat88 committed Apr 10, 2024
1 parent babc0b7 commit b82394c
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 14 deletions.
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [2.5.0 - 2024-04-xx]
## [2.5.0 - 2024-04-1x]

### Added

- Different compute device configuration for Daemon (NVIDIA, AMD, CPU)
- Different compute device configuration for Daemon (NVIDIA, AMD, CPU). #267
- Ability to add optional parameters when registering a daemon, for example *OVERRIDE_APP_HOST*. #269

### Fixed

- Fixed notification icon absolute url. #268

## [2.4.0 - 2024-04-04]

Expand Down
41 changes: 33 additions & 8 deletions docs/DeployConfigurations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -264,13 +264,10 @@ It has `fixed parameters <https://github.com/cloud-py-api/app_api/blob/main/lib/
* Accepts Deploy ID: ``docker-install``
* Protocol: ``http``
* Host: ``nextcloud-aio-docker-socket-proxy:2375``
* GPUs support: ``false``
* Compute device: ``CPU``
* Network: ``nextcloud-aio``
* Nextcloud URL (passed to ExApps): ``https://$NC_DOMAIN``

.. note::
If ``NEXTCLOUD_ENABLE_DRI_DEVICE=true`` is set - separate DaemonConfig (``docker_aio_gpu``) will be created with ``gpus=true``.

Docker Socket Proxy security
****************************

Expand Down Expand Up @@ -298,6 +295,17 @@ where:
* **deployConfig** can be custom for each Daemon type
* **auth** is an optional array, with *Basic Authentication* data if needed to access ExApp

.. note::

Starting with AppAPI version ``2.5.0``, the optional additional parameter *OVERRIDE_APP_HOST* can be used to
override the host that will be used for ExApp binding.

It can be ``0.0.0.0`` in some specific configurations, when VPN is used
or both Nextcloud instance and ExApps are one the same physical machine but different virtual environments.

Also you can specify something like ``10.10.2.5`` and in this case ``ExApp`` wil try to bind to that address and
AppAPI will try to send request s directly to this address assuming that ExApp itself bound on it.

The simplest implementation is in **Manual-Install** deploy type:

.. code-block:: php
Expand All @@ -306,10 +314,18 @@ The simplest implementation is in **Manual-Install** deploy type:
string $appId, string $protocol, string $host, array $deployConfig, int $port, array &$auth
): string {
$auth = [];
if (isset($deployConfig['additional_options']['OVERRIDE_APP_HOST']) &&
$deployConfig['additional_options']['OVERRIDE_APP_HOST'] !== ''
) {
$wideNetworkAddresses = ['0.0.0.0', '127.0.0.1', '::', '::1'];
if (!in_array($deployConfig['additional_options']['OVERRIDE_APP_HOST'], $wideNetworkAddresses)) {
$host = $deployConfig['additional_options']['OVERRIDE_APP_HOST'];
}
}
return sprintf('%s://%s:%s', $protocol, $host, $port);
}
Here we see that AppAPI always send requests to **host**:**port** specified during daemon creation.
Here we see that AppAPI send requests to **host**:**port** specified during daemon creation.

Now let's take a look at the Docker Daemon implementation of ``resolveExAppUrl``:

Expand All @@ -318,6 +334,17 @@ Now let's take a look at the Docker Daemon implementation of ``resolveExAppUrl``
public function resolveExAppUrl(
string $appId, string $protocol, string $host, array $deployConfig, int $port, array &$auth
): string {
$auth = [];
if (isset($deployConfig['additional_options']['OVERRIDE_APP_HOST']) &&
$deployConfig['additional_options']['OVERRIDE_APP_HOST'] !== ''
) {
$wideNetworkAddresses = ['0.0.0.0', '127.0.0.1', '::', '::1'];
if (!in_array($deployConfig['additional_options']['OVERRIDE_APP_HOST'], $wideNetworkAddresses)) {
return sprintf(
'%s://%s:%s', $protocol, $deployConfig['additional_options']['OVERRIDE_APP_HOST'], $port
);
}
}
$host = explode(':', $host)[0];
if ($protocol == 'https') {
$exAppHost = $host;
Expand All @@ -326,9 +353,7 @@ Now let's take a look at the Docker Daemon implementation of ``resolveExAppUrl``
} else {
$exAppHost = $appId;
}
if (empty($deployConfig['haproxy_password'])) {
$auth = [];
} else {
if (isset($deployConfig['haproxy_password']) && $deployConfig['haproxy_password'] !== '') {
$auth = [self::APP_API_HAPROXY_USER, $deployConfig['haproxy_password']];
}
return sprintf('%s://%s:%s', $protocol, $exAppHost, $port);
Expand Down
15 changes: 12 additions & 3 deletions lib/DeployActions/DockerActions.php
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,17 @@ public function buildDeployEnvs(array $params, array $deployConfig): array {
public function resolveExAppUrl(
string $appId, string $protocol, string $host, array $deployConfig, int $port, array &$auth
): string {
$auth = [];
if (isset($deployConfig['additional_options']['OVERRIDE_APP_HOST']) &&
$deployConfig['additional_options']['OVERRIDE_APP_HOST'] !== ''
) {
$wideNetworkAddresses = ['0.0.0.0', '127.0.0.1', '::', '::1'];
if (!in_array($deployConfig['additional_options']['OVERRIDE_APP_HOST'], $wideNetworkAddresses)) {
return sprintf(
'%s://%s:%s', $protocol, $deployConfig['additional_options']['OVERRIDE_APP_HOST'], $port
);
}
}
$host = explode(':', $host)[0];
if ($protocol == 'https') {
$exAppHost = $host;
Expand All @@ -436,9 +447,7 @@ public function resolveExAppUrl(
} else {
$exAppHost = $appId;
}
if (!isset($deployConfig['haproxy_password']) || $deployConfig['haproxy_password'] === '') {
$auth = [];
} else {
if (isset($deployConfig['haproxy_password']) && $deployConfig['haproxy_password'] !== '') {
$auth = [self::APP_API_HAPROXY_USER, $deployConfig['haproxy_password']];
}
return sprintf('%s://%s:%s', $protocol, $exAppHost, $port);
Expand Down
8 changes: 8 additions & 0 deletions lib/DeployActions/ManualActions.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ public function resolveExAppUrl(
string $appId, string $protocol, string $host, array $deployConfig, int $port, array &$auth
): string {
$auth = [];
if (isset($deployConfig['additional_options']['OVERRIDE_APP_HOST']) &&
$deployConfig['additional_options']['OVERRIDE_APP_HOST'] !== ''
) {
$wideNetworkAddresses = ['0.0.0.0', '127.0.0.1', '::', '::1'];
if (!in_array($deployConfig['additional_options']['OVERRIDE_APP_HOST'], $wideNetworkAddresses)) {
$host = $deployConfig['additional_options']['OVERRIDE_APP_HOST'];
}
}
return sprintf('%s://%s:%s', $protocol, $host, $port);
}
}
3 changes: 3 additions & 0 deletions lib/Service/AppAPICommonService.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ public function buildAppAPIAuthHeaders(?IRequest $request, ?string $userId, stri
}

public function buildExAppHost(array $deployConfig): string {
if (isset($deployConfig['additional_options']['OVERRIDE_APP_HOST'])) {
return $deployConfig['additional_options']['OVERRIDE_APP_HOST'];
}
if (isset($deployConfig['net'])) {
if ($deployConfig['net'] === 'host') {
return '127.0.0.1'; # ExApp using this host network, it is visible for Nextcloud on loop-back adapter
Expand Down
7 changes: 7 additions & 0 deletions src/components/DaemonConfig/DaemonConfigDetailsModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@
<b>{{ t('app_api', 'Compute device') }}:</b> {{ daemon.deploy_config?.computeDevice?.label }}
</p>

<div v-if="daemon.deploy_config.additional_options" class="additional-options">
<h3>{{ t('app_api', 'Additional options') }}</h3>
<p v-for="option_key in Object.keys(daemon.deploy_config.additional_options)" :key="option_key">
<b>{{ option_key }}:</b> {{ daemon.deploy_config.additional_options[option_key] }}
</p>
</div>

<div class="actions">
<NcButton v-if="daemon.accepts_deploy_id !== 'manual-install'" @click="verifyConnection">
{{ t('app_api', 'Verify connection') }}
Expand Down
125 changes: 124 additions & 1 deletion src/components/DaemonConfig/RegisterDaemonConfigModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,84 @@
:aria-label="t('app_api', 'GPUs support enabled hint')">
{{ t('app_api', 'All available GPU devices on daemon host will be requested to be enabled in ExApp containers by Docker.') }}
</p>

<template v-if="additionalOptions.length > 0">
<div class="row" style="flex-direction: column;">
<div
v-for="(option, index) in additionalOptions"
:id="option.key"
:key="index"
class="external-label"
:aria-label="t('app_api', 'Additional option')">
<label :for="option.key">{{ option.key }}</label>
<div class="additional-option">
<NcInputField
:id="option.key"
:value.sync="option.value"
:placeholder="option.value"
:aria-label="option.value"
style="margin: 0 5px 0 0; width: fit-content;" />
<NcButton type="tertiary" @click="removeAdditionalOption(option, index)">
<template #icon>
<Close :size="20" />
</template>
</NcButton>
</div>
</div>
</div>
</template>

<div class="additional-options">
<div style="display: flex; justify-content: flex-end;">
<NcButton type="tertiary" @click="addAdditionalOption">
<template #icon>
<Plus :size="20" />
</template>
{{ t('app_api', 'Add additional option') }}
</NcButton>
</div>
<template v-if="addingAdditionalOption">
<div class="row" style="align-items: start;">
<NcInputField
id="additional-option-key"
ref="additionalOptionKey"
:value.sync="additionalOption.key"
:label="t('app_api', 'Option key (unique)')"
:placeholder="t('app_api', 'Option key (unique, e.g. my_key)')"
:error="additionalOption.key.trim() === ''"
:helper-text="additionalOption.key.trim() === '' ? t('app_api', 'Option key is required') : ''"
style="margin: 0 5px 0 0;" />
<NcInputField
id="additional-option-value"
:value.sync="additionalOption.value"
:label="t('app_api', 'Option value')"
:placeholder="t('app_api', 'Option value')"
:error="additionalOption.value.trim() === ''"
:helper-text="additionalOption.value.trim() === '' ? t('app_api', 'Option value is required') : ''"
style="margin: 0 5px 0 0;" />
<NcButton
type="tertiary"
:aria-label="t('app_api', 'Confirm')"
:disabled="isAdditionalOptionValid === false"
@click="confirmAddingAdditionalOption">
<template #icon>
<Check :size="20" />
</template>
</NcButton>
<NcButton
type="tertiary"
:aria-label="t('app_api', 'Cancel')"
@click="cancelAddingAdditionalOption">
<template #icon>
<Close :size="20" />
</template>
</NcButton>
</div>
</template>
</div>
</div>
</template>

<div class="row">
<NcButton
type="primary"
Expand Down Expand Up @@ -163,6 +239,8 @@ import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
import Check from 'vue-material-design-icons/Check.vue'
import Connection from 'vue-material-design-icons/Connection.vue'
import Plus from 'vue-material-design-icons/Plus.vue'
import Close from 'vue-material-design-icons/Close.vue'
import UnfoldLessHorizontal from 'vue-material-design-icons/UnfoldLessHorizontal.vue'
import UnfoldMoreHorizontal from 'vue-material-design-icons/UnfoldMoreHorizontal.vue'
import { DAEMON_TEMPLATES, DAEMON_COMPUTE_DEVICES } from '../../constants/daemonTemplates.js'
Expand All @@ -180,6 +258,8 @@ export default {
NcButton,
Check,
Connection,
Plus,
Close,
},
props: {
show: {
Expand Down Expand Up @@ -224,6 +304,12 @@ export default {
],
verifyingDaemonConnection: false,
computeDevices: DAEMON_COMPUTE_DEVICES,
addingAdditionalOption: false,
additionalOption: {
key: '',
value: '',
},
additionalOptions: [],
}
},
computed: {
Expand Down Expand Up @@ -267,6 +353,9 @@ export default {
canRegister() {
return this.isDaemonNameValid === true || this.isHaProxyPasswordValid === false
},
isAdditionalOptionValid() {
return this.additionalOption.key.trim() !== '' && this.additionalOption.value.trim() !== ''
},
},
watch: {
configurationTab(newConfigurationTab) {
Expand Down Expand Up @@ -324,7 +413,7 @@ export default {
})
},
_buildDaemonParams() {
return {
const params = {
name: this.name,
display_name: this.displayName,
accepts_deploy_id: this.acceptsDeployId,
Expand All @@ -337,6 +426,13 @@ export default {
computeDevice: this.deployConfig.computeDevice,
},
}
if (this.additionalOptions.length > 0) {
params.deploy_config.additional_options = this.additionalOptions.reduce((acc, option) => {
acc[option.key] = option.value
return acc
}, {})
}
return params
},
setupFormConfiguration(templateName) {
const template = Object.assign({}, DAEMON_TEMPLATES.find(template => template.name === templateName.id))
Expand All @@ -363,6 +459,24 @@ export default {
this.host = DAEMON_TEMPLATES.find(template => template.name === this.configurationTab.id).host || ''
}
},
addAdditionalOption() {
this.addingAdditionalOption = true
this.$nextTick(() => {
this.$refs.additionalOptionKey.focus()
})
},
removeAdditionalOption(option, index) {
this.additionalOptions.splice(index, 1)
},
confirmAddingAdditionalOption() {
this.additionalOptions.push({ key: this.additionalOption.key, value: this.additionalOption.value })
this.addingAdditionalOption = false
this.additionalOption = { key: '', value: '' }
},
cancelAddingAdditionalOption() {
this.addingAdditionalOption = false
this.additionalOption = { key: '', value: '' }
},
closeModal() {
const customTemplate = DAEMON_TEMPLATES.find(template => template.name === 'custom')
this.configurationTab = { id: customTemplate.name, label: customTemplate.displayName }
Expand Down Expand Up @@ -417,5 +531,14 @@ export default {
border-bottom: 1px solid var(--color-border-dark);
padding-bottom: 20px;
}
.additional-options {
margin: 20px 0;
padding: 10px 0;
}
.additional-option {
display: flex;
}
}
</style>

0 comments on commit b82394c

Please sign in to comment.