fix: service credential provisioning and install reliability
Unit Tests / test (push) Successful in 7m21s

- calendar: create_calendar_user() now writes bcrypt htpasswd entry to
  data/services/calendar/config/users (the path Radicale reads at
  /etc/radicale/users); delete_calendar_user() removes the entry

- email: create_email_user() calls `docker exec cell-mail setup email add`
  to register the account in docker-mailserver's Dovecot/Postfix store;
  delete_email_user() calls the matching `setup email del` — both are
  non-fatal if the container isn't running

- service_composer.install(): pull image separately before up so slow
  registry pulls don't race with container startup; retry up once on
  failure so a transient registry hiccup on first install doesn't
  require the user to manually retry

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 13:41:41 -04:00
parent c696ca9ef6
commit 9bdda6aaf8
3 changed files with 83 additions and 2 deletions
+14 -2
View File
@@ -253,9 +253,21 @@ class ServiceComposer:
def install(self, service_id: str, manifest: Dict,
template_content: str) -> Dict:
"""Write compose file and start containers."""
"""Write compose file, pull image, then start containers.
pull is run first so the up step doesn't time out on slow connections.
A single retry handles transient registry hiccups on first install.
"""
self.write_compose(service_id, manifest, template_content)
return self.up(service_id)
pull = self._store_cmd(service_id, 'pull', timeout=600)
if not pull.get('ok'):
logger.warning('service_composer: image pull for %s failed, proceeding anyway: %s',
service_id, pull.get('stderr', '')[:200])
result = self.up(service_id)
if not result.get('ok'):
logger.info('service_composer: retrying up for %s after initial failure', service_id)
result = self.up(service_id)
return result
def remove(self, service_id: str, purge_data: bool = False) -> Dict:
"""Stop containers, optionally delete compose file, secrets, and service data dir."""