From 60fa4d221dd73a2a95fab7217eaf25271762f1df Mon Sep 17 00:00:00 2001 From: Matt Craddock <5796417+craddm@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:35:31 +0000 Subject: [PATCH 1/3] Fix linting --- data_safe_haven/commands/sre.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data_safe_haven/commands/sre.py b/data_safe_haven/commands/sre.py index 8c3e0b5cdc..2cccfb90d0 100644 --- a/data_safe_haven/commands/sre.py +++ b/data_safe_haven/commands/sre.py @@ -50,6 +50,7 @@ def deploy( pulumi_config = DSHPulumiConfig.from_remote_or_create( context, encrypted_key=None, projects={} ) + sre_config = SREConfig.from_remote_by_name(context, name) # Check whether current IP address is authorised to take administrator actions @@ -68,6 +69,7 @@ def deploy( pulumi_config=pulumi_config, create_project=True, ) + # Set Azure options stack.add_option( "azure-native:location", sre_config.azure.location, replace=False From 09ea41febad0005d25e4117a5e4d7d2b3f4290db Mon Sep 17 00:00:00 2001 From: Matt Craddock <5796417+craddm@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:36:17 +0000 Subject: [PATCH 2/3] Change logic of create/selecting stack, overwrite newly generated key with project key --- .../infrastructure/project_manager.py | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/data_safe_haven/infrastructure/project_manager.py b/data_safe_haven/infrastructure/project_manager.py index eca352b28b..3c526e39dd 100644 --- a/data_safe_haven/infrastructure/project_manager.py +++ b/data_safe_haven/infrastructure/project_manager.py @@ -119,27 +119,46 @@ def stack(self) -> automation.Stack: """Load the Pulumi stack, creating if needed.""" if not self._stack: self.logger.debug(f"Creating/loading stack [green]{self.stack_name}[/].") + # Note: `create_or_select_stack` is not used here because + # when creating a stack, it generates a new encryption key rather than using the project's key + # There is no way to check if a stack exists other than trying to select it + # try: - self._stack = automation.create_or_select_stack( + self._stack = automation.select_stack( opts=automation.LocalWorkspaceOptions( env_vars=self.account.env, project_settings=self.project_settings, - secrets_provider=self.context.pulumi_secrets_provider_url, stack_settings={self.stack_name: self.stack_settings}, + secrets_provider=self.context.pulumi_secrets_provider_url, ), program=self.program, project_name=self.project_name, stack_name=self.stack_name, ) - self.logger.info(f"Loaded stack [green]{self.stack_name}[/].") - # Ensure encrypted key is stored in the Pulumi configuration - self.update_dsh_pulumi_encrypted_key(self._stack.workspace) - # Ensure workspace plugins are installed - self.install_plugins(self._stack.workspace) - except automation.CommandError as exc: - self.log_exception(exc) - msg = f"Could not load Pulumi stack {self.stack_name}." - raise DataSafeHavenPulumiError(msg) from exc + except automation.CommandError: + try: + self._stack = automation.create_stack( + opts=automation.LocalWorkspaceOptions( + env_vars=self.account.env, + project_settings=self.project_settings, + secrets_provider=self.context.pulumi_secrets_provider_url, + ), + program=self.program, + project_name=self.project_name, + stack_name=self.stack_name, + ) + self._stack.workspace.save_stack_settings( + self.stack_name, self.stack_settings + ) + except automation.CommandError as exc: + self.log_exception(exc) + msg = f"Could not create Pulumi stack {self.stack_name}." + raise DataSafeHavenPulumiError(msg) from exc + self.logger.info(f"Loaded stack [green]{self.stack_name}[/].") + # Ensure encrypted key is stored in the Pulumi configuration + self.update_dsh_pulumi_encrypted_key(self._stack.workspace) + # Ensure workspace plugins are installed + self.install_plugins(self._stack.workspace) return self._stack def add_option(self, name: str, value: str, *, replace: bool) -> None: @@ -428,7 +447,6 @@ def update_dsh_pulumi_project(self) -> None: def update_dsh_pulumi_encrypted_key(self, workspace: automation.Workspace) -> None: """Update encrypted key in the DSHPulumiProject object""" stack_key = workspace.stack_settings(stack_name=self.stack_name).encrypted_key - if not self.pulumi_config.encrypted_key: self.pulumi_config.encrypted_key = stack_key elif self.pulumi_config.encrypted_key != stack_key: From 818aeaa7a53a87bbfca324d040f2522ae2d88fbb Mon Sep 17 00:00:00 2001 From: Matt Craddock <5796417+craddm@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:48:33 +0000 Subject: [PATCH 3/3] Remove function for updating stack with project key --- .../infrastructure/project_manager.py | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/data_safe_haven/infrastructure/project_manager.py b/data_safe_haven/infrastructure/project_manager.py index 3c526e39dd..5153a74a54 100644 --- a/data_safe_haven/infrastructure/project_manager.py +++ b/data_safe_haven/infrastructure/project_manager.py @@ -119,10 +119,11 @@ def stack(self) -> automation.Stack: """Load the Pulumi stack, creating if needed.""" if not self._stack: self.logger.debug(f"Creating/loading stack [green]{self.stack_name}[/].") - # Note: `create_or_select_stack` is not used here because - # when creating a stack, it generates a new encryption key rather than using the project's key - # There is no way to check if a stack exists other than trying to select it - # + # Note: `create_or_select_stack` is not used here because we need to know whether the stack exists or not. + # There is no way to check if a stack exists other than trying to `create_stack` or `select_stack` and catching the error. + # `create_or_select_stack` never generates an error, and doesn't tell us whether the stack was created or selected. + # When creating a stack, it generates a new encryption key rather than using the project's key, so we need to set the key + # manually after creating the stack. try: self._stack = automation.select_stack( opts=automation.LocalWorkspaceOptions( @@ -155,8 +156,6 @@ def stack(self) -> automation.Stack: msg = f"Could not create Pulumi stack {self.stack_name}." raise DataSafeHavenPulumiError(msg) from exc self.logger.info(f"Loaded stack [green]{self.stack_name}[/].") - # Ensure encrypted key is stored in the Pulumi configuration - self.update_dsh_pulumi_encrypted_key(self._stack.workspace) # Ensure workspace plugins are installed self.install_plugins(self._stack.workspace) return self._stack @@ -444,15 +443,6 @@ def update_dsh_pulumi_project(self) -> None: } self.pulumi_project.stack_config = all_config_dict - def update_dsh_pulumi_encrypted_key(self, workspace: automation.Workspace) -> None: - """Update encrypted key in the DSHPulumiProject object""" - stack_key = workspace.stack_settings(stack_name=self.stack_name).encrypted_key - if not self.pulumi_config.encrypted_key: - self.pulumi_config.encrypted_key = stack_key - elif self.pulumi_config.encrypted_key != stack_key: - msg = "Stack encrypted key does not match project encrypted key" - raise DataSafeHavenPulumiError(msg) - class SREProjectManager(ProjectManager): """Interact with an SRE using Pulumi"""