Source code for repository_service_tuf_api

# SPDX-FileCopyrightText: 2023 Repository Service for TUF Contributors
# SPDX-FileCopyrightText: 2022-2023 VMware Inc
#
# SPDX-License-Identifier: MIT

import logging
from dataclasses import dataclass
from typing import Optional
from uuid import uuid4

from celery import Celery
from dynaconf import Dynaconf
from dynaconf.loaders import redis_loader

logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s %(levelname)s %(message)s",
    datefmt="%H:%M:%S",
)
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(message)s",
    datefmt="%H:%M:%S",
)


[docs] @dataclass class BootstrapState: bootstrap: bool state: Optional[str] = None task_id: Optional[str] = None
settings = Dynaconf(envvar_prefix="RSTUF") settings_repository = Dynaconf( redis_enabled=True, redis={ "host": settings.REDIS_SERVER.split("redis://")[1], "port": settings.get("REDIS_SERVER_PORT", 6379), "db": settings.get("REDIS_SERVER_DB_REPO_SETTINGS", 1), "decode_responses": True, }, ) secrets_settings = Dynaconf( envvar_prefix="SECRETS_RSTUF", environments=True, ) # Celery setup celery = Celery(__name__) celery.conf.broker_url = settings.BROKER_SERVER celery.conf.result_backend = ( f"{settings.REDIS_SERVER}" f":{settings.get('REDIS_SERVER_PORT', 6379)}" f"/{settings.get('REDIS_SERVER_DB_RESULT', 0)}" ) celery.conf.accept_content = ["json", "application/json"] celery.conf.task_serializer = "json" celery.conf.result_serializer = "json" celery.conf.task_track_started = True celery.conf.broker_heartbeat = 0 celery.conf.result_persistent = True celery.conf.task_acks_late = True celery.conf.broker_pool_limit = None # celery.conf.broker_use_ssl # https://github.com/repository-service-tuf/repository-service-tuf-api/issues/91
[docs] def pre_lock_bootstrap(task_id): """ Add a pre-lock to the bootstrap repository settings. Add to the repository settings in Redis the lock as `pre-<task_id>`. Args: task_id: Task id generated by bootstrap """ settings_data = settings_repository.as_dict( env=settings_repository.current_env ) settings_data["BOOTSTRAP"] = f"pre-{task_id}" redis_loader.write(settings_repository, settings_data)
[docs] def release_bootstrap_lock(): """ Remove the pre-lock from repository settings. Move the repository settings BOOTSTRAP to None if not finished. """ settings_data = settings_repository.as_dict( env=settings_repository.current_env ) settings_data["BOOTSTRAP"] = None redis_loader.write(settings_repository, settings_data)
[docs] def bootstrap_state() -> BootstrapState: """ Bootstrap state The bootstrap state is registered in Redis. Detailed definitions are available in https://repository-service-tuf.readthedocs.io/en/stable/devel/design.html#tuf-repository-settings # noqa """ # Reload the settings # The reload is required because the settings object is created in the # `app.py`'s initialization. The `settings_repository.get_fresh() doesn't # correctly reload because of that the settings_repository.reload() do # the job. settings_repository.reload() bootstrap = settings_repository.get_fresh("BOOTSTRAP") bootstrap_state = BootstrapState(bootstrap=False, state=None, task_id=None) if bootstrap is None: return bootstrap_state if len(bootstrap.split("-")) == 1: # This is a finished bootstrap. It only contains the `<task-id>`` bootstrap_state.bootstrap = True bootstrap_state.state = "finished" bootstrap_state.task_id = bootstrap return bootstrap_state elif len(bootstrap.split("-")) == 2: # This is considered an intermediated state. It is not finished because # there is a `<state>-` like 'pre-<task_id>' or 'signing-<task_id>'. bootstrap_state.bootstrap = False bootstrap_state.state = bootstrap.split("-")[0] bootstrap_state.task_id = bootstrap.split("-")[1] return bootstrap_state
[docs] def get_task_id(): return uuid4().hex
@celery.task(name="app.repository_service_tuf_worker") def repository_metadata(action, payload): logging.debug(f"New tasks action submitted {action}") return True