CloudBender/cloudbender/pulumi.py

161 lines
5.0 KiB
Python
Raw Normal View History

2021-09-20 14:19:14 +00:00
import sys
import os
import re
import shutil
import importlib
2021-09-23 17:27:06 +00:00
import pkg_resources
2021-09-20 14:19:14 +00:00
import pulumi
import logging
2022-02-22 10:04:29 +00:00
2021-09-20 14:19:14 +00:00
logger = logging.getLogger(__name__)
def pulumi_init(stack):
# Fail early if pulumi binaries are not available
2022-02-22 10:04:29 +00:00
if not shutil.which("pulumi"):
raise FileNotFoundError(
"Cannot find pulumi binary, see https://www.pulumi.com/docs/get-started/install/"
)
2021-09-20 14:19:14 +00:00
# add all artifact_paths/pulumi to the search path for easier imports in the pulumi code
2022-02-22 10:04:29 +00:00
for artifacts_path in stack.ctx["artifact_paths"]:
_path = "{}/pulumi".format(artifacts_path.resolve())
2021-09-20 14:19:14 +00:00
sys.path.append(_path)
# Try local implementation first, similar to Jinja2 mode
_found = False
try:
2022-02-22 10:04:29 +00:00
_stack = importlib.import_module(
"config.{}.{}".format(stack.rel_path, stack.template).replace("/", ".")
)
2021-09-20 14:19:14 +00:00
_found = True
except ImportError:
2022-02-22 10:04:29 +00:00
for artifacts_path in stack.ctx["artifact_paths"]:
2021-09-20 14:19:14 +00:00
try:
2022-02-22 10:04:29 +00:00
spec = importlib.util.spec_from_file_location(
"_stack",
"{}/pulumi/{}.py".format(artifacts_path.resolve(), stack.template),
)
2021-09-20 14:19:14 +00:00
_stack = importlib.util.module_from_spec(spec)
spec.loader.exec_module(_stack)
_found = True
except FileNotFoundError:
pass
if not _found:
2022-02-22 10:04:29 +00:00
raise FileNotFoundError(
"Cannot find Pulumi implementation for {}".format(stack.stackname)
)
2021-09-20 14:19:14 +00:00
2022-02-22 10:04:29 +00:00
project_name = stack.parameters["Conglomerate"]
2021-09-20 14:19:14 +00:00
# Remove stacknameprefix if equals Conglomerate as Pulumi implicitly prefixes project_name
2022-02-22 10:04:29 +00:00
pulumi_stackname = re.sub(r"^" + project_name + "-?", "", stack.stackname)
2021-10-04 15:51:16 +00:00
try:
2022-02-22 10:04:29 +00:00
pulumi_backend = "{}/{}/{}".format(
stack.pulumi["backend"], project_name, stack.region
)
2021-10-04 15:51:16 +00:00
except KeyError:
2022-02-22 10:04:29 +00:00
raise KeyError("Missing pulumi.backend setting !")
2021-09-20 14:19:14 +00:00
2022-02-22 10:04:29 +00:00
account_id = stack.connection_manager.call(
"sts", "get_caller_identity", profile=stack.profile, region=stack.region
)["Account"]
2021-09-20 14:19:14 +00:00
# Ugly hack as Pulumi currently doesnt support MFA_TOKENs during role assumptions
# Do NOT set them via 'aws:secretKey' as they end up in the stack.json in plain text !!!
2022-02-22 10:04:29 +00:00
if (
stack.connection_manager._sessions[(stack.profile, stack.region)]
.get_credentials()
.token
):
os.environ["AWS_SESSION_TOKEN"] = (
stack.connection_manager._sessions[(stack.profile, stack.region)]
.get_credentials()
.token
)
os.environ["AWS_ACCESS_KEY_ID"] = (
stack.connection_manager._sessions[(stack.profile, stack.region)]
.get_credentials()
.access_key
)
os.environ["AWS_SECRET_ACCESS_KEY"] = (
stack.connection_manager._sessions[(stack.profile, stack.region)]
.get_credentials()
.secret_key
)
os.environ["AWS_DEFAULT_REGION"] = stack.region
2021-09-20 14:19:14 +00:00
# Secrets provider
try:
2022-02-22 10:04:29 +00:00
secrets_provider = stack.pulumi["secretsProvider"]
if (
secrets_provider == "passphrase"
and "PULUMI_CONFIG_PASSPHRASE" not in os.environ
):
raise ValueError("Missing PULUMI_CONFIG_PASSPHRASE environment variable!")
2021-09-20 14:19:14 +00:00
except KeyError:
2022-02-22 10:04:29 +00:00
logger.warning("Missing pulumi.secretsProvider setting, secrets disabled !")
2021-10-04 15:51:16 +00:00
secrets_provider = None
2021-09-20 14:19:14 +00:00
2021-09-23 17:27:06 +00:00
# Set tag for stack file name and version
_tags = stack.tags
try:
_version = _stack.VERSION
except AttributeError:
2022-02-22 10:04:29 +00:00
_version = "undefined"
2021-09-23 17:27:06 +00:00
2022-02-22 10:04:29 +00:00
_tags["zero-downtime.net/cloudbender"] = "{}:{}".format(
os.path.basename(_stack.__file__), _version
)
2021-09-23 17:27:06 +00:00
2021-09-20 14:19:14 +00:00
_config = {
"aws:region": stack.region,
"aws:profile": stack.profile,
2021-09-23 17:27:06 +00:00
"aws:defaultTags": {"tags": _tags},
2021-09-20 14:19:14 +00:00
"zdt:region": stack.region,
2021-09-23 17:27:06 +00:00
"zdt:awsAccountId": account_id,
2021-09-20 14:19:14 +00:00
}
# inject all parameters as config in the <Conglomerate> namespace
for p in stack.parameters:
2022-02-22 10:04:29 +00:00
_config["{}:{}".format(stack.parameters["Conglomerate"], p)] = stack.parameters[
p
]
2021-09-20 14:19:14 +00:00
stack_settings = pulumi.automation.StackSettings(
config=_config,
secrets_provider=secrets_provider,
2022-02-22 10:04:29 +00:00
encryption_salt=stack.pulumi.get("encryptionsalt", None),
encrypted_key=stack.pulumi.get("encryptedkey", None),
2021-09-20 14:19:14 +00:00
)
project_settings = pulumi.automation.ProjectSettings(
2022-02-22 10:04:29 +00:00
name=project_name, runtime="python", backend={"url": pulumi_backend}
)
2021-09-20 14:19:14 +00:00
ws_opts = pulumi.automation.LocalWorkspaceOptions(
work_dir=stack.work_dir,
project_settings=project_settings,
stack_settings={pulumi_stackname: stack_settings},
2022-02-22 10:04:29 +00:00
secrets_provider=secrets_provider,
)
2021-09-20 14:19:14 +00:00
2022-02-22 10:04:29 +00:00
stack = pulumi.automation.create_or_select_stack(
stack_name=pulumi_stackname,
project_name=project_name,
program=_stack.pulumi_program,
opts=ws_opts,
)
stack.workspace.install_plugin(
"aws", pkg_resources.get_distribution("pulumi_aws").version
)
2021-09-20 14:19:14 +00:00
return stack