Skip to content

Commit

Permalink
Replace Pulumi login wrappers with env variable
Browse files Browse the repository at this point in the history
  • Loading branch information
JimMadge committed Oct 23, 2023
1 parent 39d6287 commit 302d554
Show file tree
Hide file tree
Showing 7 changed files with 7 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from data_safe_haven.exceptions import DataSafeHavenActiveDirectoryError
from data_safe_haven.external import AzureApi
from data_safe_haven.functions import b64encode
from data_safe_haven.infrastructure import PulumiAccount, SHMStackManager
from data_safe_haven.infrastructure import SHMStackManager
from data_safe_haven.utility import FileReader, LoggingSingleton

from .research_user import ResearchUser
Expand All @@ -23,7 +23,6 @@ def __init__(
**kwargs: Any,
) -> None:
super().__init__(*args, **kwargs)
PulumiAccount(config).handle_login()
shm_stack = SHMStackManager(config)
self.azure_api = AzureApi(config.subscription_name)
self.logger = LoggingSingleton()
Expand Down
3 changes: 1 addition & 2 deletions data_safe_haven/administration/users/guacamole_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@

from data_safe_haven.config import Config
from data_safe_haven.external import AzurePostgreSQLDatabase
from data_safe_haven.infrastructure import PulumiAccount, SREStackManager
from data_safe_haven.infrastructure import SREStackManager

from .research_user import ResearchUser


class GuacamoleUsers:
def __init__(self, config: Config, sre_name: str, *args: Any, **kwargs: Any):
super().__init__(*args, **kwargs)
PulumiAccount(config).handle_login()
sre_stack = SREStackManager(config, sre_name)
self.postgres_provisioner = AzurePostgreSQLDatabase(
sre_stack.output("remote_desktop")["connection_db_name"],
Expand Down
3 changes: 1 addition & 2 deletions data_safe_haven/commands/deploy_shm.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from data_safe_haven.exceptions import DataSafeHavenError
from data_safe_haven.external import GraphApi
from data_safe_haven.functions import password
from data_safe_haven.infrastructure import PulumiAccount, SHMStackManager
from data_safe_haven.infrastructure import SHMStackManager
from data_safe_haven.provisioning import SHMProvisioningManager


Expand Down Expand Up @@ -40,7 +40,6 @@ def deploy_shm(
verification_record = graph_api.add_custom_domain(config.shm.fqdn)

# Initialise Pulumi stack
PulumiAccount(config).handle_login()
stack = SHMStackManager(config)
# Set Azure options
stack.add_option("azure-native:location", config.azure.location, replace=False)
Expand Down
7 changes: 1 addition & 6 deletions data_safe_haven/commands/deploy_sre.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@
)
from data_safe_haven.external import GraphApi
from data_safe_haven.functions import alphanumeric, bcrypt_salt
from data_safe_haven.infrastructure import (
PulumiAccount,
SHMStackManager,
SREStackManager,
)
from data_safe_haven.infrastructure import SHMStackManager, SREStackManager
from data_safe_haven.provisioning import SREProvisioningManager
from data_safe_haven.utility import DatabaseSystem, SoftwarePackageCategory

Expand Down Expand Up @@ -52,7 +48,6 @@ def deploy_sre(
)

# Initialise Pulumi stack
PulumiAccount(config).handle_login()
shm_stack = SHMStackManager(config)
stack = SREStackManager(config, sre_name, graph_api_token=graph_api.token)
# Set Azure options
Expand Down
3 changes: 1 addition & 2 deletions data_safe_haven/commands/teardown_shm.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
DataSafeHavenError,
DataSafeHavenInputError,
)
from data_safe_haven.infrastructure import PulumiAccount, SHMStackManager
from data_safe_haven.infrastructure import SHMStackManager


def teardown_shm() -> None:
Expand All @@ -15,7 +15,6 @@ def teardown_shm() -> None:

