Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge SHM and context resource deployment #1963

Merged
Changes from 1 commit
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
cc9e31f
:truck: Move creation of context resources into 'shm deploy'
jemrobinson Jun 20, 2024
e1e1e4a
:truck: Move teardown of context resources into 'shm teardown'
jemrobinson Jun 20, 2024
4f0a048
:truck: Move ContextInfrastructure to BackendInfrastructure
jemrobinson Jun 20, 2024
f5b3c19
:coffin: Drop unused stack_all_config property
jemrobinson Jun 20, 2024
865868d
:truck: Use context resource group for SHM resources
jemrobinson Jun 21, 2024
abd7d2f
:recycle: Use json_safe to construct SHM name in context
jemrobinson Jun 21, 2024
f86f183
:alien: Add ensure_dns_zone function to AzureApi
jemrobinson Jun 21, 2024
d24d944
:truck: Move ContextSettings to config
jemrobinson Jun 21, 2024
df5899e
:recycle: Drop key argument to ContextSettings and use JSON-safe 'nam…
jemrobinson Jun 21, 2024
2bdc430
:bug: Catch blob_exists exception when the storage account does not e…
jemrobinson Jun 21, 2024
4a3c7bc
:truck: Auto-construct SHMConfig using optional fqdn and entra-tenant…
jemrobinson Jun 21, 2024
729b942
:sparkles: Add ensure_dns_caa_record function
jemrobinson Jun 21, 2024
a0c669d
:sparkles: Upload SHM config after deployment
jemrobinson Jun 21, 2024
7338d3d
:truck: Move DNS zone creation to BackendInfrastructure
jemrobinson Jun 21, 2024
9079445
:truck: Move CAA record creation to BackendInfrastructure
jemrobinson Jun 21, 2024
7b604e6
:wrench: Check SHM config diff if user has made local changes
jemrobinson Jun 21, 2024
6ae1e16
:truck: Move config upload closer to config creation
jemrobinson Jun 24, 2024
ef4f625
:truck: Move creation of domain verification record into BackendInfra…
jemrobinson Jun 24, 2024
72af56f
:truck: Move Entra ID verification to single location inside BackendI…
jemrobinson Jun 24, 2024
f927fe9
:wrench: Add SRE sandbox to test DSHPulumiConfig
jemrobinson Jun 24, 2024
4a104de
:coffin: Drop unnecessary sre_name argument to SREProjectManager and …
jemrobinson Jun 24, 2024
aa78dcc
:white_check_mark: Add tests for SREProjectManager
jemrobinson Jun 24, 2024
b4d4a02
:bug: Only upload SHM config to blob storage after the container has …
jemrobinson Jun 24, 2024
3d76376
:coffin: Drop Pulumi part of 'dsh deploy shm' and 'dsh teardown shm'
jemrobinson Jun 24, 2024
4dd7d11
:white_check_mark: Add tests for pulumi run sre
jemrobinson Jun 24, 2024
caec4f1
:recycle: Simplify UserHandler to only point to a single GuacamoleUs…
jemrobinson Jun 24, 2024
9d6b52d
:coffin: Remove unused SHMProjectManager
jemrobinson Jun 24, 2024
85a6069
:truck: Move 'location' parameter from Context to SHMConfig
jemrobinson Jun 24, 2024
9f85b9a
:coffin: Drop unused DeclarativeSHM
jemrobinson Jun 24, 2024
46f1f8a
:truck: Rename BackendInfrastructure to ImperativeSHM
jemrobinson Jun 24, 2024
66af49a
:coffin: Drop unused template-shm and upload-shm and update docs acco…
jemrobinson Jun 24, 2024
f5111e3
:recycle: Simplify 'dsh config X' commands by removing the '-sre' suf…
jemrobinson Jun 24, 2024
5642f8b
:sparkles: Add EntraGroupName type and validator
jemrobinson Jun 25, 2024
4bd0abc
:sparkles: Add personal_access_token property to AzureCliSingleton
jemrobinson Jun 25, 2024
a867152
:truck: Directly make the group-ID lookups from Azure CLI
jemrobinson Jun 25, 2024
c9fd5d5
:sparkles: Make admin_group_name a property of ConfigSectionSHM. Clos…
jemrobinson Jun 25, 2024
3f3d835
:coffin: Remove 'admin_group_id' from context
jemrobinson Jun 25, 2024
5021f97
:truck: Renamed ContextSettings to ContextManager
jemrobinson Jun 25, 2024
f0b0a94
:truck: Move Context into config module
jemrobinson Jun 25, 2024
36557c0
:truck: Move admin_group_name from SHMConfig to Context
jemrobinson Jun 25, 2024
deb3cb7
:recycle: Rename 'from_local' to 'from_args'
jemrobinson Jun 25, 2024
1d0524c
:bug: Remove deprecated multiline exceptions
jemrobinson Jun 25, 2024
9469082
:wrench: Use 'component' tag to indicate SHM/SRE
jemrobinson Jun 27, 2024
900f185
:sparkles: Add SafeString type and validator
jemrobinson Jun 27, 2024
0f5e6c4
:wrench: Ensure Context has 'name' and 'description'
jemrobinson Jun 27, 2024
e54188b
Merge branch 'develop' into 1900-merge-shm-with-context
jemrobinson Jun 27, 2024
0b6fa65
:bug: Avoid recursion loop when logging in to Azure CLI
jemrobinson Jun 27, 2024
b0a8d8d
:loud_sound: Remove coloured exception messages
jemrobinson Jun 27, 2024
0c24d55
:truck: Reduce dependency of UserHandler on DSHPulumiConfig
jemrobinson Jun 27, 2024
82c89e3
:goal_net: Catch errors when attempting to retrieve remote configs
jemrobinson Jun 27, 2024
5151a31
:goal_net: Ensure that from_remote calls are wrapped inside try/excep…
jemrobinson Jun 27, 2024
b6bf718
:bug: Remove checks for SHM Pulumi project which no longer exists
jemrobinson Jun 27, 2024
c12091d
:coffin: Drop shm_name property of Context
jemrobinson Jun 27, 2024
48b88af
:wrench: Add description to SREConfig
jemrobinson Jun 27, 2024
41c5ce5
:truck: Drop 'context' suffix from SHM resources
jemrobinson Jun 27, 2024
7d359a0
:coffin: Remove duplicated output in context show
jemrobinson Jun 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Merge branch 'develop' into 1900-merge-shm-with-context
  • Loading branch information
