Commit Graph

21 Commits

Author SHA1 Message Date
143cf9c792 feat: add peer sync worker for zone_data exchange between nodes 🔄
Adds optional peer-to-peer zone_data replication between directdnsonly
instances. Enables eventual consistency in DA Multi-Server topologies
without a shared datastore.

- InternalAPI: GET /internal/zones (list) and ?domain= (detail)
  exposes zone_data to peers via existing basic auth
- PeerSyncWorker: interval-based daemon thread that fetches zone_data
  from configured peers, storing newer entries locally; peer downtime
  is silently skipped and retried next interval
- WorkerManager: wires PeerSyncWorker alongside reconciler; exposes
  peer_syncer_alive in queue_status
- Config: peer_sync block with enabled/interval_minutes/peers[]
- Tests: 13 tests covering sync, skip-older, skip-unreachable, empty
  peer list, bad status, and missing zone_data scenarios
2026-02-19 22:16:55 +13:00
33f4f30b5f feat: add initial_delay_minutes to reconciler for LB stagger 🕐
Configurable startup delay before the first reconciliation pass so that
multiple receivers behind a load balancer can be offset without relying
on container start order (which is lost on reboot). Set to half the
interval on the secondary receiver — e.g. interval 60m → delay 30m.
Default is 0 (no change to existing behaviour). Stop event is respected
during the delay so the worker shuts down cleanly even mid-wait.
2026-02-19 15:28:30 +13:00
b939bb5fa0 docs: add DNS server resource and scale guide with NSD/Knot comparison 📊
Cover memory profiles, zone-count thresholds, reload behaviour, and
throughput characteristics for BIND9, CoreDNS MySQL, NSD, and Knot DNS.
Call out NSD as the recommended lighter bundled alternative to BIND9
(~5-10 MB base, near-identical zone file format, same reload semantics)
and note the ~300-zone crossover where CoreDNS MySQL starts to win.
2026-02-19 14:48:10 +13:00
70ae81ee0d docs: rewrite topology comparison with accurate failure-mode analysis 📋
Expand both topology diagrams to show the retry queue and healing pass in
the flow. Add per-topology failure-behaviour tables covering transient backend
failure, prolonged outage, container-down-during-push, and cross-node drift.
Rewrite the comparison table to call out the key architectural difference:
Topology A has no auto-recovery from prolonged BIND failure (needs next DA push);
Topology B's reconciler healing pass re-syncs missing backends from stored
zone_data without any DA involvement.
2026-02-19 14:17:53 +13:00
b523b17f30 feat: retry queue, backend healing, and zone_data persistence 🔁
- worker.py: third persistent retry queue with exponential backoff (30s→30m,
  max 5 attempts); failed backends tracked per-item so retries target only the
  failing nodes; zone_data stored in DB after every successful write
- Domain model: zone_data TEXT + zone_updated_at DATETIME columns; additive
  migration applied on startup so existing deployments upgrade in place
- ReconciliationWorker: Option C healing pass — checks every configured backend
  for zone presence after each reconciliation cycle and re-queues any zone
  missing from a backend using stored zone_data, enabling automatic recovery
  from prolonged backend outages without waiting for DirectAdmin to re-push
- 82 tests, all passing
2026-02-19 14:05:22 +13:00
0e044b7dc2 chore: remove unimplemented PowerDNS MySQL backend 🗑️
Dead code from v1 planning — never implemented, superseded by the
CoreDNS MySQL backend. Also carried a broken stale import that would
have caused an ImportError on load.
2026-02-19 12:24:30 +13:00
e0a119558d refactor: extract DirectAdminClient into directdnsonly.app.da module 🏗️
Move all outbound DirectAdmin HTTP logic out of ReconciliationWorker and
into a dedicated, independently testable DirectAdminClient class:

- directdnsonly/app/da/client.py: list_domains (paginated JSON + legacy
  fallback), get (authenticated GET to any CMD_* endpoint), _login
  (DA Evo session-cookie fallback), _parse_legacy_domain_list
- directdnsonly/app/da/__init__.py: public re-export of DirectAdminClient
- reconciler.py: now purely reconciliation logic; instantiates a client
  per configured server — no HTTP code remaining
- tests/test_da_client.py: 16 dedicated tests for DirectAdminClient
- tests/test_reconciler.py: mocks at the DirectAdminClient class boundary
  instead of the internal _fetch_da_domains method

