exordos-mail 0.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. exordos_mail-0.0.2/.github/workflows/build.yml +125 -0
  2. exordos_mail-0.0.2/.github/workflows/publish-to-pypi.yml +97 -0
  3. exordos_mail-0.0.2/.github/workflows/tests.yaml +29 -0
  4. exordos_mail-0.0.2/.gitignore +12 -0
  5. exordos_mail-0.0.2/Makefile +48 -0
  6. exordos_mail-0.0.2/PKG-INFO +254 -0
  7. exordos_mail-0.0.2/README.md +223 -0
  8. exordos_mail-0.0.2/etc/exordos_metapaas/logging.yaml +29 -0
  9. exordos_mail-0.0.2/etc/exordos_metapaas/metapaas_mail_agent.conf +10 -0
  10. exordos_mail-0.0.2/etc/systemd/exordos-metapaas-mail-agent.service +14 -0
  11. exordos_mail-0.0.2/etc/systemd/exordos-metapaas-mail-configure.service +14 -0
  12. exordos_mail-0.0.2/exordos/exordos.yaml +42 -0
  13. exordos_mail-0.0.2/exordos/images/dp_bootstrap.sh +36 -0
  14. exordos_mail-0.0.2/exordos/images/dp_install.sh +177 -0
  15. exordos_mail-0.0.2/exordos/manifests/example_mail.yaml.j2 +36 -0
  16. exordos_mail-0.0.2/exordos/manifests/mailaas.yaml.j2 +102 -0
  17. exordos_mail-0.0.2/exordos_mail/__init__.py +13 -0
  18. exordos_mail-0.0.2/exordos_mail/constants.py +29 -0
  19. exordos_mail-0.0.2/exordos_mail/controlplane/__init__.py +0 -0
  20. exordos_mail-0.0.2/exordos_mail/controlplane/api/__init__.py +0 -0
  21. exordos_mail-0.0.2/exordos_mail/controlplane/api/controllers.py +97 -0
  22. exordos_mail-0.0.2/exordos_mail/controlplane/api/routes.py +44 -0
  23. exordos_mail-0.0.2/exordos_mail/controlplane/dm/__init__.py +0 -0
  24. exordos_mail-0.0.2/exordos_mail/controlplane/dm/models.py +151 -0
  25. exordos_mail-0.0.2/exordos_mail/controlplane/infra/__init__.py +0 -0
  26. exordos_mail-0.0.2/exordos_mail/controlplane/infra/dm/__init__.py +0 -0
  27. exordos_mail-0.0.2/exordos_mail/controlplane/infra/dm/models.py +106 -0
  28. exordos_mail-0.0.2/exordos_mail/controlplane/infra/services/__init__.py +0 -0
  29. exordos_mail-0.0.2/exordos_mail/controlplane/infra/services/builder.py +168 -0
  30. exordos_mail-0.0.2/exordos_mail/controlplane/paas/__init__.py +0 -0
  31. exordos_mail-0.0.2/exordos_mail/controlplane/paas/dm/__init__.py +0 -0
  32. exordos_mail-0.0.2/exordos_mail/controlplane/paas/dm/models.py +88 -0
  33. exordos_mail-0.0.2/exordos_mail/controlplane/paas/services/__init__.py +0 -0
  34. exordos_mail-0.0.2/exordos_mail/controlplane/paas/services/builder.py +131 -0
  35. exordos_mail-0.0.2/exordos_mail/dataplane/__init__.py +0 -0
  36. exordos_mail-0.0.2/exordos_mail/dataplane/driver.py +190 -0
  37. exordos_mail-0.0.2/exordos_mail/definition.py +71 -0
  38. exordos_mail-0.0.2/exordos_mail/migrations/0000-init-mail.py +102 -0
  39. exordos_mail-0.0.2/exordos_mail/migrations/__init__.py +0 -0
  40. exordos_mail-0.0.2/exordos_mail/tests/__init__.py +1 -0
  41. exordos_mail-0.0.2/exordos_mail/tests/functional/__init__.py +1 -0
  42. exordos_mail-0.0.2/exordos_mail/tests/functional/conftest.py +239 -0
  43. exordos_mail-0.0.2/exordos_mail/tests/functional/prepare_env.py +421 -0
  44. exordos_mail-0.0.2/exordos_mail/tests/functional/test_mail_provision.py +137 -0
  45. exordos_mail-0.0.2/exordos_mail/tests/functional/test_smtp_auth.py +436 -0
  46. exordos_mail-0.0.2/exordos_mail/tests/unit/__init__.py +0 -0
  47. exordos_mail-0.0.2/exordos_mail/tests/unit/test_driver.py +102 -0
  48. exordos_mail-0.0.2/exordos_mail/tests/unit/test_models.py +37 -0
  49. exordos_mail-0.0.2/exordos_mail/utils.py +29 -0
  50. exordos_mail-0.0.2/exordos_mail.egg-info/PKG-INFO +254 -0
  51. exordos_mail-0.0.2/exordos_mail.egg-info/SOURCES.txt +57 -0
  52. exordos_mail-0.0.2/exordos_mail.egg-info/dependency_links.txt +1 -0
  53. exordos_mail-0.0.2/exordos_mail.egg-info/entry_points.txt +5 -0
  54. exordos_mail-0.0.2/exordos_mail.egg-info/requires.txt +25 -0
  55. exordos_mail-0.0.2/exordos_mail.egg-info/top_level.txt +1 -0
  56. exordos_mail-0.0.2/pyproject.toml +103 -0
  57. exordos_mail-0.0.2/setup.cfg +4 -0
  58. exordos_mail-0.0.2/tox.ini +69 -0
  59. exordos_mail-0.0.2/uv.lock +2283 -0
