Skip to content

Commit ddebae1

Browse files
authored
Client: Separate plugin file system (#57)
* prepare another setting variable to store plugin coupling thightness * provide two functions to retrieve and push file content to qiita master * add a configurable parameter to define plugincoupling for plugin * codestyle * adapting test to new configuration option * start working on tests * check spelling * fixing existing test * wrong list index * initial tests + docstrings * use a prefix user has access to * remove / * capitalize comments * use qiita branch * specify directory name * add testing https protocol * fix protected * operate on file copy * debug * avoid // * remove trailing / * remove debug, activate endpoint, reactivate all tests * set path to / of only filename is given * fix typo * enable environment variable, configuration file and default for "plugincoupling" * revert to qiita dev and remove ENABLE_HTTPS_PLUGIN_FILETRANSFER * address Antonio's great suggestions * promoting comment to part of readme * fix formatting * "not prefix" did not match properly
1 parent ccbfa94 commit ddebae1

File tree

6 files changed

+308
-26
lines changed

6 files changed

+308
-26
lines changed

.github/workflows/qiita-ci.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ jobs:
9393
conda activate qiita
9494
export QIITA_ROOTCA_CERT=`pwd`/qiita-dev/qiita_core/support_files/ci_rootca.crt
9595
export QIITA_CONFIG_FP=`pwd`/qiita-dev/qiita_core/support_files/config_test_local.cfg
96-
sed "s#/home/runner/work/qiita/qiita#${PWD}/qiita-dev/#g" `pwd`/qiita-dev/qiita_core/support_files/config_test.cfg > ${QIITA_CONFIG_FP}
96+
sed "s#/home/runner/work/qiita/qiita#${PWD}/qiita-dev#g" `pwd`/qiita-dev/qiita_core/support_files/config_test.cfg > ${QIITA_CONFIG_FP}
9797
9898
export REDBIOM_HOST="http://localhost:7379"
9999
@@ -104,7 +104,8 @@ jobs:
104104
mkdir -p ${CONDA_PREFIX}/var/run/nginx/
105105
export NGINX_FILE=`pwd`/qiita-dev/qiita_pet/nginx_example.conf
106106
export NGINX_FILE_NEW=`pwd`/qiita-dev/qiita_pet/nginx_example_local.conf
107-
sed "s#/home/runner/work/qiita/qiita#${PWD}/qiita-dev/#g" ${NGINX_FILE} > ${NGINX_FILE_NEW}
107+
sed "s#/home/runner/work/qiita/qiita#${PWD}/qiita-dev#g" ${NGINX_FILE} > ${NGINX_FILE_NEW}
108+
sed -i "s#/Users/username/qiita#${PWD}/qiita-dev#g" ${NGINX_FILE_NEW}
108109
nginx -c ${NGINX_FILE_NEW}
109110
110111
echo "3. Setting up qiita"

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,32 @@ Also, if Qiita is running with the default server SSL certificate, you need to e
1616

1717
export QIITA_ROOT_CA=<QIITA_INSTALL_PATH>/qiita_core/support_files/ci_rootca.crt
1818
```
19+
20+
Configure for cloud computing
21+
-----------------------------
22+
In the default scenario, Qiita main and Qiita plugins are executed on the same
23+
machines, maybe spread across a Slurm or other grid compute cluster, but main
24+
and plugins have direct access to all files in `BASE_DATA_DIR`.
25+
26+
This can be different, if you set up Qiita within a cloud compute environment,
27+
where main and plugins do **not** share one file system. In this case, input-
28+
files must first be transferred from main to plugin, then plugin can do its
29+
processing and resulting files must be transferred back to main, once
30+
processing is finished. To achieve this, the qiita_client, as it is part of
31+
each plugin, provides the two functions for this file transfer
32+
`fetch_file_from_central` and `push_file_to_central`. According to
33+
`self._plugincoupling`, these functions operate on different "protocols";
34+
as of 2025-08-29, either "filesystem" or "https". Switch to **"https"** for
35+
cloud environments, default is **filesystem**.
36+
37+
The plugin coupling protocoll can be set in three ways
38+
39+
1. default is always `filesystem`, i.e. `_DEFAULT_PLUGIN_COUPLINGS`
40+
This is to be downward compatible.
41+
2. the plugin configuration can hold a section `network` with an
42+
option `PLUGINCOUPLING`. For old config files, this might not
43+
(yet) be the case. Therefore, we are double checking existance
44+
of this section and parameter here.
45+
3. you can set the environment variable `QIITA_PLUGINCOUPLING`
46+
Precedence is 3, 2, 1, i.e. the environment variable overrides
47+
the other two ways.

qiita_client/plugin.py

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,46 @@ def __init__(self, name, description, can_be_submitted_to_ebi,
136136

137137

138138
class BaseQiitaPlugin(object):
139-
def __init__(self, name, version, description, publications=None):
139+
_DEFAULT_PLUGIN_COUPLINGS = 'filesystem'
140+
_ALLOWED_PLUGIN_COUPLINGS = [_DEFAULT_PLUGIN_COUPLINGS, 'https']
141+
142+
def __init__(self, name, version, description, publications=None,
143+
plugincoupling=_DEFAULT_PLUGIN_COUPLINGS):
140144
logger.debug('Entered BaseQiitaPlugin.__init__()')
141145
self.name = name
142146
self.version = version
143147
self.description = description
144148
self.publications = dumps(publications) if publications else ""
145149

150+
# Depending on your compute architecture, there are multiple options
151+
# available how "thight" plugins are coupled to the central
152+
# Qiita master/workers
153+
# --- filesystem ---
154+
# The default scenario is "filesystem", i.e. plugins as well as
155+
# master/worker have unrestricted direct access to a shared filesystem,
156+
# e.g. a larger volume / directory, defined in the server configuration
157+
# as base_data_dir
158+
# --- https ---
159+
# A second scenario is that your plugins execute as independent jobs on
160+
# another machine, e.g. as docker containers or other cloud techniques.
161+
# Intentionally, you don't want to use a shared filesystem, but you
162+
# have to make sure necessary input files are provided to the
163+
# containerized plugin before execution and resulting files are
164+
# transfered back to the central Qiita master/worker. In this case,
165+
# files are pulled / pushed through functions
166+
# qiita_client.fetch_file_from_central and
167+
# qiita_client.push_file_to_central, respectivey.
168+
# Actually, all files need to be decorated with this function.
169+
# The decision how data are transferred is then made within these two
170+
# functions according to the "plugincoupling" setting.
171+
if plugincoupling not in self._ALLOWED_PLUGIN_COUPLINGS:
172+
raise ValueError(
173+
("valid plugincoupling values are ['%s'], but you "
174+
"provided %s") % (
175+
"', '".join(self._ALLOWED_PLUGIN_COUPLINGS),
176+
plugincoupling))
177+
self.plugincoupling = plugincoupling
178+
146179
# Will hold the different commands
147180
self.task_dict = {}
148181

@@ -151,7 +184,8 @@ def __init__(self, name, version, description, publications=None):
151184
'QIITA_PLUGINS_DIR', join(expanduser('~'), '.qiita_plugins'))
152185
self.conf_fp = join(conf_dir, "%s_%s.conf" % (self.name, self.version))
153186

154-
def generate_config(self, env_script, start_script, server_cert=None):
187+
def generate_config(self, env_script, start_script, server_cert=None,
188+
plugin_coupling=_DEFAULT_PLUGIN_COUPLINGS):
155189
"""Generates the plugin configuration file
156190
157191
Parameters
@@ -165,6 +199,9 @@ def generate_config(self, env_script, start_script, server_cert=None):
165199
If the Qiita server used does not have a valid certificate, the
166200
path to the Qiita certificate so the plugin can connect over
167201
HTTPS to it
202+
plugin_coupling : str
203+
Type of coupling of plugin to central for file exchange.
204+
Valid values: see _ALLOWED_PLUGIN_COUPLINGS.
168205
"""
169206
logger.debug('Entered BaseQiitaPlugin.generate_config()')
170207
sr = SystemRandom()
@@ -178,7 +215,8 @@ def generate_config(self, env_script, start_script, server_cert=None):
178215
f.write(CONF_TEMPLATE % (self.name, self.version, self.description,
179216
env_script, start_script,
180217
self._plugin_type, self.publications,
181-
server_cert, client_id, client_secret))
218+
server_cert, client_id, client_secret,
219+
plugin_coupling))
182220

183221
def _register_command(self, command):
184222
"""Registers a command in the plugin
@@ -188,8 +226,8 @@ def _register_command(self, command):
188226
command: QiitaCommand
189227
The command to be added to the plugin
190228
"""
191-
logger.debug(
192-
f'Entered BaseQiitaPlugin._register_command({command.name})')
229+
logger.debug('Entered BaseQiitaPlugin._register_command(%s)' %
230+
command.name)
193231
self.task_dict[command.name] = command
194232

195233
def _register(self, qclient):
@@ -244,14 +282,23 @@ def __call__(self, server_url, job_id, output_dir):
244282
with open(self.conf_fp, 'U') as conf_file:
245283
config.readfp(conf_file)
246284

285+
plugincoupling = self._DEFAULT_PLUGIN_COUPLINGS
286+
if config.has_section('network') and \
287+
config.has_option('network', 'PLUGINCOUPLING'):
288+
plugincoupling = config.get('network', 'PLUGINCOUPLING')
289+
if 'QIITA_PLUGINCOUPLING' in environ.keys() and \
290+
environ['QIITA_PLUGINCOUPLING'] is not None:
291+
plugincoupling = environ['QIITA_PLUGINCOUPLING']
292+
247293
qclient = QiitaClient(server_url, config.get('oauth2', 'CLIENT_ID'),
248294
config.get('oauth2', 'CLIENT_SECRET'),
249295
# for this group of tests, confirm optional
250296
# ca_cert parameter works as intended. Setting
251297
# this value will prevent underlying libraries
252298
# from validating the server's cert using
253299
# certifi's pem cache.
254-
ca_cert=config.get('oauth2', 'SERVER_CERT'))
300+
ca_cert=config.get('oauth2', 'SERVER_CERT'),
301+
plugincoupling=plugincoupling)
255302

256303
if job_id == 'register':
257304
self._register(qclient)
@@ -314,9 +361,11 @@ class QiitaTypePlugin(BaseQiitaPlugin):
314361
_plugin_type = "artifact definition"
315362

316363
def __init__(self, name, version, description, validate_func,
317-
html_generator_func, artifact_types, publications=None):
364+
html_generator_func, artifact_types, publications=None,
365+
plugincoupling=BaseQiitaPlugin._DEFAULT_PLUGIN_COUPLINGS):
318366
super(QiitaTypePlugin, self).__init__(name, version, description,
319-
publications=publications)
367+
publications=publications,
368+
plugincoupling=plugincoupling)
320369

321370
logger.debug('Entered QiitaTypePlugin.__init__()')
322371
self.artifact_types = artifact_types
@@ -382,4 +431,8 @@ def register_command(self, command):
382431
[oauth2]
383432
SERVER_CERT = %s
384433
CLIENT_ID = %s
385-
CLIENT_SECRET = %s"""
434+
CLIENT_SECRET = %s
435+
436+
[network]
437+
PLUGINCOUPLING = %s
438+
"""

0 commit comments

Comments
 (0)