jemrobinson committed Jun 27, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit e54188b5f7207ae0550be1ed741f0df45a1269aa
10 changes: 10 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
@@ -599,6 +599,16 @@
"contributions": [
"bug"
]
},
{
"login": "dsj976",
"name": "David Salvador Jasin",
"avatar_url": "https://avatars.githubusercontent.com/u/57944311?v=4",
"profile": "https://github.com/dsj976",
"contributions": [
"bug",
"doc"
]
}
],
"contributorsSortAlphabetically": true,
13 changes: 7 additions & 6 deletions README.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions data_safe_haven/administration/users/entra_users.py
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
from data_safe_haven.exceptions import (
DataSafeHavenEntraIDError,
DataSafeHavenError,
DataSafeHavenInputError,
DataSafeHavenTypeError,
)
from data_safe_haven.external import GraphApi
from data_safe_haven.functions import password
@@ -51,10 +51,10 @@ def add(self, new_users: Sequence[ResearchUser]) -> None:
msg = (
f"User '[green]{user.username}[/]' is missing an email address."
)
raise DataSafeHavenInputError(msg)
raise DataSafeHavenTypeError(msg)
if not user.phone_number:
msg = f"User '[green]{user.username}[/]' is missing a phone number."
raise DataSafeHavenInputError(msg)
raise DataSafeHavenTypeError(msg)
self.graph_api.create_user(
request_json, user.email_address, user.phone_number
)
13 changes: 6 additions & 7 deletions data_safe_haven/commands/shm.py
Original file line number Diff line number Diff line change
@@ -8,8 +8,8 @@
from data_safe_haven.config import ContextManager, SHMConfig
from data_safe_haven.exceptions import (
DataSafeHavenAzureAPIAuthenticationError,
DataSafeHavenAzureError,
DataSafeHavenConfigError,
DataSafeHavenError,
)
from data_safe_haven.infrastructure import ImperativeSHM
from data_safe_haven.logging import get_logger
@@ -45,8 +45,9 @@ def deploy(
] = None,
) -> None:
"""Deploy a Safe Haven Management environment."""
# Load selected context
logger = get_logger()