@@ -0,0 +1,125 @@
1
+ name: build
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+ types: [opened]
7
+
8
+ env:
9
+ PACKER_VERSION: "1.9.2"
10
+ PYTHON_VERSION: "3.12"
11
+ CORE_ENDPOINT_URL: http://10.20.0.2:80/api/core
12
+ CORE_USERNAME: "admin"
13
+ CORE_PASSWORD: "admin"
14
+ ELEMENT_NAME: mailaas
15
+
16
+ jobs:
17
+ Build:
18
+ runs-on: ubuntu-24.04
19
+ if: ${{ github.actor != 'dependabot[bot]' }}
20
+ steps:
21
+ - name: Setup packer
22
+ uses: hashicorp/setup-packer@main
23
+ with:
24
+ version: ${{ env.PACKER_VERSION }}
25
+
26
+ - name: Install exordos CLI
27
+ run: curl -fsSL https://repo.exordos.com/install.sh | sudo sh
28
+
29
+ - name: Install hypervisor
30
+ run: |
31
+ sudo exordos compute hypervisors init
32
+ sudo chmod 666 /dev/kvm
33
+
34
+ - uses: actions/checkout@v6
35
+ with:
36
+ fetch-depth: 0
37
+
38
+ - name: Set up Python ${{ env.PYTHON_VERSION }}
39
+ uses: actions/setup-python@v6
40
+ with:
41
+ python-version: ${{ env.PYTHON_VERSION }}
42
+
43
+ - name: Install uv + tox + build
44
+ uses: astral-sh/setup-uv@v7
45
+ - run: |
46
+ uv tool install tox --with tox-uv
47
+ pip install build
48
+
49
+ - name: Build and push exordos_mail
50
+ timeout-minutes: 20
51
+ env:
52
+ PUSH_EXORDOS_CFG: ${{ secrets.PUSH_EXORDOS_CFG }}
53
+ run: |
54
+ set -eux
55
+
56
+ VERSION="$(exordos get-version .)"
57
+
58
+ exordos build $(pwd)
59
+
60
+ echo "$PUSH_EXORDOS_CFG" | base64 -d > exordos/exordos.push.yaml
61
+ exordos push -c exordos/exordos.push.yaml -t exordos_repo -f --latest
62
+
63
+ echo "MAILAAS_VERSION=${VERSION}" >> $GITHUB_ENV
64
+
65
+ - name: Configure exordos settings
66
+ run: |
67
+ exordos settings set-realm default --endpoint $CORE_ENDPOINT_URL
68
+ exordos settings set-context default --name default --user $CORE_USERNAME --password $CORE_PASSWORD
69
+
70
+ - name: Bootstrap exordos_core
71
+ run: exordos bootstrap -i latest -f -m core --admin-password $CORE_PASSWORD --cidr 10.20.0.0/22
72
+
73
+ - name: Wait for Exordos Core API
74
+ run: |
75
+ for i in {1..60}; do
76
+ if exordos ready_api; then
77
+ echo "Exordos Core API is ready"
78
+ break
79
+ fi
80
+ echo "Waiting for Exordos Core API... ($i/60)"
81
+ sleep 5
82
+ done
83
+
84
+ - name: Set hypervisor with overbooking
85
+ run: |
86
+ HYPER_UUID="$(exordos c h l -o json | jq -r .[0]."UUID")"
87
+ exordos compute hypervisors update $HYPER_UUID --cores-ratio 10.0 --ram-ratio 10.0
88
+
89
+ - name: Build + serve + install (prepare_env.py)
90
+ run: |
91
+ tox -e develop
92
+
93
+ .tox/develop/bin/python \
94
+ exordos_mail/tests/functional/prepare_env.py \
95
+ --project-dir . \
96
+ --output-dir /tmp/metapaas-mail-build \
97
+ --endpoint $CORE_ENDPOINT_URL \
98
+ --username $CORE_USERNAME \
99
+ --password "${CORE_PASSWORD}" \
100
+ --wait-timeout 600 \
101
+ 2>&1 | tee /tmp/prepare_env.log
102
+
103
+ grep "^ export " /tmp/prepare_env.log | sed 's/^ export //' >> $GITHUB_ENV
104
+
105
+ - name: Wait for Mail API
106
+ run: |
107
+ CP_URL=$(grep "EXORDOS_MAIL_CP_URL" $GITHUB_ENV | cut -d= -f2)
108
+ for i in {1..30}; do
109
+ CODE=$(curl -s -o /dev/null -w "%{http_code}" "${CP_URL}/v1/")
110
+ if echo "$CODE" | grep -qE "200|404"; then
111
+ echo "MetaPaaS user-api ready (HTTP $CODE)"; exit 0
112
+ fi
113
+ echo "Waiting ($i/30) for user-api…"; sleep 10
114
+ done
115
+ exit 1
116
+
117
+ - name: Run functional tests
118
+ run: tox -e ${{ env.PYTHON_VERSION }}-functional
119
+
120
+ - name: Debug session on failure
121
+ uses: owenthereal/action-upterm@v1
122
+ if: failure()
123
+ with:
124
+ limit-access-to-actor: true
125
+ wait-timeout-minutes: 30
@@ -0,0 +1,97 @@
1
+ name: Publish Python 🐍 distribution 📦 to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - '*'
7
+
8
+ jobs:
9
+ build:
10
+ name: Build distribution 📦
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - uses: actions/checkout@v6
15
+ with:
16
+ persist-credentials: false
17
+ - name: Set up Python
18
+ uses: actions/setup-python@v6
19
+ with:
20
+ python-version: "3.12"
21
+ - name: Install pypa/build
22
+ run: >-
23
+ python3 -m
24
+ pip install
25
+ build
26
+ --user
27
+ - name: Build a binary wheel and a source tarball
28
+ run: python3 -m build
29
+ - name: Store the distribution packages
30
+ uses: actions/upload-artifact@v7
31
+ with:
32
+ name: python-package-distributions
33
+ path: dist/
34
+
35
+ publish-to-pypi:
36
+ name: >-
37
+ Publish Python 🐍 distribution 📦 to PyPI
38
+ needs:
39
+ - build
40
+ runs-on: ubuntu-latest
41
+ environment:
42
+ name: pypi
43
+ url: https://pypi.org/p/exordos_mail
44
+ permissions:
45
+ id-token: write # IMPORTANT: mandatory for trusted publishing
46
+
47
+ steps:
48
+ - name: Download all the dists
49
+ uses: actions/download-artifact@v8
50
+ with:
51
+ name: python-package-distributions
52
+ path: dist/
53
+ - name: Publish distribution 📦 to PyPI
54
+ uses: pypa/gh-action-pypi-publish@release/v1
55
+
56
+ github-release:
57
+ name: >-
58
+ Sign the Python 🐍 distribution 📦 with Sigstore
59
+ and upload them to GitHub Release
60
+ needs:
61
+ - publish-to-pypi
62
+ runs-on: ubuntu-latest
63
+
64
+ permissions:
65
+ contents: write # IMPORTANT: mandatory for making GitHub Releases
66
+ id-token: write # IMPORTANT: mandatory for sigstore
67
+
68
+ steps:
69
+ - name: Download all the dists
70
+ uses: actions/download-artifact@v8
71
+ with:
72
+ name: python-package-distributions
73
+ path: dist/
74
+ - name: Sign the dists with Sigstore
75
+ uses: sigstore/gh-action-sigstore-python@v3.3.0
76
+ with:
77
+ inputs: >-
78
+ ./dist/*.tar.gz
79
+ ./dist/*.whl
80
+ - name: Create GitHub Release
81
+ env:
82
+ GITHUB_TOKEN: ${{ github.token }}
83
+ run: >-
84
+ gh release create
85
+ "$GITHUB_REF_NAME"
86
+ --repo "$GITHUB_REPOSITORY"
87
+ --notes ""
88
+ - name: Upload artifact signatures to GitHub Release
89
+ env:
90
+ GITHUB_TOKEN: ${{ github.token }}
91
+ # Upload to GitHub Release using the `gh` CLI.
92
+ # `dist/` contains the built packages, and the
93
+ # sigstore-produced signatures and certificates.
94
+ run: >-
95
+ gh release upload
96
+ "$GITHUB_REF_NAME" dist/**
97
+ --repo "$GITHUB_REPOSITORY"
@@ -0,0 +1,29 @@
1
+ name: tests
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+ types: [opened]
7
+
8
+ jobs:
9
+ Lint:
10
+ runs-on: ubuntu-24.04
11
+ strategy:
12
+ fail-fast: true
13
+ matrix:
14
+ python-version: [ "3.12" ]
15
+ steps:
16
+ - uses: actions/checkout@v6
17
+ - name: Set up Python ${{ matrix.python-version }}
18
+ uses: actions/setup-python@v6
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+ - name: Install requirements
22
+ run: sudo apt update && sudo apt install --yes libev-dev libvirt-dev
23
+ - name: Install uv
24
+ uses: astral-sh/setup-uv@v7
25
+ - name: Install tox-uv
26
+ run: uv tool install tox --with tox-uv
27
+ - name: ruff
28
+ run: |
29
+ tox -e ruff-check
@@ -0,0 +1,12 @@
1
+ .venv/
2
+ .tox/
3
+ __pycache__/
4
+ *.pyc
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ cover/
9
+ output/
10
+ *.raw
11
+ *.zst
12
+ .pytest_cache/
@@ -0,0 +1,48 @@
1
+ SHELL := bash
2
+ SSH_KEY ?= ~/.ssh/id_ed25519.pub
3
+ REPOSITORY ?= http://10.20.0.1:8080/exordos-elements
4
+ INDEX_URL ?= http://10.20.0.1:8080/simple/
5
+ PKG_VERSION ?=
6
+
7
+ all: help
8
+
9
+ help:
10
+ @echo "build - build the mailaas element manifest + DP image"
11
+ @echo "install - install mailaas element into Core"
12
+ @echo "wheel - build Python wheel for exordos_mail"
13
+ @echo "publish-wheel - copy wheel to local pip index"
14
+ @echo "lint - run ruff check"
15
+ @echo "format - run ruff format"
16
+ @echo "test - run unit tests via tox"
17
+ @echo "functional - run functional tests (needs live stand)"
18
+ @echo "typecheck - run mypy"
19
+
20
+ build:
21
+ exordos build -c exordos/exordos.yaml -i $(SSH_KEY) -f \
22
+ --manifest-var repository=$(REPOSITORY) \
23
+ --manifest-var index_url=$(INDEX_URL) \
24
+ $(if $(PKG_VERSION),--manifest-var pkg_version=$(PKG_VERSION),)
25
+
26
+ install:
27
+ exordos em elements install output/manifests/mailaas.yaml
28
+
29
+ wheel:
30
+ python -m build --wheel
31
+
32
+ publish-wheel: wheel
33
+ cp dist/exordos_mail-*.whl /srv/exordos-local-repo/simple/
34
+
35
+ lint:
36
+ tox -e ruff-check
37
+
38
+ format:
39
+ tox -e ruff
40
+
41
+ test:
42
+ tox -e py312
43
+
44
+ functional:
45
+ tox -e py312-functional
46
+
47
+ typecheck:
48
+ tox -e mypy
@@ -0,0 +1,254 @@
1
+ Metadata-Version: 2.4
2
+ Name: exordos_mail
3
+ Version: 0.0.2
4
+ Summary: SMTP relay PaaS plugin for the Exordos MetaPaaS runtime (exim4)
5
+ Author-email: Genesis Corporation <mail@gmelikov.ru>
6
+ License: Apache-2.0
7
+ Project-URL: homepage, https://github.com/infraguys/metapaas_mail/
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: oslo.config<10.0.0,>=3.22.2
11
+ Requires-Dist: restalchemy<16.0.0,>=15.0.0
12
+ Requires-Dist: gcl_iam<2.0.0,>=1.0.3
13
+ Requires-Dist: gcl_looper<2.0.0,>=1.2.3
14
+ Requires-Dist: gcl_sdk<4.0.0,>=3.0.5
15
+ Requires-Dist: exordos_metapaas>=0.0.1
16
+ Requires-Dist: passlib>=1.7.0
17
+ Provides-Extra: dev
18
+ Requires-Dist: tox>=4.0.0; extra == "dev"
19
+ Requires-Dist: tox-uv; extra == "dev"
20
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
21
+ Provides-Extra: test
22
+ Requires-Dist: coverage>=4.0; extra == "test"
23
+ Requires-Dist: mock<4.0.0,>=3.0.5; extra == "test"
24
+ Requires-Dist: pytest<9.0.0,>=8.0.0; extra == "test"
25
+ Requires-Dist: pytest-timer<2.0.0,>=1.0.0; extra == "test"
26
+ Requires-Dist: exordos<3.0.0,>=2.0.0; extra == "test"
27
+ Provides-Extra: ruff
28
+ Requires-Dist: ruff; extra == "ruff"
29
+ Provides-Extra: mypy
30
+ Requires-Dist: mypy; extra == "mypy"
31
+
32
+ # mailaas: SMTP Relay PaaS Plugin for MetaPaaS
33
+
34
+ exim4 SMTP submission server packaged as a MetaPaaS plugin,
35
+ following the same pattern as `metapaas_s3`.
36
+
37
+ ## Architecture
38
+
39
+ ```
40
+ metapaas-cp
41
+ └── mail plugin (exordos_paas_mail)
42
+ ├── CP: MailInstance + MailAccount models, REST API, migrations
43
+ ├── infra_builder: CoreInfraBuilder → NodeSet + Config on core
44
+ └── paas_builder: MailInstanceBuilder → MailInstanceNode → DP agent
45
+
46
+ mailaas-dp-<uuid> (VM)
47
+ └── exim4 (SMTP 25/465/587 — STARTTLS + auth)
48
+ ├── /etc/exordos_metapaas/mail.env ← delivered by CP (MAIL_DOMAIN)
49
+ ├── /etc/exim4/passwd ← managed by DP agent (lsearch auth)
50
+ └── exordos-universal-agent ← MailCapabilityDriver
51
+ ```
52
+
53
+ ## Quick Start
54
+
55
+ ### Build
56
+
57
+ ```bash
58
+ make build \
59
+ REPOSITORY=http://10.20.0.1:8080/exordos-elements \
60
+ INDEX_URL=http://10.20.0.1:8080/simple/
61
+ ```
62
+
63
+ Produces:
64
+ - `output/images/exordos-metapaas-mail-dp.raw.zst` (DP image)
65
+ - `output/manifests/mailaas.yaml` (element manifest)
66
+
67
+ ### Install on running metapaas
68
+
69
+ ```bash
70
+ make install
71
+ ```
72
+
73
+ PluginReconciler on metapaas-cp installs `exordos_paas_mail` via pip and
74
+ activates the `/v1/types/mail/` route.
75
+
76
+ ### Create Instance
77
+
78
+ ```bash
79
+ curl -X POST http://metapaas-cp:8080/v1/types/mail/instances/ \
80
+ -H 'Content-Type: application/json' \
81
+ -H 'Authorization: Bearer <token>' \
82
+ -d '{
83
+ "name": "mail-prod",
84
+ "project_id": "4d657461-0000-0000-0000-000000000002",
85
+ "domain": "example.com",
86
+ "cpu": 2,
87
+ "ram": 2048,
88
+ "disk_size": 20,
89
+ "version": "/v1/types/mail/versions/<version-uuid>"
90
+ }'
91
+ ```
92
+
93
+ ### Create SMTP Account
94
+
95
+ ```bash
96
+ # Generate SHA512-crypt hash (compatible with exim4 crypteq)
97
+ HASH=$(openssl passwd -6 "mypassword")
98
+
99
+ curl -X POST http://metapaas-cp:8080/v1/types/mail/instances/<uuid>/accounts/ \
100
+ -H 'Content-Type: application/json' \
101
+ -H 'Authorization: Bearer <token>' \
102
+ -d "{
103
+ \"username\": \"alice\",
104
+ \"password_hash\": \"${HASH}\",
105
+ \"project_id\": \"4d657461-0000-0000-0000-000000000002\",
106
+ \"instance\": \"/v1/types/mail/instances/<uuid>\"
107
+ }"
108
+ ```
109
+
110
+ Within seconds the DP agent writes the account to `/etc/exim4/passwd` and
111
+ reloads exim4. The user can then authenticate via SMTP AUTH (PLAIN/LOGIN over
112
+ STARTTLS on port 587 or SMTPS on port 465).
113
+
114
+ Hashes stored with a Dovecot `{SHA512-CRYPT}` scheme prefix are accepted — the
115
+ driver strips the prefix before writing to exim4's passwd file.
116
+
117
+ ### Configure DNS
118
+
119
+ For mail to be accepted by other servers you must publish a few DNS records for
120
+ your domain (referred to below as `$1`, e.g. `example.com`).
121
+
122
+ - **DKIM** — the key is generated on the node by the configure script. Once the
123
+ instance is `ACTIVE`, read it from the API:
124
+
125
+ ```bash
126
+ curl -s http://metapaas-cp:8080/v1/types/mail/instances/<uuid> \
127
+ -H 'Authorization: Bearer <token>' | jq -r '{dkim_selector, dkim_public_key}'
128
+ ```
129
+
130
+ Publish it as a `TXT` record at `<dkim_selector>._domainkey.$1`
131
+ (default selector: `platform`), with the `dkim_public_key` value as data.
132
+ The raw record is also on the node at `/etc/exim4/dkim/platform.txt`.
133
+ - **SPF** — name: `@` (the domain itself), type: `TXT`, TTL: 3600,
134
+ data: `"v=spf1 ip4:YOUR_SERVER_IP/32 a mx ~all"`
135
+ - **DMARC** — name: `_dmarc`, type: `TXT`, TTL: 3600,
136
+ data: `"v=DMARC1; p=none; pct=100; adkim=s; aspf=s"`
137
+ - **PTR** — set the reverse record for your server IP to `$1`
138
+ - **MX** — ensure an `MX` record exists (e.g. name: `@`, type: `MX`, data: `$1`)
139
+
140
+ ## API Endpoints
141
+
142
+ | Method | Path | Description |
143
+ |--------|------|-------------|
144
+ | POST | `/v1/types/mail/instances/` | Create mail server |
145
+ | GET | `/v1/types/mail/instances/` | List instances |
146
+ | GET | `/v1/types/mail/instances/<uuid>` | Get instance |
147
+ | PATCH | `/v1/types/mail/instances/<uuid>` | Update (cpu/ram/disk_size) |
148
+ | DELETE | `/v1/types/mail/instances/<uuid>` | Delete |
149
+ | GET | `/v1/types/mail/versions/` | List DP image versions |
150
+ | POST | `/v1/types/mail/instances/<uuid>/accounts/` | Create account |
151
+ | GET | `/v1/types/mail/instances/<uuid>/accounts/` | List accounts |
152
+ | PATCH | `/v1/types/mail/instances/<uuid>/accounts/<uuid>` | Update (active, password_hash) |
153
+ | DELETE | `/v1/types/mail/instances/<uuid>/accounts/<uuid>` | Delete account |
154
+
155
+ ### Field permissions
156
+
157
+ | Field | Create | Read | Update |
158
+ |-------|--------|------|--------|
159
+ | `domain` | RW | RO | RO |
160
+ | `status` | — | RO | RO |
161
+ | `ipsv4` | — | RO | RO |
162
+ | `dkim_public_key` | — | RO | RO |
163
+ | `dkim_selector` | — | RO | RO |
164
+ | `password_hash` | RW | hidden | RW |
165
+ | `username` | RW | RO | RO |
166
+ | `active` | RW | RW | RW |
167
+
168
+ ## Repository Layout
169
+
170
+ ```
171
+ .
172
+ ├── exordos_paas_mail/
173
+ │ ├── constants.py # MAIL_ENV_FILE, EXIM4_PASSWD_FILE paths
174
+ │ ├── models.py # MailVersion, MailInstance, MailAccount (restalchemy)
175
+ │ ├── controllers.py # REST controllers (gcl_iam policy-based)
176
+ │ ├── routes.py # Route tree (instances/accounts, versions)
177
+ │ ├── definition.py # MailDefinition (PaaSDefinition contract)
178
+ │ ├── infra_models.py # MailInstance + infra (NodeSet + Config)
179
+ │ ├── infra_builder.py # CoreInfraBuilder (creates VMs + delivers mail.env)
180
+ │ ├── paas_models.py # MailInstanceNode (target resource for DP agent)
181
+ │ ├── paas_builder.py # MailInstanceBuilder (maps accounts → DP payload)
182
+ │ ├── driver.py # MailCapabilityDriver: writes /etc/exim4/passwd
183
+ │ ├── utils.py # remove_nested_dm helper
184
+ │ ├── migrations/
185
+ │ │ ├── 0000-init-mail.py # Creates mail_versions, mail_instances, mail_accounts
186
+ │ │ └── 0001-drop-root-password.py
187
+ │ └── tests/
188
+ │ ├── unit/ # Unit tests (models, driver)
189
+ │ └── functional/ # E2E tests (prepare_env.py + SMTP auth tests)
190
+ ├── exordos/
191
+ │ ├── exordos.yaml # Build config (deps + elements + DP image)
192
+ │ ├── images/
193
+ │ │ ├── dp_install.sh # Packer: install exim4 + configure script + agent
194
+ │ │ └── dp_bootstrap.sh # First-boot: persistent disk + start configure service
195
+ │ └── manifests/
196
+ │ ├── mailaas.yaml.j2 # Element manifest: type reg + IAM + DP version
197
+ │ └── example_mail.yaml.j2 # Example consumer element
198
+ ├── etc/
199
+ │ ├── systemd/
200
+ │ │ ├── exordos-metapaas-mail-configure.service # Configures exim4 from mail.env
201
+ │ │ └── exordos-metapaas-mail-agent.service # Universal agent (MailCapabilityDriver)
202
+ │ └── exordos_metapaas/
203
+ │ ├── logging.yaml
204
+ │ └── metapaas_mail_agent.conf
205
+ ├── .github/workflows/
206
+ │ ├── tests.yaml # Lint (ruff) on every push
207
+ │ └── func_tests.yaml # Full e2e: bootstrap core + deploy + SMTP tests
208
+ ├── pyproject.toml
209
+ ├── tox.ini
210
+ └── Makefile
211
+ ```
212
+
213
+ ## Development
214
+
215
+ ```bash
216
+ make test # Unit tests via tox
217
+ make lint # ruff check
218
+ make format # ruff format
219
+ make typecheck # mypy
220
+ make functional # E2E tests (needs live stand)
221
+ ```
222
+
223
+ ### Running functional tests manually
224
+
225
+ ```bash
226
+ python exordos_paas_mail/tests/functional/prepare_env.py \
227
+ --metapaas-dir ../exordos_metapaas \
228
+ --project-dir . \
229
+ --output-dir /tmp/mail-build \
230
+ --endpoint http://10.20.0.2:11010 \
231
+ --username admin --password <pass>
232
+
233
+ # Use env vars printed by prepare_env.py, then:
234
+ tox -e py312-functional
235
+ ```
236
+
237
+ ## Key differences from metapaas_s3
238
+
239
+ | Aspect | s3aas | mailaas |
240
+ |--------|-------|---------|
241
+ | DP software | RustFS | exim4 (SMTP relay) |
242
+ | Instance children | Bucket, Policy, User, AccessKey | Account |
243
+ | Replicas | 1–16 | Always 1 |
244
+ | Config delivered | `rustfs.env` | `mail.env` (domain only) |
245
+ | DP auth state | S3 access keys | `/etc/exim4/passwd` (SHA512-crypt) |
246
+ | On-change | `systemctl restart rustfs` | `systemctl restart mail-configure` |
247
+ | DP agent state file | `s3_meta.json` | `mail_meta.json` |
248
+ | uuid5 name | `s3aas` | `mailaas` |
249
+
250
+ ## References
251
+
252
+ - MetaPaaS design: `../exordos_metapaas/DESIGN.md`
253
+ - How to build new PaaS: `../exordos_s3/HOW_TO_BUILD_NEW_PAAS.md`
254
+ - Working reference: `../exordos_s3/`