# Remove infrastructure deployed with Pulumi
try:
PulumiAccount(config).handle_login()
stack = SHMStackManager(config)
stack.teardown()
except Exception as exc:
Expand Down
3 changes: 1 addition & 2 deletions data_safe_haven/commands/teardown_sre.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
)
from data_safe_haven.external import GraphApi
from data_safe_haven.functions import alphanumeric
from data_safe_haven.infrastructure import PulumiAccount, SREStackManager
from data_safe_haven.infrastructure import SREStackManager


def teardown_sre(name: str) -> None:
Expand All @@ -28,7 +28,6 @@ def teardown_sre(name: str) -> None:

# Remove infrastructure deployed with Pulumi
try:
PulumiAccount(config).handle_login()
stack = SREStackManager(config, sre_name, graph_api_token=graph_api.token)
if stack.work_dir.exists():
stack.teardown()
Expand Down
65 changes: 1 addition & 64 deletions data_safe_haven/infrastructure/stack_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@
import logging
import pathlib
import shutil
import subprocess
import time
from contextlib import suppress
from importlib import metadata
from shutil import which
from typing import Any

import typer
from pulumi import automation

from data_safe_haven.config import Config
Expand All @@ -26,12 +24,10 @@ class PulumiAccount:
def __init__(self, config: Config):
self.cfg = config
self.env_: dict[str, Any] | None = None
self.logger = LoggingSingleton()
path = which("pulumi")
if path is None:
msg = "Unable to find Pulumi CLI executable in your path.\nPlease ensure that Pulumi is installed"
raise DataSafeHavenPulumiError(msg)
self.path = path

# Ensure Azure CLI account is correct
# This will be needed to populate env
Expand All @@ -50,69 +46,10 @@ def env(self) -> dict[str, Any]:
"AZURE_STORAGE_ACCOUNT": self.cfg.backend.storage_account_name,
"AZURE_STORAGE_KEY": str(backend_storage_account_keys[0].value),
"AZURE_KEYVAULT_AUTH_VIA_CLI": "true",
"PULUMI_BACKEND_URL": f"azblob://{self.cfg.pulumi.storage_container_name}",
}
return self.env_

def confirm(self) -> bool:
"""Prompt user to confirm the Pulumi account is correct"""
# Because the who_am_i method requires a stack and workspace, it is difficult to
# do this with a minimal dummy stack which also works with the Azure backend.
# A subprocess call is used here.
try:
result = subprocess.check_output(
[self.path, "whoami", "--verbose"],
stderr=subprocess.PIPE,
encoding="utf8",
env=self.env,
)
except subprocess.CalledProcessError as exc:
msg = f"Logging into Pulumi failed.\n{exc}\n{result}"
raise DataSafeHavenPulumiError(msg) from exc

self.logger.info("Confirming Pulumi account details")
for line in result.splitlines():
self.logger.info(line)

if typer.confirm("Is this the Pulumi account you expect?"):
self.logger.info("confirmed")
return True
else:
return False

def login(self) -> None:
"""Login to Pulumi."""
try:
result = subprocess.check_output(
[
self.path,
"login",
f"azblob://{self.cfg.pulumi.storage_container_name}",
],
stderr=subprocess.PIPE,
encoding="utf8",
env=self.env,
)
self.logger.info(result)
except subprocess.CalledProcessError as exc:
msg = f"Logging into Pulumi failed.\n{exc}."
raise DataSafeHavenPulumiError(msg) from exc

def handle_login(self) -> None:
"""Ensure the user is using the DSH Pulumi backend"""
if not self.confirm():
msg = (
"Attempting to login to Pulumi account using"
f" container [green]{self.cfg.pulumi.storage_container_name}[/]"
f" in Azure storage account [green]{self.cfg.backend.storage_account_name}[/]"
)
self.logger.info(msg)
self.login()
if not self.confirm():
self.logger.error(
"Mismatch between expected Pulumi account and configuration"
)
raise typer.Exit(1)


class StackManager:
"""Interact with infrastructure using Pulumi"""
Expand Down

0 comments on commit 302d554

Please sign in to comment.