diff --git a/data_safe_haven/backend/__init__.py b/data_safe_haven/backend/__init__.py deleted file mode 100644 index 7401760657..0000000000 --- a/data_safe_haven/backend/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .backend import Backend - -__all__ = [ - "Backend", -] diff --git a/data_safe_haven/commands/context.py b/data_safe_haven/commands/context.py index 9055728a9d..da70dbfd87 100644 --- a/data_safe_haven/commands/context.py +++ b/data_safe_haven/commands/context.py @@ -4,7 +4,7 @@ import typer from rich import print -from data_safe_haven.backend import Backend +from data_safe_haven.context import Context from data_safe_haven.config import ContextSettings from data_safe_haven.functions import validate_aad_guid @@ -138,13 +138,13 @@ def remove( @context_command_group.command() def create() -> None: """Create Data Safe Haven context infrastructure""" - backend = Backend() - backend.create() - backend.config.upload() + context = Context() + context.create() + context.config.upload() @context_command_group.command() def teardown() -> None: """Tear down Data Safe Haven context infrastructure""" - backend = Backend() - backend.teardown() + context = Context() + context.teardown() diff --git a/data_safe_haven/commands/teardown.py b/data_safe_haven/commands/teardown.py index 2895d20ee7..5e6e7d08ec 100644 --- a/data_safe_haven/commands/teardown.py +++ b/data_safe_haven/commands/teardown.py @@ -3,18 +3,12 @@ import typer -from .teardown_backend import teardown_backend from .teardown_shm import teardown_shm from .teardown_sre import teardown_sre teardown_command_group = typer.Typer() -@teardown_command_group.command(help="Tear down a deployed Data Safe Haven backend.") -def backend() -> None: - teardown_backend() - - @teardown_command_group.command( help="Tear down a deployed a Safe Haven Management component." ) diff --git a/data_safe_haven/commands/teardown_backend.py b/data_safe_haven/commands/teardown_backend.py deleted file mode 100644 index f90e4a6c16..0000000000 --- a/data_safe_haven/commands/teardown_backend.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tear down a deployed Data Safe Haven backend""" -from data_safe_haven.backend import Backend -from data_safe_haven.exceptions import ( - DataSafeHavenError, - DataSafeHavenInputError, -) - - -def teardown_backend() -> None: - """Tear down a deployed Data Safe Haven backend""" - try: - # Remove the Pulumi backend - try: - backend = Backend() - backend.teardown() - except Exception as exc: - msg = f"Unable to teardown Pulumi backend.\n{exc}" - raise DataSafeHavenInputError(msg) from exc - except DataSafeHavenError as exc: - msg = f"Could not teardown Data Safe Haven backend.\n{exc}" - raise DataSafeHavenError(msg) from exc diff --git a/data_safe_haven/config/config.py b/data_safe_haven/config/config.py index f7d920a910..c0f32247df 100644 --- a/data_safe_haven/config/config.py +++ b/data_safe_haven/config/config.py @@ -95,7 +95,7 @@ class ConfigSectionAzure(ConfigSection): @dataclass -class ConfigSectionBackend(ConfigSection): +class ConfigSectionContext(ConfigSection): key_vault_name: str = "" managed_identity_name: str = "" resource_group_name: str = "" @@ -350,12 +350,12 @@ class Config: def __init__(self) -> None: # Initialise config sections self.azure_: ConfigSectionAzure | None = None - self.backend_: ConfigSectionBackend | None = None + self.context_: ConfigSectionContext | None = None self.pulumi_: ConfigSectionPulumi | None = None self.shm_: ConfigSectionSHM | None = None self.tags_: ConfigSectionTags | None = None self.sres: dict[str, ConfigSectionSRE] = defaultdict(ConfigSectionSRE) - # Read backend settings + # Read context settings settings = ContextSettings.from_file() context = settings.context # Check if backend exists and was loaded @@ -367,13 +367,13 @@ def __init__(self) -> None: self.subscription_name = context.subscription_name self.azure.location = context.location self.azure.admin_group_id = context.admin_group_id - self.backend_storage_container_name = "config" + self.context_storage_container_name = "config" # Set derived names self.shm_name_ = alphanumeric(self.name).lower() self.filename = f"config-{self.shm_name_}.yaml" - self.backend_resource_group_name = f"shm-{self.shm_name_}-rg-backend" - self.backend_storage_account_name = ( - f"shm{self.shm_name_[:14]}backend" # maximum of 24 characters allowed + self.context_resource_group_name = f"shm-{self.shm_name_}-rg-context" + self.context_storage_account_name = ( + f"shm{self.shm_name_[:14]}context" # maximum of 24 characters allowed ) self.work_directory = config_dir() / self.shm_name_ self.azure_api = AzureApi(subscription_name=self.subscription_name) @@ -383,18 +383,18 @@ def __init__(self) -> None: yaml_input = yaml.safe_load( self.azure_api.download_blob( self.filename, - self.backend_resource_group_name, - self.backend_storage_account_name, - self.backend_storage_container_name, + self.context_resource_group_name, + self.context_storage_account_name, + self.context_storage_container_name, ) ) # Attempt to decode each config section if yaml_input: if "azure" in yaml_input: self.azure_ = chili.decode(yaml_input["azure"], ConfigSectionAzure) - if "backend" in yaml_input: - self.backend_ = chili.decode( - yaml_input["backend"], ConfigSectionBackend + if "context" in yaml_input: + self.context_ = chili.decode( + yaml_input["context"], ConfigSectionContext ) if "pulumi" in yaml_input: self.pulumi_ = chili.decode(yaml_input["pulumi"], ConfigSectionPulumi) @@ -411,16 +411,16 @@ def azure(self) -> ConfigSectionAzure: return self.azure_ @property - def backend(self) -> ConfigSectionBackend: - if not self.backend_: - self.backend_ = ConfigSectionBackend( - key_vault_name=f"shm-{self.shm_name_[:9]}-kv-backend", - managed_identity_name=f"shm-{self.shm_name_}-identity-reader-backend", - resource_group_name=self.backend_resource_group_name, - storage_account_name=self.backend_storage_account_name, - storage_container_name=self.backend_storage_container_name, + def context(self) -> ConfigSectionContext: + if not self.context_: + self.context_ = ConfigSectionContext( + key_vault_name=f"shm-{self.shm_name_[:9]}-kv-context", + managed_identity_name=f"shm-{self.shm_name_}-identity-reader-context", + resource_group_name=self.context_resource_group_name, + storage_account_name=self.context_storage_account_name, + storage_container_name=self.context_storage_container_name, ) - return self.backend_ + return self.context_ @property def pulumi(self) -> ConfigSectionPulumi: @@ -445,8 +445,8 @@ def __str__(self) -> str: contents: dict[str, Any] = {} if self.azure_: contents["azure"] = self.azure.to_dict() - if self.backend_: - contents["backend"] = self.backend.to_dict() + if self.context_: + contents["context"] = self.context.to_dict() if self.pulumi_: contents["pulumi"] = self.pulumi.to_dict() if self.shm_: @@ -485,9 +485,9 @@ def upload(self) -> None: self.azure_api.upload_blob( str(self), self.filename, - self.backend_resource_group_name, - self.backend_storage_account_name, - self.backend_storage_container_name, + self.context_resource_group_name, + self.context_storage_account_name, + self.context_storage_container_name, ) def write_stack(self, name: str, path: pathlib.Path) -> None: diff --git a/data_safe_haven/context/__init__.py b/data_safe_haven/context/__init__.py new file mode 100644 index 0000000000..94370d9ba1 --- /dev/null +++ b/data_safe_haven/context/__init__.py @@ -0,0 +1,5 @@ +from .context import Context + +__all__ = [ + "Context", +] diff --git a/data_safe_haven/backend/backend.py b/data_safe_haven/context/context.py similarity index 81% rename from data_safe_haven/backend/backend.py rename to data_safe_haven/context/context.py index a303955af1..bbb037c2ca 100644 --- a/data_safe_haven/backend/backend.py +++ b/data_safe_haven/context/context.py @@ -1,17 +1,15 @@ -"""Azure backend for a Data Safe Haven deployment""" - from data_safe_haven.config import Config from data_safe_haven.exceptions import DataSafeHavenAzureError from data_safe_haven.external import AzureApi -class Backend: - """Azure backend for a Data Safe Haven deployment""" +class Context: + """Azure resources to support Data Safe Haven context""" def __init__(self) -> None: self.azure_api_: AzureApi | None = None self.config = Config() - self.tags = {"component": "backend"} | self.config.tags.to_dict() + self.tags = {"component": "context"} | self.config.tags.to_dict() @property def azure_api(self) -> AzureApi: @@ -37,28 +35,28 @@ def create(self) -> None: self.config.azure.tenant_id = self.azure_api.tenant_id resource_group = self.azure_api.ensure_resource_group( location=self.config.azure.location, - resource_group_name=self.config.backend.resource_group_name, + resource_group_name=self.config.context.resource_group_name, tags=self.tags, ) if not resource_group.name: - msg = f"Resource group '{self.config.backend.resource_group_name}' was not created." + msg = f"Resource group '{self.config.context.resource_group_name}' was not created." raise DataSafeHavenAzureError(msg) identity = self.azure_api.ensure_managed_identity( - identity_name=self.config.backend.managed_identity_name, + identity_name=self.config.context.managed_identity_name, location=resource_group.location, resource_group_name=resource_group.name, ) storage_account = self.azure_api.ensure_storage_account( location=resource_group.location, resource_group_name=resource_group.name, - storage_account_name=self.config.backend.storage_account_name, + storage_account_name=self.config.context.storage_account_name, tags=self.tags, ) if not storage_account.name: - msg = f"Storage account '{self.config.backend.storage_account_name}' was not created." + msg = f"Storage account '{self.config.context.storage_account_name}' was not created." raise DataSafeHavenAzureError(msg) _ = self.azure_api.ensure_storage_blob_container( - container_name=self.config.backend.storage_container_name, + container_name=self.config.context.storage_container_name, resource_group_name=resource_group.name, storage_account_name=storage_account.name, ) @@ -69,7 +67,7 @@ def create(self) -> None: ) keyvault = self.azure_api.ensure_keyvault( admin_group_id=self.config.azure.admin_group_id, - key_vault_name=self.config.backend.key_vault_name, + key_vault_name=self.config.context.key_vault_name, location=resource_group.location, managed_identity=identity, resource_group_name=resource_group.name, @@ -77,7 +75,7 @@ def create(self) -> None: ) if not keyvault.name: msg = ( - f"Keyvault '{self.config.backend.key_vault_name}' was not created." + f"Keyvault '{self.config.context.key_vault_name}' was not created." ) raise DataSafeHavenAzureError(msg) pulumi_encryption_key = self.azure_api.ensure_keyvault_key( @@ -87,7 +85,7 @@ def create(self) -> None: key_version = pulumi_encryption_key.id.split("/")[-1] self.config.pulumi.encryption_key_version = key_version except Exception as exc: - msg = f"Failed to create backend resources.\n{exc}" + msg = f"Failed to create context resources.\n{exc}" raise DataSafeHavenAzureError(msg) from exc def teardown(self) -> None: @@ -98,8 +96,8 @@ def teardown(self) -> None: """ try: self.azure_api.remove_resource_group( - self.config.backend.resource_group_name + self.config.context.resource_group_name ) except Exception as exc: - msg = f"Failed to destroy backend resources.\n{exc}" + msg = f"Failed to destroy context resources.\n{exc}" raise DataSafeHavenAzureError(msg) from exc diff --git a/data_safe_haven/external/interface/azure_container_instance.py b/data_safe_haven/external/interface/azure_container_instance.py index 623c837555..1f696f4b04 100644 --- a/data_safe_haven/external/interface/azure_container_instance.py +++ b/data_safe_haven/external/interface/azure_container_instance.py @@ -1,4 +1,3 @@ -"""Backend for a Data Safe Haven environment""" import contextlib import time diff --git a/data_safe_haven/external/interface/azure_postgresql_database.py b/data_safe_haven/external/interface/azure_postgresql_database.py index 395791105a..ad0edc30da 100644 --- a/data_safe_haven/external/interface/azure_postgresql_database.py +++ b/data_safe_haven/external/interface/azure_postgresql_database.py @@ -1,4 +1,3 @@ -"""Backend for a Data Safe Haven environment""" import datetime import pathlib import time