# Load selected context
try:
context = ContextManager.from_file().assert_context()
except DataSafeHavenConfigError as exc:
@@ -113,7 +114,7 @@ def deploy(
msg = "Failed to authenticate with the Azure API. You may not be logged into the Azure CLI, or your login may have expired. Try running `az login`."
logger.critical(msg)
raise typer.Exit(1) from exc
except DataSafeHavenAzureError as exc:
except DataSafeHavenError as exc:
msg = "Failed to deploy Data Safe Haven infrastructure."
logger.critical(msg)
raise typer.Exit(1) from exc
@@ -140,8 +141,6 @@ def teardown() -> None:
config = SHMConfig.from_remote(context)
shm_infra = ImperativeSHM(context, config)
shm_infra.teardown()
except DataSafeHavenAzureAPIAuthenticationError as exc:
logger.critical(
"Failed to authenticate with the Azure API. You may not be logged into the Azure CLI, or your login may have expired. Try running `az login`."
)
except DataSafeHavenError as exc:
logger.critical("Could not teardown Safe Haven Management environment.")
raise typer.Exit(1) from exc
17 changes: 11 additions & 6 deletions data_safe_haven/commands/sre.py
Original file line number Diff line number Diff line change
@@ -10,9 +10,10 @@
SHMConfig,
SREConfig,
)
from data_safe_haven.exceptions import DataSafeHavenError, DataSafeHavenInputError
from data_safe_haven.exceptions import DataSafeHavenError, DataSafeHavenPulumiError
from data_safe_haven.external import GraphApi
from data_safe_haven.infrastructure import SREProjectManager
from data_safe_haven.logging import get_logger
from data_safe_haven.provisioning import SREProvisioningManager

