updated remarks and docs

master
Alexander Graf 4 years ago
parent 1e2b5f26ab
commit 10435114ec

@ -337,16 +337,19 @@ def config_import(verbose=0, secrets=False, quiet=False, color=False, update=Fal
""" """
# verbose # verbose
# 0 : show number of changes # 0 : only show number of changes
# 1 : also show changes # 1 : also show detailed changes
# 2 : also show secrets # 2 : also show input data
# 3 : also show input data # 3 : also show sql queries (also needs -s, as sql may contain secrets)
# 4 : also show sql queries # 4 : also show tracebacks (also needs -s, as tracebacks may contain secrets)
# 5 : also show tracebacks
if quiet: if quiet:
verbose = -1 verbose = -1
if verbose > 2 and not secrets:
print('[Warning] Verbosity level capped to 2. Specify --secrets to log sql and tracebacks.')
verbose = 2
color_cfg = { color_cfg = {
'color': color or sys.stdout.isatty(), 'color': color or sys.stdout.isatty(),
'lexer': 'python', 'lexer': 'python',
@ -376,7 +379,7 @@ def config_import(verbose=0, secrets=False, quiet=False, color=False, update=Fal
fmt = f' - {{:<{max([len(loc) for loc, msg in res])}}} : {{}}' fmt = f' - {{:<{max([len(loc) for loc, msg in res])}}} : {{}}'
res = [fmt.format(loc, msg) for loc, msg in res] res = [fmt.format(loc, msg) for loc, msg in res]
num = f'error{["s",""][len(res)==1]}' num = f'error{["s",""][len(res)==1]}'
res.insert(0, f'[ValidationError] {len(res)} {num} occured during input validation') res.insert(0, f'[ValidationError] {len(res)} {num} occurred during input validation')
return '\n'.join(res) return '\n'.join(res)
@ -484,7 +487,7 @@ def config_import(verbose=0, secrets=False, quiet=False, color=False, update=Fal
if backref is not None: if backref is not None:
log('Modified', item, '{target!r} {key}: {before!r} -> {after!r}'.format(**backref)) log('Modified', item, '{target!r} {key}: {before!r} -> {after!r}'.format(**backref))
return return
# verbose? # show input data?
if not verbose >= 2: if not verbose >= 2:
return return
# hide secrets in data # hide secrets in data
@ -532,7 +535,7 @@ def config_import(verbose=0, secrets=False, quiet=False, color=False, update=Fal
except ValidationError as exc: except ValidationError as exc:
raise click.ClickException(format_errors(exc.messages)) from exc raise click.ClickException(format_errors(exc.messages)) from exc
except Exception as exc: except Exception as exc:
if verbose >= 5: if verbose >= 3:
raise raise
# (yaml.scanner.ScannerError, UnicodeDecodeError, ...) # (yaml.scanner.ScannerError, UnicodeDecodeError, ...)
raise click.ClickException( raise click.ClickException(
@ -584,7 +587,7 @@ def config_export(full=False, secrets=False, color=False, dns=False, output=None
if only: if only:
for spec in only: for spec in only:
if spec.split('.', 1)[0] not in MailuSchema.Meta.order: if spec.split('.', 1)[0] not in MailuSchema.Meta.order:
raise click.ClickException(f'[ERROR] Unknown section: {spec}') raise click.ClickException(f'[ValidationError] Unknown section: {spec}')
else: else:
only = MailuSchema.Meta.order only = MailuSchema.Meta.order
@ -606,7 +609,7 @@ def config_export(full=False, secrets=False, color=False, dns=False, output=None
print(colorize(schema.dumps(models.MailuConfig()), **color_cfg), file=output) print(colorize(schema.dumps(models.MailuConfig()), **color_cfg), file=output)
except ValueError as exc: except ValueError as exc:
if spec := get_fieldspec(exc): if spec := get_fieldspec(exc):
raise click.ClickException(f'[ERROR] Invalid filter: {spec}') from exc raise click.ClickException(f'[ValidationError] Invalid filter: {spec}') from exc
raise raise

@ -33,7 +33,7 @@ class IdnaDomain(db.TypeDecorator):
""" Stores a Unicode string in it's IDNA representation (ASCII only) """ Stores a Unicode string in it's IDNA representation (ASCII only)
""" """
# TODO: String(80) is too small? # TODO: use db.String(255)?
impl = db.String(80) impl = db.String(80)
def process_bind_param(self, value, dialect): def process_bind_param(self, value, dialect):
@ -50,7 +50,7 @@ class IdnaEmail(db.TypeDecorator):
""" Stores a Unicode string in it's IDNA representation (ASCII only) """ Stores a Unicode string in it's IDNA representation (ASCII only)
""" """
# TODO: String(255) is too small? # TODO: use db.String(254)?
impl = db.String(255) impl = db.String(255)
def process_bind_param(self, value, dialect): def process_bind_param(self, value, dialect):
@ -314,7 +314,7 @@ class Relay(Base):
__tablename__ = 'relay' __tablename__ = 'relay'
name = db.Column(IdnaDomain, primary_key=True, nullable=False) name = db.Column(IdnaDomain, primary_key=True, nullable=False)
# TODO: String(80) is too small? # TODO: use db.String(266)? transport(8):(1)[nexthop(255)](2)
smtp = db.Column(db.String(80), nullable=True) smtp = db.Column(db.String(80), nullable=True)
@ -322,9 +322,7 @@ class Email(object):
""" Abstraction for an email address (localpart and domain). """ Abstraction for an email address (localpart and domain).
""" """
# TODO: validate max. total length of address (<=254) # TODO: use db.String(64)?
# TODO: String(80) is too large (64)?
localpart = db.Column(db.String(80), nullable=False) localpart = db.Column(db.String(80), nullable=False)
@declarative.declared_attr @declarative.declared_attr
@ -634,7 +632,7 @@ class Token(Base):
user = db.relationship(User, user = db.relationship(User,
backref=db.backref('tokens', cascade='all, delete-orphan')) backref=db.backref('tokens', cascade='all, delete-orphan'))
password = db.Column(db.String(255), nullable=False) password = db.Column(db.String(255), nullable=False)
# TODO: String(255) is too large? (43 should be sufficient) # TODO: use db.String(32)?
ip = db.Column(db.String(255)) ip = db.Column(db.String(255))
def check_password(self, password): def check_password(self, password):

@ -36,7 +36,7 @@ from . import models, dkim
ma = Marshmallow() ma = Marshmallow()
# TODO: how and where to mark keys as "required" while unserializing in api? # TODO: how and where to mark keys as "required" while deserializing in api?
# - when modifying, nothing is required (only the primary key, but this key is in the uri) # - when modifying, nothing is required (only the primary key, but this key is in the uri)
# - the primary key from post data must not differ from the key in the uri # - the primary key from post data must not differ from the key in the uri
# - when creating all fields without default or auto-increment are required # - when creating all fields without default or auto-increment are required
@ -705,6 +705,7 @@ class BaseSchema(ma.SQLAlchemyAutoSchema):
# add attributes required for validation from db # add attributes required for validation from db
# TODO: this will cause validation errors if value from database does not validate # TODO: this will cause validation errors if value from database does not validate
# but there should not be an invalid value in the database
for attr_name, field_obj in self.load_fields.items(): for attr_name, field_obj in self.load_fields.items():
if field_obj.required and attr_name not in data: if field_obj.required and attr_name not in data:
data[attr_name] = getattr(instance, attr_name) data[attr_name] = getattr(instance, attr_name)

@ -97,7 +97,7 @@ where mail-config.yml looks like:
without ``--delete-object`` option config-update will only add/update new values but will *not* remove any entries missing in provided YAML input. without ``--delete-object`` option config-update will only add/update new values but will *not* remove any entries missing in provided YAML input.
Users Users
----- ^^^^^
following are additional parameters that could be defined for users: following are additional parameters that could be defined for users:
@ -116,7 +116,7 @@ following are additional parameters that could be defined for users:
* spam_threshold * spam_threshold
Alias Alias
----- ^^^^^
additional fields: additional fields:
@ -125,11 +125,11 @@ additional fields:
config-export config-export
------------- -------------
The purpose of this command is to export domain-, relay-, alias- and user-configuration in YAML or JSON format. The purpose of this command is to export the complete configuration in YAML or JSON format.
.. code-block:: bash .. code-block:: bash
# docker-compose exec admin flask mailu config-export --help $ docker-compose exec admin flask mailu config-export --help
Usage: flask mailu config-export [OPTIONS] [FILTER]... Usage: flask mailu config-export [OPTIONS] [FILTER]...
@ -152,18 +152,18 @@ filters to export only some objects or attributes (try: ``user`` or ``domain.nam
.. code-block:: bash .. code-block:: bash
docker-compose exec admin flask mailu config-export -o mail-config.yml $ docker-compose exec admin flask mailu config-export -o mail-config.yml
docker-compose exec admin flask mailu config-export --dns domain.dns_mx domain.dns_spf $ docker-compose exec admin flask mailu config-export --dns domain.dns_mx domain.dns_spf
config-import config-import
------------- -------------
The purpose of this command is for importing domain-, relay-, alias- and user-configuration in bulk and synchronizing DB entries with an external YAML/JOSN source. This command imports configuration data from an external YAML or JSON source.
.. code-block:: bash .. code-block:: bash
# docker-compose exec admin flask mailu config-import --help $ docker-compose exec admin flask mailu config-import --help
Usage: flask mailu config-import [OPTIONS] [FILENAME|-] Usage: flask mailu config-import [OPTIONS] [FILENAME|-]
@ -211,13 +211,28 @@ mail-config.yml contains the configuration and looks like this:
config-update shows the number of created/modified/deleted objects after import. config-update shows the number of created/modified/deleted objects after import.
To suppress all messages except error messages use ``--quiet``. To suppress all messages except error messages use ``--quiet``.
By adding the ``--verbose`` switch (one or more times) the import gets more detailed and shows exactyl what attributes changed. By adding the ``--verbose`` switch (up to two times) the import gets more detailed and shows exactly what attributes changed.
In all messages plain-text secrets (dkim-keys, passwords) are hidden by default. Use ``--secrets`` to show secrets. In all log messages plain-text secrets (dkim-keys, passwords) are hidden by default. Use ``--secrets`` to log secrets.
If you want to test what would be done when importing use ``--dry-run``. If you want to test what would be done when importing without committing any changes, use ``--dry-run``.
By default config-update replaces the whole configuration. You can use ``--update`` to change the existing configuration instead.
When updating you can add new and change existing objects. By default config-update replaces the whole configuration. ``--update`` allows to modify the existing configuration instead.
To delete an object use ``-key: value`` (To delete the domain example.com ``-name: example.com`` for example). New elements will be added and existing elements will be modified.
To reset an attribute to default use ``-key: null`` (To reset enable_imap ``-enable_imap: null`` for example). It is possible to delete a single element or prune all elements from lists and associative arrays using a special notation:
+-----------------------------+------------------+--------------------------+
| Delete what? | notation | example |
+=============================+==================+==========================+
| specific array object | ``- -key: id`` | ``- -name: example.com`` |
+-----------------------------+------------------+--------------------------+
| specific list item | ``- -id`` | ``- -user1@example.com`` |
+-----------------------------+------------------+--------------------------+
| all remaining array objects | ``- -key: null`` | ``- -email: null`` |
+-----------------------------+------------------+--------------------------+
| all remaining list items | ``- -prune-`` | ``- -prune-`` |
+-----------------------------+------------------+--------------------------+
The ``-key: null`` notation can also be used to reset an attribute to its default.
To reset *spam_threshold* to it's default *80* use ``-spam_threshold: null``.
This is a complete YAML template with all additional parameters that can be defined: This is a complete YAML template with all additional parameters that can be defined:

Loading…
Cancel
Save