import logging import os from datetime import datetime from typing import List, Any from flask import current_app from app.extensions import db from app.models.base import Group from app.models.cloud import CloudProvider, CloudAccount from app.models.mirrors import StaticOrigin from app.terraform.terraform import TerraformAutomation def import_state(state: Any) -> None: if not isinstance(state, dict): raise RuntimeError("The Terraform state object returned was not a dict.") if "values" not in state or "child_modules" not in state['values']['root_module']: # There are no CloudFront origins deployed to import state for return # CloudFront distributions (origins) for mod in state['values']['root_module']['child_modules']: if mod['address'].startswith('module.static_'): static_id = mod['address'][len('module.static_'):] logging.debug("Found static module in state: %s", static_id) for res in mod['resources']: if res['address'].endswith('aws_cloudfront_distribution.this'): logging.debug("and found related cloudfront distribution") static = StaticOrigin.query.filter(StaticOrigin.id == static_id).first() static.origin_domain_name = res['values']['domain_name'] logging.debug("and found static origin: %s to update with domain name: %s", static.id, static.origin_domain_name) static.terraform_updated = datetime.utcnow() break db.session.commit() class StaticAWSAutomation(TerraformAutomation): short_name = "static_aws" description = "Deploy static origins to AWS" provider = CloudProvider.AWS cloud_name = "aws" template_parameters: List[str] = [] template = """ terraform { {{ backend_config }} required_providers { aws = { version = "~> 4.67.0" } gitlab = { source = "gitlabhq/gitlab" version = "~> 15.11.0" } } } {% for group in groups %} module "label_{{ group.id }}" { source = "cloudposse/label/null" version = "0.25.0" namespace = "{{ global_namespace }}" tenant = "{{ group.group_name }}" label_order = ["namespace", "tenant", "name", "attributes"] } {% endfor %} {% for account in source_cloud_accounts -%} provider "gitlab" { token = "{{ account.credentials['gitlab_token'] }}" alias = "account_{{ account.id }}" } {% endfor -%} {% for account in storage_cloud_accounts -%} provider "aws" { access_key = "{{ account.credentials['aws_access_key'] }}" secret_key = "{{ account.credentials['aws_secret_key'] }}" region = "{{ account.credentials['aws_region'] }}" alias = "account_{{ account.id }}" } provider "aws" { access_key = "{{ account.credentials['aws_access_key'] }}" secret_key = "{{ account.credentials['aws_secret_key'] }}" region = "us-east-1" alias = "account_{{ account.id }}_us_east_1" } {% for static in account.statics | selectattr("destroyed", "none") %} module "static_{{ static.id }}" { providers = { aws = aws.account_{{ account.id }} aws.us_east_1 = aws.account_{{ account.id }}_us_east_1 gitlab = gitlab.account_{{ static.source_cloud_account_id }} } source = "{{ terraform_modules_path }}/terraform-aws-bc-static-origin" name = "static" context = module.label_{{ static.group.id }}.context {% if static.keanu_convene_path -%} keanu_convene_path = "{{ static.keanu_convene_path }}" {%- endif %} {% if static.keanu_convene_config -%} keanu_convene_config = "{{ static.keanu_convene_config | replace('"', '\\\\"') }}" {%- endif %} {% if static.matrix_homeserver -%} matrix_homeserver = "{{ static.matrix_homeserver }}" {%- endif %} gitlab_project = "{{ static.source_project }}" attributes = ["{{ static.id }}"] } {% endfor -%} {%- endfor %} """ def tf_generate(self) -> None: groups = Group.query.all() storage_cloud_accounts = CloudAccount.query.filter( CloudAccount.provider == CloudProvider.AWS ).all() source_cloud_accounts = CloudAccount.query.filter( CloudAccount.provider == CloudProvider.GITLAB ).all() self.tf_write( self.template, groups=groups, storage_cloud_accounts=storage_cloud_accounts, source_cloud_accounts=source_cloud_accounts, global_namespace=current_app.config['GLOBAL_NAMESPACE'], bypass_token=current_app.config['BYPASS_TOKEN'], terraform_modules_path=os.path.join(*list(os.path.split(current_app.root_path))[:-1], 'terraform-modules'), backend_config=f"""backend "http" {{ lock_address = "{current_app.config['TFSTATE_BACKEND']}/{self.short_name}" unlock_address = "{current_app.config['TFSTATE_BACKEND']}/{self.short_name}" address = "{current_app.config['TFSTATE_BACKEND']}/{self.short_name}" }}""", **{k: current_app.config[k.upper()] for k in self.template_parameters}) def tf_posthook(self, *, prehook_result: Any = None) -> None: import_state(self.tf_show())