2022-03-10 14:26:22 +00:00
|
|
|
import datetime
|
2022-08-30 10:05:12 +01:00
|
|
|
import os
|
2023-01-26 15:42:25 +00:00
|
|
|
import sys
|
2023-02-26 12:52:08 +00:00
|
|
|
from typing import Optional, Any, List, Tuple
|
|
|
|
|
|
|
|
from sqlalchemy import select
|
2022-03-10 14:26:22 +00:00
|
|
|
|
|
|
|
from app import app
|
|
|
|
from app.extensions import db
|
2023-02-26 12:52:08 +00:00
|
|
|
from app.models.bridges import Bridge, BridgeConf
|
|
|
|
from app.models.cloud import CloudAccount, CloudProvider
|
2022-05-08 17:20:04 +01:00
|
|
|
from app.terraform.terraform import TerraformAutomation
|
2022-03-10 14:26:22 +00:00
|
|
|
|
2023-02-26 12:52:08 +00:00
|
|
|
BridgeResourceRow = List[Tuple[Bridge, BridgeConf, CloudAccount]]
|
|
|
|
|
|
|
|
|
|
|
|
def active_bridges_by_provider(provider: CloudProvider) -> List[BridgeResourceRow]:
|
|
|
|
stmt = select(Bridge, BridgeConf, CloudAccount).join_from(Bridge, BridgeConf).join_from(Bridge, CloudAccount).where(
|
|
|
|
CloudAccount.provider == provider,
|
|
|
|
Bridge.destroyed.is_(None),
|
|
|
|
)
|
|
|
|
bridges: List[BridgeResourceRow] = db.session.execute(stmt).all()
|
|
|
|
return bridges
|
|
|
|
|
|
|
|
|
|
|
|
def recently_destroyed_bridges_by_provider(provider: CloudProvider) -> List[BridgeResourceRow]:
|
|
|
|
cutoff = datetime.datetime.utcnow() - datetime.timedelta(hours=72)
|
|
|
|
stmt = select(Bridge, BridgeConf, CloudAccount).join_from(Bridge, BridgeConf).join_from(Bridge, CloudAccount).where(
|
|
|
|
CloudAccount.provider == provider,
|
|
|
|
Bridge.destroyed.is_not(None),
|
|
|
|
Bridge.destroyed >= cutoff,
|
|
|
|
)
|
|
|
|
bridges: List[BridgeResourceRow] = db.session.execute(stmt).all()
|
|
|
|
return bridges
|
|
|
|
|
2022-03-10 14:26:22 +00:00
|
|
|
|
2022-05-08 17:20:04 +01:00
|
|
|
class BridgeAutomation(TerraformAutomation):
|
2022-05-16 11:44:03 +01:00
|
|
|
template: str
|
|
|
|
"""
|
|
|
|
Terraform configuration template using Jinja 2.
|
|
|
|
"""
|
|
|
|
|
|
|
|
template_parameters: List[str]
|
|
|
|
"""
|
|
|
|
List of parameters to be read from the application configuration for use
|
|
|
|
in the templating of the Terraform configuration.
|
|
|
|
"""
|
|
|
|
|
2023-01-26 15:42:25 +00:00
|
|
|
max_bridges = sys.maxsize
|
2022-03-10 14:26:22 +00:00
|
|
|
|
2023-01-26 15:42:25 +00:00
|
|
|
# TODO: Only enable providers that have details configured
|
|
|
|
enabled = True
|
2022-03-10 14:26:22 +00:00
|
|
|
|
2022-06-23 13:42:45 +01:00
|
|
|
def tf_prehook(self) -> Optional[Any]: # pylint: disable=useless-return
|
2022-05-16 11:44:03 +01:00
|
|
|
return None
|
2022-05-08 17:20:04 +01:00
|
|
|
|
2022-05-16 11:44:03 +01:00
|
|
|
def tf_generate(self) -> None:
|
2022-05-08 17:20:04 +01:00
|
|
|
self.tf_write(
|
2022-03-10 14:26:22 +00:00
|
|
|
self.template,
|
2023-02-26 12:52:08 +00:00
|
|
|
active_resources=active_bridges_by_provider(self.provider),
|
|
|
|
destroyed_resources=recently_destroyed_bridges_by_provider(self.provider),
|
2022-03-10 14:26:22 +00:00
|
|
|
global_namespace=app.config['GLOBAL_NAMESPACE'],
|
2022-08-30 10:05:12 +01:00
|
|
|
terraform_modules_path=os.path.join(*list(os.path.split(app.root_path))[:-1], 'terraform-modules'),
|
|
|
|
backend_config=f"""backend "http" {{
|
|
|
|
lock_address = "{app.config['TFSTATE_BACKEND']}/{self.short_name}"
|
|
|
|
unlock_address = "{app.config['TFSTATE_BACKEND']}/{self.short_name}"
|
|
|
|
address = "{app.config['TFSTATE_BACKEND']}/{self.short_name}"
|
|
|
|
}}""",
|
2022-03-10 14:26:22 +00:00
|
|
|
**{
|
|
|
|
k: app.config[k.upper()]
|
|
|
|
for k in self.template_parameters
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2022-05-08 17:20:04 +01:00
|
|
|
def tf_posthook(self, *, prehook_result: Any = None) -> None:
|
|
|
|
outputs = self.tf_output()
|
2022-03-10 14:26:22 +00:00
|
|
|
for output in outputs:
|
|
|
|
if output.startswith('bridge_hashed_fingerprint_'):
|
|
|
|
parts = outputs[output]['value'].split(" ")
|
|
|
|
if len(parts) < 2:
|
|
|
|
continue
|
|
|
|
bridge = Bridge.query.filter(Bridge.id == output[len('bridge_hashed_fingerprint_'):]).first()
|
|
|
|
bridge.nickname = parts[0]
|
|
|
|
bridge.hashed_fingerprint = parts[1]
|
|
|
|
bridge.terraform_updated = datetime.datetime.utcnow()
|
|
|
|
if output.startswith('bridge_bridgeline_'):
|
|
|
|
parts = outputs[output]['value'].split(" ")
|
|
|
|
if len(parts) < 4:
|
|
|
|
continue
|
|
|
|
bridge = Bridge.query.filter(Bridge.id == output[len('bridge_bridgeline_'):]).first()
|
2022-06-17 14:02:10 +01:00
|
|
|
del parts[3]
|
2022-03-10 14:26:22 +00:00
|
|
|
bridge.bridgeline = " ".join(parts)
|
|
|
|
bridge.terraform_updated = datetime.datetime.utcnow()
|
|
|
|
db.session.commit()
|
2023-01-26 15:42:25 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def active_bridges_count(self) -> int:
|
|
|
|
active_bridges = Bridge.query.filter(
|
|
|
|
Bridge.provider == self.provider,
|
|
|
|
Bridge.destroyed.is_(None),
|
|
|
|
).all()
|
|
|
|
return len(active_bridges)
|