Bumped to 2.2.0 — DirectAdminClient is now a first-class public API.
2026-02-19 12:16:22 +13:00
ae1e89a236 feat: conditional BIND startup; config search path priority fix 🔧
- entrypoint: only start named when a bind backend is configured and
  enabled in app.yml; CoreDNS-only deployments skip named entirely
- config: user-supplied paths (/etc/directdnsonly, ./config) now
  searched before the bundled app.yml so mounted configs take effect
- docs: deployment topology reference — Topology A (dual BIND HA) and
  Topology B (single instance, multi-DC CoreDNS MySQL)
- chore: bump version to 2.1.0
- justfile: add build-docker recipe
2026-02-19 12:07:37 +13:00
aac7b365a5 fix: remove stale COPY config from Dockerfile 🐛
Root config/ directory was removed when the duplicate config/app.yml was
deleted — the canonical config is now bundled inside directdnsonly/config/
and is already covered by the existing COPY directdnsonly step.
2026-02-18 23:16:52 +13:00
0903d78458 fix: update .gitignore to include dist/ and modify build command in justfile 🐛 2026-02-18 23:04:41 +13:00
74c5f4012e style: apply black formatting across codebase 🎨
No logic changes — pure reformatting of line lengths, dict literals,
method-chain line breaks, and trailing newlines to satisfy black's style.
2026-02-18 22:53:09 +13:00
807d6271f1 chore: rewrite justfile for pyenv + poetry dev workflow 🔧
Replaces outdated PyInstaller-only recipe with full task runner:
install, test, coverage, coverage-html, test-one, fmt, fmt-check, ci, run,
build, clean. PATH export wires in pyenv shims and poetry automatically.
2026-02-18 22:46:18 +13:00
bd46227364 feat: add test suite, fix backend bugs, remove legacy artifacts 🧪
- Add 73-test suite across conftest, utils, admin API, reconciler, zone parser,
  and CoreDNS MySQL backend (all green, ~0.5s)
- Fix zone_exists filter using wrong column name (name → zone_name)
- Fix delete_zone missing dot_fqdn normalization on lookup
- Remove spurious unused `from config import config` in coredns_mysql.py
- Fix config loader to search module-relative path so tests find app.yml
  without needing a root-level config/ directory
- Remove legacy v1 Flask prototype (app.py), empty config.json, and
  duplicate root config/app.yml
2026-02-18 22:03:04 +13:00
b8f12d0208 feat: Update dependencies in poetry.lock and pyproject.toml
- Added `certifi` version 2026.1.4 and `charset-normalizer` version 3.4.4 to poetry.lock.
- Introduced `idna` version 3.11 to poetry.lock.
- Updated `requests` to version 2.32.5 in poetry.lock and added it as a dependency in pyproject.toml.
- Updated `urllib3` to version 2.6.3 in poetry.lock.
- Added extras for `requests` and `urllib3` in poetry.lock.
2026-02-18 07:18:44 +13:00
5c8bc2653c feat: enhance README with detailed concurrent multi-backend processing architecture and usage instructions 2026-02-17 16:19:51 +13:00
02536cd448 feat: update Dockerfile for improved BIND configuration and application setup 2026-02-17 16:16:01 +13:00
24877be037 chore: add .gitkeep to logs directory for empty directory preservation 2026-02-17 16:14:24 +13:00
6445cf49c0 feat: migrate to Poetry and implement multi-backend DNS management
- Migrated from setuptools to Poetry; added pyproject.toml, poetry.lock,
  poetry.toml and .python-version (Python 3.11.12)
- Built out full directdnsonly Python package with BIND and CoreDNS MySQL
  backends, CherryPy REST API, persist-queue worker, and vyper-based config
- Auth credentials now read from config/env (app.auth_username/password)
  rather than hardcoded; override via DADNS_APP_AUTH_PASSWORD env var
- Added Dockerfile.deepseek: Python 3.11 slim + BIND9 + Poetry install
- Rewrote docker-compose.yml for local dev stack (MySQL + dadns services)
- Added SQL schema, docker/ BIND configs, justfile, tests, and README
- Expanded .gitignore for Poetry/Python project artifacts
2026-02-17 16:12:46 +13:00
1d1c12b661 chore: Clean out previous version of directdnsonly 🔥 2025-05-28 09:50:35 +12:00
49c957da4a version 1.0.9 refresh v1.0.9 2021-09-07 22:39:25 +12:00
af437cfae5 Initial project commit 2021-08-28 11:28:23 +12:00