"git@gitlab.cesnet.cz:madcatxster/molstar.git" did not exist on "ddaa970f8df23d5eddc5e6345b9c48cbe2cfd616"
Newer
Older
.. _section-development:
Development
================================================================================
This is the documentation for developers of the Mentat library itself, or developers
of components and modules usable by or pluggable into the Mentat system.
Key information
--------------------------------------------------------------------------------
* `Project Mentat: official website <https://mentat.cesnet.cz/en/index>`__
* `Project Warden: official website <https://warden.cesnet.cz/en/index>`__
* `IDEA: official website <https://idea.cesnet.cz/en/index>`__
* `Project issue tracking system (Redmine) <https://homeproj.cesnet.cz/projects/mentat>`__
* `Primary source code repository (Git) <https://homeproj.cesnet.cz/git/mentat-ng.git/>`__
* `Automated build system (Alchemist) <https://alchemist.cesnet.cz>`__
General guidelines
--------------------------------------------------------------------------------
* Let `PEP 20 <https://www.python.org/dev/peps/pep-0020/>`__ be the guide for your mind.
* Let `PEP 8 <https://www.python.org/dev/peps/pep-0008/>`__ be the guide for your hand.
* Let you and `PEP 257 <https://www.python.org/dev/peps/pep-0257/>`__ and `PEP 287 <https://www.python.org/dev/peps/pep-0287/>`__ be the guide for others.
* Use `Sphinx-doc <http://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html>`__ format to document the code.
* Pull and merge often.
* Use *devel* branch for small updates and bugfixes.
* For bigger features fork *devel*, merge after accepting, delete branch.
* Use *release* branch only for code that is ready to be released into production.
* Use *master* branch only for production level and stable code.
* *master* and *release* branches **must not** break unittests, lint or build in general. *devel* branch should not.
* Unless you have been explicitly allowed to, do not use *master* and *release* branches.
* New feature should be accompanied with unit tests.
* Do not introduce new dependencies into core library. Dependent code should go into its own submodule, so dependency can be runtime and enforced by system administrator if necessary, but not by library.
* Reuse existing (even soft) dependencies. There is no need to use three competing IP address libraries. However, do not prevent application developer to use different one in his app, should he need to.
--------------------------------------------------------------------------------
There is a project master *Makefile* in the root of the project repository which
can perform various essential or useful development tasks. You can get the full
list of all available make commands/targets by executing one of the following
commands::
Preparing development environment
````````````````````````````````````````````````````````````````````````````````
There are several development prerequisites, that already have to be present on
your development machine. These prerequisites are not installed automatically
for you, because the installation is too complex. There prerequisites are:
* `Python 3 <https://www.python.org/>`__: Please use version similar to current stable Python3 release on current stable Debian release.
* `Pip <https://pip.pypa.io/en/stable/>`__: Python package manager, we recommend installation with `get-pip.py <https://pip.pypa.io/en/stable/installing/#installing-with-get-pip-py>`__.
* `Yarn <https://yarnpkg.com/en/>`__: NPM package manager for web interface libraries.
* `PostgreSQL 11 <https://www.postgresql.org/>`__: Relational database, please use version 11 wherever possible.
You can check for presence of all of these dependencies with this handy make target:
# Check for presence of all prerequisites:
$ make deps-prerequisites
Now please execute the following command from the root directory of the repository to
initialize correct Python virtual environment, install all requirements (including
those only for development) and finally install the project locally in editable mode:
# Perform all installations:
$ make develop
# Activate virtual environment before any development work:
$ . venv/bin/activate
# Deactivate virtual environment when not needed with:
(venv) $ deactivate
Checking code with Pyflakes
````````````````````````````````````````````````````````````````````````````````
You may check the whole codebase with `Pyflakes <https://github.com/PyCQA/pyflakes>`__
tool by executing following command:
.. code-block:: shell
# Always make sure your virtual environment is activated:
$ . venv/bin/activate
# Run tests:
(venv) $ make pyflakes
Or you may check just the single file by executing following command:
.. code-block:: shell
# Always make sure your virtual environment is activated:
$ . venv/bin/activate
# Run tests:
(venv) $ cd lib
(venv) $ pyflakes path/to/module.py
Checking code with Pylint
````````````````````````````````````````````````````````````````````````````````
You may check the whole codebase with `Pylint <https://pylint.readthedocs.io/en/latest/>`__
tool by executing following command:
.. code-block:: shell
# Always make sure your virtual environment is activated:
$ . venv/bin/activate
# Run tests:
(venv) $ make pylint
Or you may check just the single file by executing following command:
.. code-block:: shell
# Always make sure your virtual environment is activated:
$ . venv/bin/activate
# Run tests:
(venv) $ cd lib
(venv) $ pylint --rcfile=../.pylintrc-lib path/to/module.py
Running unit tests
````````````````````````````````````````````````````````````````````````````````
You may run prepared unit tests on the whole codebase by executing the following
command:
.. code-block:: shell
# Always make sure your virtual environment is activated:
$ . venv/bin/activate
# Run tests:
(venv) $ make test
````````````````````````````````````````````````````````````````````````````````
Project documentation is generated using the `Sphinx-doc <http://www.sphinx-doc.org/en/stable/contents.html>`__
tool into various formats. Please use `RST <http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html>`__
markup features where appropriate to increase readability and cross-reference to
related content. It should however still be possible to view the documentation of
all Python modules in *Pythonic* way via `pydoc3 <https://docs.python.org/3/library/pydoc.html>`__
and the result should still be more or less readable. Please test it immediately with:
.. code-block:: shell
# Always make sure your virtual environment is activated:
$ . venv/bin/activate
# Run tests:
(venv) $ pydoc3 ./path/to/module.py
You may generate and review the documentation locally by executing the following
command:
# Always make sure your virtual environment is activated:
$ . venv/bin/activate
# Run tests:
(venv) $ make docs
Documentation will be generated into ``doc/sphinx/_build/html/manual.html``.
Important resources
````````````````````````````````````````````````````````````````````````````````
* `pyflakes <https://github.com/PyCQA/pyflakes>`__
* `pylint <https://pylint.readthedocs.io/en/latest/>`__
* `nosetests <http://nose.readthedocs.io/en/latest/>`__
* `pydoc3 <https://docs.python.org/3/library/pydoc.html>`__
* `Sphinx-doc <http://www.sphinx-doc.org/en/stable/contents.html>`__
* `reStructuredText Primer <http://www.sphinx-doc.org/en/stable/rest.html>`__
* `Sphinx markup constructs <http://www.sphinx-doc.org/en/stable/markup/index.html>`__
* `The Python domain <http://www.sphinx-doc.org/en/stable/domains.html#the-python-domain>`__
* `Documenting functions and methods <http://www.sphinx-doc.org/en/stable/domains.html#info-field-lists>`__
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
Versioning
--------------------------------------------------------------------------------
This project uses the `semantic versioning <https://semver.org/>`__. When the
**production** level packages are being built and deployed, the automated build
system takes the project version directly from following files (paths are relative
to project root):
* ``lib/mentat/__init__.py``
* ``package.json``
Sadly you have to adjust the version string on both of these places, currently
there is no way how to do it on one.
When building the **release** or **development** level packages, the automated
build system appends an internal build number as additional subversion. This way
each build produces unique version string and unique package. This feature can
be used during development to reduce the need for incrementing the version numbers
manually between each builds.
Tagging
--------------------------------------------------------------------------------
Each major and minor version release must be tagged within the repository. Please
use only annotated or signed tags and provide short comment for the release. Before
tagging please view existing tags so that you can attempt to maintain the style of
the tag messages.
.. code-block:: shell
# List all existing tags
git tag -l -n999
# Create new annotated tag and provide message
git tag -a v2.0.0
# Push tags to remote servers (if you are permitted to do so)
git push origin v2.0.0
git push buildbot v2.0.0
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
Database schema migrations
--------------------------------------------------------------------------------
Event database migrations
````````````````````````````````````````````````````````````````````````````````
Due to the performance reasons the event database abstraction layer is implemented
directly on top of the `psycopg2 <http://initd.org/psycopg/>`__ driver. To be
consistent with metadata database migrations we are using separate configured instance
of `Alembic <https://alembic.sqlalchemy.org/en/latest/index.html>`__ database
migration utility. The migration environment is located in ``migrations-events``
subdirectory.
To create new migration during development follow these steps::
cd migrations-events
alembic revision -m "revision description"
Now edit the generated revision file to suit your needs. You may wish to use
following resources as reference:
* `Operation Reference <https://alembic.sqlalchemy.org/en/latest/ops.html>`__
* `Cookbook <https://alembic.sqlalchemy.org/en/latest/cookbook.html>`__
Migration can be then invoked locally from within the migration environment directory::
cd migrations-events
alembic upgrade head
alembic history
To enable execution of database migrations on target systems after installation
from package there is a simple wrapper script ``/etc/mentat/scripts/sqldb-migrate-e.sh``::
/etc/mentat/scripts/sqldb-migrate-e.sh upgrade head
/etc/mentat/scripts/sqldb-migrate-e.sh history
Metadata database migrations
````````````````````````````````````````````````````````````````````````````````
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
Examples
--------------------------------------------------------------------------------
Implementing example daemon module
````````````````````````````````````````````````````````````````````````````````
Before going further please read the documentation and study source code of following
libraries:
* :py:mod:`pyzenkit.baseapp`
* :py:mod:`pyzenkit.zendaemon`
* :py:mod:`mentat.daemon.piper`
Now save following content into the file ``/etc/mentat/examples/mentat-demopiper.py``:
.. code-block:: python
import pyzenkit
import mentat.const
import mentat.daemon.piper
class DemoPrintComponent(pyzenkit.zendaemon.ZenDaemonComponent):
def get_events(self):
return [
{
'event': 'message_process',
'callback': self.cbk_event_message_process,
'prepend': False
}
]
def cbk_event_message_process(self, daemon, args):
daemon.logger.info(
"Processing message: '{}': '{}'".format(
args['id'], str(args['data']).strip()
)
)
daemon.queue.schedule('message_commit', args)
self.inc_statistic('cnt_printed')
return (daemon.FLAG_CONTINUE, None)
class DemoPiperDaemon(mentat.daemon.piper.PiperDaemon):
def __init__(self):
super().__init__(
name = 'mentat-demopiper.py',
description = 'DemoPiperDaemon - Demonstration daemon',
path_bin = '/usr/local/bin',
path_cfg = '/tmp',
path_log = '/var/mentat/log',
path_run = '/var/mentat/run',
path_tmp = '/tmp',
default_config_dir = None,
default_queue_in_dir = '/var/mentat/spool/mentat-demopiper.py',
default_queue_out_dir = None,
schedule = [
('message_enqueue', {'data': '{"testA1": 1, "testA2": 2}'}),
('message_enqueue', {'data': '{"testB1": 1, "testB2": 2}'}),
(mentat.const.DFLT_EVENT_START,)
],
schedule_after = [
(mentat.const.DFLT_INTERVAL_STATISTICS, mentat.const.DFLT_EVENT_LOG_STATISTICS)
],
components = [
DemoPrintComponent()
]
)
if __name__ == "__main__":
DemoPiperDaemon().run()
Now let`s create configuration file ``/tmp/mentat-demopiper.py``. It must contain
a valid JSON dictionary, that may or may not be empty, so it must contain at least
following:
.. code-block:: python
# Configuration for module
{}
Note, that you may use single-line comments. Any line, that beginswith ``#`` is
ignored. However there may be only white characters on the line before the comment.
Now add your module somewhere into the message processing pipeline. For the simplicity
let`s put it after the default ``mentat-storage.py`` module, so that we have to make
only one change in existing configuration files. Replace the existing value for
``queue_out_dir`` with following line in ``/etc/mentat/mentat-storage.py.conf`` file:
.. code-block:: python
"queue_out_dir": "/var/mentat/spool/mentat-demopiper.py",
And finally add your new module to the ``/etc/mentat/mentat-controller.py.conf``
file into the key ``modules``, so that you can start and stop it together with
the rest of the modules:
.. code-block:: python
{
"exec": "mentat-demopiper.py",
"args": [
# Enable debug information before daemonization
"--debug"
# Force logging level ['debug', 'info', 'warning', 'error', 'critical']
"--log-level=debug"
]
},
Place it on top of the list so that it gets started first since it is the last
module in the message processing chain.
Now everything is ready for you to start everything up:
.. code-block:: shell
# Create symlink to example
ln -s /etc/mentat/examples/mentat-demopiper.py /usr/local/bin/mentat-demopiper.py
# Stop all currently running components
mentat-controller.py --command stop
# Start all currently components
mentat-controller.py --command start
# Generate test messages
mentat-ideagen.py --count 10
# View log file
tail -f /var/mentat/log/mentat-demopiper.py.log