sre_command_group = typer.Typer()
@@ -36,6 +37,7 @@ def deploy(
shm_config = SHMConfig.from_remote(context)
sre_config = SREConfig.from_remote_by_name(context, name)

logger = get_logger()
try:
# Load GraphAPI as this may require user-interaction that is not possible as
# part of a Pulumi declarative command
@@ -103,8 +105,10 @@ def deploy(
)
manager.run()
except DataSafeHavenError as exc:
msg = f"Could not deploy Secure Research Environment {sre_config.safe_name}."
raise DataSafeHavenError(msg) from exc
logger.critical(
f"Could not deploy Secure Research Environment {sre_config.safe_name}."
)
raise typer.Exit(code=1) from exc
finally:
# Upload Pulumi config to blob storage
pulumi_config.upload(context)
@@ -120,6 +124,7 @@ def teardown(
shm_config = SHMConfig.from_remote(context)
sre_config = SREConfig.from_remote_by_name(context, name)

logger = get_logger()
try:
# Load GraphAPI as this may require user-interaction that is not possible as
# part of a Pulumi declarative command
@@ -139,15 +144,15 @@ def teardown(
stack.teardown()
except Exception as exc:
msg = "Unable to teardown Pulumi infrastructure."
raise DataSafeHavenInputError(msg) from exc
raise DataSafeHavenPulumiError(msg) from exc

# Remove Pulumi project from Pulumi config file
del pulumi_config[name]

# Upload Pulumi config to blob storage
pulumi_config.upload(context)
except DataSafeHavenError as exc:
msg = (
logger.critical(
f"Could not teardown Secure Research Environment '{sre_config.safe_name}'."
)
raise DataSafeHavenError(msg) from exc
raise typer.Exit(1) from exc
24 changes: 14 additions & 10 deletions data_safe_haven/commands/users.py
Original file line number Diff line number Diff line change
@@ -55,8 +55,8 @@ def add(
users = UserHandler(context, pulumi_config, graph_api)
users.add(csv)
except DataSafeHavenError as exc:
msg = f"Could not add users to Data Safe Haven '{shm_name}'."
raise DataSafeHavenError(msg) from exc
logger.critical(f"Could not add users to Data Safe Haven '{shm_name}'.")
raise typer.Exit(1) from exc


@users_command_group.command("list")
@@ -91,8 +91,8 @@ def list_users(
users = UserHandler(context, pulumi_config, graph_api)
users.list(sre)
except DataSafeHavenError as exc:
msg = f"Could not list users for Data Safe Haven '{shm_name}'."
raise DataSafeHavenError(msg) from exc
logger.critical(f"Could not list users for Data Safe Haven '{shm_name}'.")
raise typer.Exit(1) from exc


@users_command_group.command()
@@ -159,8 +159,10 @@ def register(
)
users.register(sre_name, usernames_to_register)
except DataSafeHavenError as exc:
msg = f"Could not register users from Data Safe Haven '{shm_name}' with SRE '{sre_name}'."
raise DataSafeHavenError(msg) from exc
logger.critical(
f"Could not register users from Data Safe Haven '{shm_name}' with SRE '{sre_name}'."
)
raise typer.Exit(1) from exc


@users_command_group.command()
@@ -198,8 +200,8 @@ def remove(
users = UserHandler(context, pulumi_config, graph_api)
users.remove(usernames)
except DataSafeHavenError as exc:
msg = f"Could not remove users from Data Safe Haven '{shm_name}'."
raise DataSafeHavenError(msg) from exc
logger.critical(f"Could not remove users from Data Safe Haven '{shm_name}'.")
raise typer.Exit(1) from exc


@users_command_group.command()
@@ -267,5 +269,7 @@ def unregister(
):
users.unregister(group_name, usernames_to_unregister)
except DataSafeHavenError as exc:
msg = f"Could not unregister users from Data Safe Haven '{shm_name}' with SRE '{sre_name}'."
raise DataSafeHavenError(msg) from exc
logger.critical(
f"Could not unregister users from Data Safe Haven '{shm_name}' with SRE '{sre_name}'."
)
raise typer.Exit(1) from exc
9 changes: 4 additions & 5 deletions data_safe_haven/config/context_manager.py
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@
from data_safe_haven.directories import config_dir
from data_safe_haven.exceptions import (
DataSafeHavenConfigError,
DataSafeHavenParameterError,
DataSafeHavenValueError,
)
from data_safe_haven.logging import get_logger
from data_safe_haven.serialisers import YAMLSerialisableModel
@@ -68,7 +68,7 @@ def selected(self, name: str | None) -> None:
self.logger.info(f"Switched context to '{name}'.")
else:
msg = f"Context '{name}' is not defined."
raise DataSafeHavenParameterError(msg)
raise DataSafeHavenValueError(msg)

@property
def context(self) -> Context | None:
@@ -130,7 +130,7 @@ def add(
# Ensure context is not already present
if name in self.available:
msg = f"A context with name '{name}' is already defined."
raise DataSafeHavenParameterError(msg)
raise DataSafeHavenValueError(msg)

self.logger.info(f"Creating a new context with name '{name}'.")
self.contexts[name] = Context(
@@ -145,8 +145,7 @@ def add(
def remove(self, name: str) -> None:
if name not in self.available:
msg = f"No context with name '{name}'."
raise DataSafeHavenParameterError(msg)

raise DataSafeHavenValueError(msg)
del self.contexts[name]

# Prevent having a deleted context selected
84 changes: 67 additions & 17 deletions data_safe_haven/exceptions/__init__.py
Original file line number Diff line number Diff line change
@@ -2,6 +2,12 @@


class DataSafeHavenError(Exception):
"""
Parent class for all DataSafeHaven exceptions.

This class is not intended to be instantiated directly. Developers should use one of the subclasses instead.
"""

def __init__(self, message: str | bytes):
super().__init__(message)

@@ -12,61 +18,105 @@ def __init__(self, message: str | bytes):
logger.error(message_str.replace("\n", r"\n"))


class DataSafeHavenCloudError(DataSafeHavenError):
pass
class DataSafeHavenAzureError(DataSafeHavenError):
"""
Exception class for handling errors when interacting with Azure.

Raise this error when, for example, creating resources in Azure fails.
"""

class DataSafeHavenConfigError(DataSafeHavenError):
pass


class DataSafeHavenEntraIDError(DataSafeHavenCloudError):
class DataSafeHavenAzureAPIAuthenticationError(DataSafeHavenError):
"""
Exception class for handling errors when authenticating against the Azure API.

Used to capture exceptions generated when the user is not authenticated or authentication has expired.
"""

pass


class DataSafeHavenInputError(DataSafeHavenError):
class DataSafeHavenConfigError(DataSafeHavenError):
"""
Exception class for handling errors related to configuration files.

Examples include missing configuration files or invalid configuration values.
"""

pass


class DataSafeHavenInternalError(DataSafeHavenError):
class DataSafeHavenEntraIDError(DataSafeHavenError):
"""
Exception class for handling errors when interacting with Entra ID.

For example, when adding users to an Entra group fails.
"""

pass


class DataSafeHavenIPRangeError(DataSafeHavenError):
"""Exception raised when it is not possible to generate a valid IPv4 range."""

pass


class DataSafeHavenNotImplementedError(DataSafeHavenInternalError):
class DataSafeHavenMicrosoftGraphError(DataSafeHavenAzureError):
"""
Exception class for handling errors when interacting with the Microsoft Graph API.
"""

pass


class DataSafeHavenParameterError(DataSafeHavenError):
class DataSafeHavenPulumiError(DataSafeHavenError):
"""
Exception class for handling errors when interacting with Pulumi.

For example, when a Pulumi operation such as a deployment fails.
"""

pass


class DataSafeHavenSSLError(DataSafeHavenError):
pass
"""
Exception class for handling errors related to administration of SSL certificates.

For example, errors refreshing or creating SSL certificates.
"""

class DataSafeHavenAzureError(DataSafeHavenCloudError):
pass


class DataSafeHavenAzureAPIError(DataSafeHavenError):
pass
class DataSafeHavenTypeError(DataSafeHavenError):
"""
Exception class for handling errors related to type checking.

For example, when a function is called with an argument of the wrong type.
"""

class DataSafeHavenAzureAPIAuthenticationError(DataSafeHavenAzureAPIError):
pass


class DataSafeHavenUserHandlingError(DataSafeHavenInternalError):
pass
class DataSafeHavenUserHandlingError(DataSafeHavenError):
"""
Exception class for handling errors related to user handling.

For example, when listing or registering users fails.
"""

class DataSafeHavenMicrosoftGraphError(DataSafeHavenAzureError):
pass


class DataSafeHavenPulumiError(DataSafeHavenCloudError):
class DataSafeHavenValueError(DataSafeHavenError):
"""
Exception class for handling errors related to value checking.

For example, when a function is called with an argument of the wrong value.
"""

pass
7 changes: 2 additions & 5 deletions data_safe_haven/external/api/azure_api.py
Original file line number Diff line number Diff line change
@@ -62,10 +62,7 @@
from azure.storage.blob import BlobClient, BlobServiceClient
from azure.storage.filedatalake import DataLakeServiceClient

from data_safe_haven.exceptions import (
DataSafeHavenAzureError,
DataSafeHavenInternalError,
)
from data_safe_haven.exceptions import DataSafeHavenAzureError
from data_safe_haven.external.interface.azure_authenticator import AzureAuthenticator
from data_safe_haven.logging import get_logger

@@ -966,7 +963,7 @@ def remove_resource_group(self, resource_group_name: str) -> None:
]
if resource_groups:
msg = f"There are still {len(resource_groups)} resource group(s) remaining."
raise DataSafeHavenInternalError(msg)
raise DataSafeHavenAzureError(msg)
self.logger.info(
f"Ensured that resource group [green]{resource_group_name}[/] does not exist.",
)
Loading
You are viewing a condensed version of this merge commit. You can view the full changes here.