feat: switch all timezone naive datetimes to timezone aware
This commit is contained in:
parent
41fc0a73a5
commit
e22abb383c
30 changed files with 322 additions and 226 deletions
|
@ -1,5 +1,5 @@
|
||||||
import datetime
|
from datetime import datetime, timezone
|
||||||
from typing import Optional, List
|
from typing import List, Optional
|
||||||
|
|
||||||
from app.brm.brn import BRN
|
from app.brm.brn import BRN
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
|
@ -25,8 +25,8 @@ def _get_alarm(target: BRN,
|
||||||
alarm.aspect = aspect
|
alarm.aspect = aspect
|
||||||
alarm.target = target_str
|
alarm.target = target_str
|
||||||
alarm.text = "New alarm"
|
alarm.text = "New alarm"
|
||||||
alarm.state_changed = datetime.datetime.utcnow()
|
alarm.state_changed = datetime.now(tz=timezone.utc)
|
||||||
alarm.last_updated = datetime.datetime.utcnow()
|
alarm.last_updated = datetime.now(tz=timezone.utc)
|
||||||
db.session.add(alarm)
|
db.session.add(alarm)
|
||||||
return alarm
|
return alarm
|
||||||
|
|
||||||
|
|
|
@ -1,44 +1,48 @@
|
||||||
import datetime
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
|
from datetime import datetime, timedelta, timezone
|
||||||
from traceback import TracebackException
|
from traceback import TracebackException
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
from app import app
|
from app import app
|
||||||
from app.cli import _SubparserType, BaseCliHandler
|
from app.cli import BaseCliHandler, _SubparserType
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.models.activity import Activity
|
from app.models.activity import Activity
|
||||||
from app.models.automation import Automation, AutomationState, AutomationLogs
|
from app.models.automation import Automation, AutomationLogs, AutomationState
|
||||||
from app.terraform import BaseAutomation
|
from app.terraform import BaseAutomation
|
||||||
|
from app.terraform.alarms.eotk_aws import AlarmEotkAwsAutomation
|
||||||
|
from app.terraform.alarms.proxy_azure_cdn import AlarmProxyAzureCdnAutomation
|
||||||
|
from app.terraform.alarms.proxy_cloudfront import \
|
||||||
|
AlarmProxyCloudfrontAutomation
|
||||||
|
from app.terraform.alarms.proxy_http_status import \
|
||||||
|
AlarmProxyHTTPStatusAutomation
|
||||||
|
from app.terraform.alarms.smart_aws import AlarmSmartAwsAutomation
|
||||||
from app.terraform.block.block_blocky import BlockBlockyAutomation
|
from app.terraform.block.block_blocky import BlockBlockyAutomation
|
||||||
|
from app.terraform.block.block_scriptzteam import \
|
||||||
|
BlockBridgeScriptzteamAutomation
|
||||||
from app.terraform.block.bridge_github import BlockBridgeGitHubAutomation
|
from app.terraform.block.bridge_github import BlockBridgeGitHubAutomation
|
||||||
from app.terraform.block.bridge_gitlab import BlockBridgeGitlabAutomation
|
from app.terraform.block.bridge_gitlab import BlockBridgeGitlabAutomation
|
||||||
from app.terraform.block.bridge_roskomsvoboda import BlockBridgeRoskomsvobodaAutomation
|
from app.terraform.block.bridge_roskomsvoboda import \
|
||||||
from app.terraform.block.block_scriptzteam import BlockBridgeScriptzteamAutomation
|
BlockBridgeRoskomsvobodaAutomation
|
||||||
from app.terraform.block_external import BlockExternalAutomation
|
from app.terraform.block_external import BlockExternalAutomation
|
||||||
from app.terraform.block_ooni import BlockOONIAutomation
|
from app.terraform.block_ooni import BlockOONIAutomation
|
||||||
from app.terraform.block_roskomsvoboda import BlockRoskomsvobodaAutomation
|
from app.terraform.block_roskomsvoboda import BlockRoskomsvobodaAutomation
|
||||||
from app.terraform.bridge.meta import BridgeMetaAutomation
|
|
||||||
from app.terraform.eotk.aws import EotkAWSAutomation
|
|
||||||
from app.terraform.alarms.eotk_aws import AlarmEotkAwsAutomation
|
|
||||||
from app.terraform.alarms.proxy_azure_cdn import AlarmProxyAzureCdnAutomation
|
|
||||||
from app.terraform.alarms.proxy_cloudfront import AlarmProxyCloudfrontAutomation
|
|
||||||
from app.terraform.alarms.proxy_http_status import AlarmProxyHTTPStatusAutomation
|
|
||||||
from app.terraform.alarms.smart_aws import AlarmSmartAwsAutomation
|
|
||||||
from app.terraform.bridge.aws import BridgeAWSAutomation
|
from app.terraform.bridge.aws import BridgeAWSAutomation
|
||||||
from app.terraform.bridge.gandi import BridgeGandiAutomation
|
from app.terraform.bridge.gandi import BridgeGandiAutomation
|
||||||
from app.terraform.bridge.hcloud import BridgeHcloudAutomation
|
from app.terraform.bridge.hcloud import BridgeHcloudAutomation
|
||||||
|
from app.terraform.bridge.meta import BridgeMetaAutomation
|
||||||
from app.terraform.bridge.ovh import BridgeOvhAutomation
|
from app.terraform.bridge.ovh import BridgeOvhAutomation
|
||||||
|
from app.terraform.eotk.aws import EotkAWSAutomation
|
||||||
from app.terraform.list.github import ListGithubAutomation
|
from app.terraform.list.github import ListGithubAutomation
|
||||||
from app.terraform.list.gitlab import ListGitlabAutomation
|
from app.terraform.list.gitlab import ListGitlabAutomation
|
||||||
from app.terraform.list.http_post import ListHttpPostAutomation
|
from app.terraform.list.http_post import ListHttpPostAutomation
|
||||||
from app.terraform.list.s3 import ListS3Automation
|
from app.terraform.list.s3 import ListS3Automation
|
||||||
from app.terraform.proxy.meta import ProxyMetaAutomation
|
|
||||||
from app.terraform.proxy.azure_cdn import ProxyAzureCdnAutomation
|
from app.terraform.proxy.azure_cdn import ProxyAzureCdnAutomation
|
||||||
from app.terraform.proxy.cloudfront import ProxyCloudfrontAutomation
|
from app.terraform.proxy.cloudfront import ProxyCloudfrontAutomation
|
||||||
from app.terraform.proxy.fastly import ProxyFastlyAutomation
|
from app.terraform.proxy.fastly import ProxyFastlyAutomation
|
||||||
|
from app.terraform.proxy.meta import ProxyMetaAutomation
|
||||||
from app.terraform.static.aws import StaticAWSAutomation
|
from app.terraform.static.aws import StaticAWSAutomation
|
||||||
from app.terraform.static.meta import StaticMetaAutomation
|
from app.terraform.static.meta import StaticMetaAutomation
|
||||||
|
|
||||||
|
@ -108,7 +112,7 @@ def run_job(job_cls: Type[BaseAutomation], *,
|
||||||
automation.description = job_cls.description
|
automation.description = job_cls.description
|
||||||
automation.enabled = False
|
automation.enabled = False
|
||||||
automation.next_is_full = False
|
automation.next_is_full = False
|
||||||
automation.added = datetime.datetime.utcnow()
|
automation.added = datetime.now(tz=timezone.utc)
|
||||||
automation.updated = automation.added
|
automation.updated = automation.added
|
||||||
db.session.add(automation)
|
db.session.add(automation)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -117,7 +121,7 @@ def run_job(job_cls: Type[BaseAutomation], *,
|
||||||
logging.warning("Not running an already running automation")
|
logging.warning("Not running an already running automation")
|
||||||
return
|
return
|
||||||
if not ignore_schedule and not force:
|
if not ignore_schedule and not force:
|
||||||
if automation.next_run is not None and automation.next_run > datetime.datetime.utcnow():
|
if automation.next_run is not None and automation.next_run > datetime.now(tz=timezone.utc):
|
||||||
logging.warning("Not time to run this job yet")
|
logging.warning("Not time to run this job yet")
|
||||||
return
|
return
|
||||||
if not automation.enabled and not force:
|
if not automation.enabled and not force:
|
||||||
|
@ -145,7 +149,7 @@ def run_job(job_cls: Type[BaseAutomation], *,
|
||||||
logs = "\n".join(trace.format())
|
logs = "\n".join(trace.format())
|
||||||
if job is not None and success:
|
if job is not None and success:
|
||||||
automation.state = AutomationState.IDLE
|
automation.state = AutomationState.IDLE
|
||||||
automation.next_run = datetime.datetime.utcnow() + datetime.timedelta(
|
automation.next_run = datetime.now(tz=timezone.utc) + timedelta(
|
||||||
minutes=getattr(job, "frequency", 7))
|
minutes=getattr(job, "frequency", 7))
|
||||||
if 'TERRAFORM_DIRECTORY' not in app.config and working_dir is not None:
|
if 'TERRAFORM_DIRECTORY' not in app.config and working_dir is not None:
|
||||||
# We used a temporary working directory
|
# We used a temporary working directory
|
||||||
|
@ -168,8 +172,8 @@ def run_job(job_cls: Type[BaseAutomation], *,
|
||||||
automation.next_run = None
|
automation.next_run = None
|
||||||
log = AutomationLogs()
|
log = AutomationLogs()
|
||||||
log.automation_id = automation.id
|
log.automation_id = automation.id
|
||||||
log.added = datetime.datetime.utcnow()
|
log.added = datetime.now(tz=timezone.utc)
|
||||||
log.updated = datetime.datetime.utcnow()
|
log.updated = datetime.now(tz=timezone.utc)
|
||||||
log.logs = str(logs)
|
log.logs = str(logs)
|
||||||
db.session.add(log)
|
db.session.add(log)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -182,7 +186,7 @@ def run_job(job_cls: Type[BaseAutomation], *,
|
||||||
)
|
)
|
||||||
db.session.add(activity)
|
db.session.add(activity)
|
||||||
activity.notify() # Notify before commit because the failure occurred even if we can't commit.
|
activity.notify() # Notify before commit because the failure occurred even if we can't commit.
|
||||||
automation.last_run = datetime.datetime.utcnow()
|
automation.last_run = datetime.now(tz=timezone.utc)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Dict, List, Optional, TypedDict
|
from typing import Dict, List, Optional, TypedDict
|
||||||
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
from sqlalchemy.orm import selectinload
|
from sqlalchemy.orm import selectinload
|
||||||
|
@ -8,7 +9,7 @@ from tldextract import extract
|
||||||
|
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.models.base import Group, Pool
|
from app.models.base import Group, Pool
|
||||||
from app.models.mirrors import Proxy, Origin
|
from app.models.mirrors import Origin, Proxy
|
||||||
|
|
||||||
|
|
||||||
class MirrorMappingMirror(TypedDict):
|
class MirrorMappingMirror(TypedDict):
|
||||||
|
@ -29,7 +30,7 @@ class MirrorMapping(TypedDict):
|
||||||
|
|
||||||
|
|
||||||
def mirror_mapping(_: Optional[Pool]) -> MirrorMapping:
|
def mirror_mapping(_: Optional[Pool]) -> MirrorMapping:
|
||||||
two_days_ago = datetime.utcnow() - timedelta(days=2)
|
two_days_ago = datetime.now(tz=timezone.utc) - timedelta(days=2)
|
||||||
|
|
||||||
proxies = (
|
proxies = (
|
||||||
db.session.query(Proxy)
|
db.session.query(Proxy)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import logging
|
import logging
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from typing import Union, List, Optional, Any, Dict
|
from typing import Any, Dict, List, Optional, Union
|
||||||
|
|
||||||
from sqlalchemy.orm import Mapped, mapped_column
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
|
|
||||||
|
@ -14,9 +14,9 @@ class AbstractConfiguration(db.Model): # type: ignore
|
||||||
|
|
||||||
id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
|
id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
|
||||||
description: Mapped[str]
|
description: Mapped[str]
|
||||||
added: Mapped[datetime]
|
added: Mapped[datetime] = mapped_column(db.DateTime(timezone=True))
|
||||||
updated: Mapped[datetime]
|
updated: Mapped[datetime] = mapped_column(db.DateTime(timezone=True))
|
||||||
destroyed: Mapped[Optional[datetime]]
|
destroyed: Mapped[Optional[datetime]] = mapped_column(db.DateTime(timezone=True), nullable=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
@ -24,8 +24,8 @@ class AbstractConfiguration(db.Model): # type: ignore
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def destroy(self) -> None:
|
def destroy(self) -> None:
|
||||||
self.destroyed = datetime.utcnow()
|
self.destroyed = datetime.now(tz=timezone.utc)
|
||||||
self.updated = datetime.utcnow()
|
self.updated = datetime.now(tz=timezone.utc)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def csv_header(cls) -> List[str]:
|
def csv_header(cls) -> List[str]:
|
||||||
|
@ -41,11 +41,11 @@ class AbstractConfiguration(db.Model): # type: ignore
|
||||||
|
|
||||||
class Deprecation(db.Model): # type: ignore[name-defined,misc]
|
class Deprecation(db.Model): # type: ignore[name-defined,misc]
|
||||||
id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
|
id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
|
||||||
resource_type: Mapped[str] = mapped_column(db.String(50))
|
resource_type: Mapped[str]
|
||||||
resource_id: Mapped[int] = mapped_column(db.Integer)
|
resource_id: Mapped[int]
|
||||||
deprecated_at: Mapped[datetime] = mapped_column(db.DateTime(), default=datetime.utcnow, nullable=False)
|
deprecated_at: Mapped[datetime] = mapped_column(db.DateTime(timezone=True))
|
||||||
meta: Mapped[Optional[Dict[str, Any]]] = mapped_column(db.JSON())
|
meta: Mapped[Optional[Dict[str, Any]]] = mapped_column(db.JSON())
|
||||||
reason: Mapped[str] = mapped_column(db.String(), nullable=False)
|
reason: Mapped[str]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def resource(self) -> "AbstractResource":
|
def resource(self) -> "AbstractResource":
|
||||||
|
@ -58,11 +58,11 @@ class AbstractResource(db.Model): # type: ignore
|
||||||
__abstract__ = True
|
__abstract__ = True
|
||||||
|
|
||||||
id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
|
id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
|
||||||
added: Mapped[datetime] = mapped_column(db.DateTime(), default=datetime.utcnow)
|
added: Mapped[datetime] = mapped_column(db.DateTime(timezone=True))
|
||||||
updated: Mapped[datetime] = mapped_column(db.DateTime(), default=datetime.utcnow)
|
updated: Mapped[datetime] = mapped_column(db.DateTime(timezone=True))
|
||||||
deprecated: Mapped[Optional[datetime]]
|
deprecated: Mapped[Optional[datetime]] = mapped_column(db.DateTime(timezone=True), nullable=True)
|
||||||
deprecation_reason: Mapped[Optional[str]]
|
deprecation_reason: Mapped[Optional[str]]
|
||||||
destroyed: Mapped[Optional[datetime]]
|
destroyed: Mapped[Optional[datetime]] = mapped_column(db.DateTime(timezone=True), nullable=True)
|
||||||
|
|
||||||
def __init__(self, *,
|
def __init__(self, *,
|
||||||
id: Optional[int] = None,
|
id: Optional[int] = None,
|
||||||
|
@ -73,9 +73,9 @@ class AbstractResource(db.Model): # type: ignore
|
||||||
destroyed: Optional[datetime] = None,
|
destroyed: Optional[datetime] = None,
|
||||||
**kwargs: Any) -> None:
|
**kwargs: Any) -> None:
|
||||||
if added is None:
|
if added is None:
|
||||||
added = datetime.utcnow()
|
added = datetime.now(tz=timezone.utc)
|
||||||
if updated is None:
|
if updated is None:
|
||||||
updated = datetime.utcnow()
|
updated = datetime.now(tz=timezone.utc)
|
||||||
super().__init__(id=id,
|
super().__init__(id=id,
|
||||||
added=added,
|
added=added,
|
||||||
updated=updated,
|
updated=updated,
|
||||||
|
@ -101,9 +101,9 @@ class AbstractResource(db.Model): # type: ignore
|
||||||
"""
|
"""
|
||||||
if self.deprecated is None:
|
if self.deprecated is None:
|
||||||
logging.info("Deprecating %s (reason=%s)", self.brn, reason)
|
logging.info("Deprecating %s (reason=%s)", self.brn, reason)
|
||||||
self.deprecated = datetime.utcnow()
|
self.deprecated = datetime.now(tz=timezone.utc)
|
||||||
self.deprecation_reason = reason
|
self.deprecation_reason = reason
|
||||||
self.updated = datetime.utcnow()
|
self.updated = datetime.now(tz=timezone.utc)
|
||||||
if reason not in [d.reason for d in self.deprecations]:
|
if reason not in [d.reason for d in self.deprecations]:
|
||||||
new_deprecation = Deprecation(
|
new_deprecation = Deprecation(
|
||||||
resource_type=type(self).__name__,
|
resource_type=type(self).__name__,
|
||||||
|
@ -132,8 +132,8 @@ class AbstractResource(db.Model): # type: ignore
|
||||||
"""
|
"""
|
||||||
if self.deprecated is None:
|
if self.deprecated is None:
|
||||||
self.deprecate(reason="destroyed")
|
self.deprecate(reason="destroyed")
|
||||||
self.destroyed = datetime.utcnow()
|
self.destroyed = datetime.now(tz=timezone.utc)
|
||||||
self.updated = datetime.utcnow()
|
self.updated = datetime.now(tz=timezone.utc)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def csv_header(cls) -> List[str]:
|
def csv_header(cls) -> List[str]:
|
||||||
|
|
|
@ -1,39 +1,40 @@
|
||||||
import datetime
|
from datetime import datetime, timezone
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
|
|
||||||
from app.brm.brn import BRN
|
from app.brm.brn import BRN
|
||||||
from app.models import AbstractConfiguration
|
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
|
from app.models import AbstractConfiguration
|
||||||
|
|
||||||
|
|
||||||
class Activity(db.Model): # type: ignore
|
class Activity(db.Model): # type: ignore
|
||||||
id = db.Column(db.Integer(), primary_key=True)
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
group_id = db.Column(db.Integer(), nullable=True)
|
group_id: Mapped[Optional[int]]
|
||||||
activity_type = db.Column(db.String(20), nullable=False)
|
activity_type: Mapped[str]
|
||||||
text = db.Column(db.Text(), nullable=False)
|
text: Mapped[str]
|
||||||
added = db.Column(db.DateTime(), nullable=False)
|
added: Mapped[datetime] = mapped_column(db.DateTime(timezone=True))
|
||||||
|
|
||||||
def __init__(self, *,
|
def __init__(self, *,
|
||||||
id: Optional[int] = None,
|
id: Optional[int] = None,
|
||||||
group_id: Optional[int] = None,
|
group_id: Optional[int] = None,
|
||||||
activity_type: str,
|
activity_type: str,
|
||||||
text: str,
|
text: str,
|
||||||
added: Optional[datetime.datetime] = None,
|
added: Optional[datetime] = None,
|
||||||
**kwargs: Any) -> None:
|
**kwargs: Any) -> None:
|
||||||
if not isinstance(activity_type, str) or len(activity_type) > 20 or activity_type == "":
|
if not isinstance(activity_type, str) or len(activity_type) > 20 or activity_type == "":
|
||||||
raise TypeError("expected string for activity type between 1 and 20 characters")
|
raise TypeError("expected string for activity type between 1 and 20 characters")
|
||||||
if not isinstance(text, str):
|
if not isinstance(text, str):
|
||||||
raise TypeError("expected string for text")
|
raise TypeError("expected string for text")
|
||||||
|
if added is None:
|
||||||
|
added = datetime.now(tz=timezone.utc)
|
||||||
super().__init__(id=id,
|
super().__init__(id=id,
|
||||||
group_id=group_id,
|
group_id=group_id,
|
||||||
activity_type=activity_type,
|
activity_type=activity_type,
|
||||||
text=text,
|
text=text,
|
||||||
added=added,
|
added=added,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
if self.added is None:
|
|
||||||
self.added = datetime.datetime.utcnow()
|
|
||||||
|
|
||||||
def notify(self) -> int:
|
def notify(self) -> int:
|
||||||
count = 0
|
count = 0
|
||||||
|
@ -47,8 +48,8 @@ class Activity(db.Model): # type: ignore
|
||||||
|
|
||||||
|
|
||||||
class Webhook(AbstractConfiguration):
|
class Webhook(AbstractConfiguration):
|
||||||
format = db.Column(db.String(20))
|
format: Mapped[str]
|
||||||
url = db.Column(db.String(255))
|
url: Mapped[str]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def brn(self) -> BRN:
|
def brn(self) -> BRN:
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import enum
|
import enum
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from typing import List, Any
|
from typing import Any, List
|
||||||
|
|
||||||
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
|
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.models.activity import Activity
|
from app.models.activity import Activity
|
||||||
|
@ -24,13 +26,13 @@ class AlarmState(enum.Enum):
|
||||||
|
|
||||||
|
|
||||||
class Alarm(db.Model): # type: ignore
|
class Alarm(db.Model): # type: ignore
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
|
||||||
target = db.Column(db.String(255), nullable=False)
|
target: Mapped[str]
|
||||||
aspect = db.Column(db.String(255), nullable=False)
|
aspect: Mapped[str]
|
||||||
alarm_state = db.Column(db.Enum(AlarmState), default=AlarmState.UNKNOWN, nullable=False)
|
alarm_state: Mapped[AlarmState] = mapped_column(default=AlarmState.UNKNOWN)
|
||||||
state_changed = db.Column(db.DateTime(), nullable=False)
|
state_changed: Mapped[datetime] = mapped_column(db.DateTime(timezone=True))
|
||||||
last_updated = db.Column(db.DateTime(), nullable=False)
|
last_updated: Mapped[datetime] = mapped_column(db.DateTime(timezone=True))
|
||||||
text = db.Column(db.String(255), nullable=False)
|
text: Mapped[str]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def csv_header(cls) -> List[str]:
|
def csv_header(cls) -> List[str]:
|
||||||
|
@ -40,11 +42,8 @@ class Alarm(db.Model): # type: ignore
|
||||||
return [getattr(self, x) for x in self.csv_header()]
|
return [getattr(self, x) for x in self.csv_header()]
|
||||||
|
|
||||||
def update_state(self, state: AlarmState, text: str) -> None:
|
def update_state(self, state: AlarmState, text: str) -> None:
|
||||||
if self.alarm_state is None:
|
|
||||||
self.alarm_state = AlarmState.UNKNOWN
|
|
||||||
|
|
||||||
if self.alarm_state != state or self.state_changed is None:
|
if self.alarm_state != state or self.state_changed is None:
|
||||||
self.state_changed = datetime.utcnow()
|
self.state_changed = datetime.now(tz=timezone.utc)
|
||||||
activity = Activity(activity_type="alarm_state",
|
activity = Activity(activity_type="alarm_state",
|
||||||
text=f"[{self.aspect}] {state.emoji} Alarm state changed from "
|
text=f"[{self.aspect}] {state.emoji} Alarm state changed from "
|
||||||
f"{self.alarm_state.name} to {state.name} on {self.target}: {text}.")
|
f"{self.alarm_state.name} to {state.name} on {self.target}: {text}.")
|
||||||
|
@ -56,4 +55,4 @@ class Alarm(db.Model): # type: ignore
|
||||||
db.session.add(activity)
|
db.session.add(activity)
|
||||||
self.alarm_state = state
|
self.alarm_state = state
|
||||||
self.text = text
|
self.text = text
|
||||||
self.last_updated = datetime.utcnow()
|
self.last_updated = datetime.now(tz=timezone.utc)
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import datetime
|
|
||||||
import enum
|
import enum
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
|
|
||||||
from app.brm.brn import BRN
|
from app.brm.brn import BRN
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
|
@ -13,12 +16,12 @@ class AutomationState(enum.Enum):
|
||||||
|
|
||||||
|
|
||||||
class Automation(AbstractConfiguration):
|
class Automation(AbstractConfiguration):
|
||||||
short_name = db.Column(db.String(25), nullable=False)
|
short_name: Mapped[str]
|
||||||
state = db.Column(db.Enum(AutomationState), default=AutomationState.IDLE, nullable=False)
|
state: Mapped[AutomationState] = mapped_column(default=AutomationState.IDLE)
|
||||||
enabled = db.Column(db.Boolean, nullable=False)
|
enabled: Mapped[bool]
|
||||||
last_run = db.Column(db.DateTime(), nullable=True)
|
last_run: Mapped[Optional[datetime]] = mapped_column(db.DateTime(timezone=True))
|
||||||
next_run = db.Column(db.DateTime(), nullable=True)
|
next_run: Mapped[Optional[datetime]] = mapped_column(db.DateTime(timezone=True))
|
||||||
next_is_full = db.Column(db.Boolean(), nullable=False)
|
next_is_full: Mapped[bool]
|
||||||
|
|
||||||
logs = db.relationship("AutomationLogs", back_populates="automation")
|
logs = db.relationship("AutomationLogs", back_populates="automation")
|
||||||
|
|
||||||
|
@ -34,13 +37,13 @@ class Automation(AbstractConfiguration):
|
||||||
|
|
||||||
def kick(self) -> None:
|
def kick(self) -> None:
|
||||||
self.enabled = True
|
self.enabled = True
|
||||||
self.next_run = datetime.datetime.utcnow()
|
self.next_run = datetime.now(tz=timezone.utc)
|
||||||
self.updated = datetime.datetime.utcnow()
|
self.updated = datetime.now(tz=timezone.utc)
|
||||||
|
|
||||||
|
|
||||||
class AutomationLogs(AbstractResource):
|
class AutomationLogs(AbstractResource):
|
||||||
automation_id = db.Column(db.Integer, db.ForeignKey(Automation.id), nullable=False)
|
automation_id: Mapped[int] = mapped_column(db.ForeignKey("automation.id"))
|
||||||
logs = db.Column(db.Text)
|
logs: Mapped[str]
|
||||||
|
|
||||||
automation = db.relationship("Automation", back_populates="logs")
|
automation = db.relationship("Automation", back_populates="logs")
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from typing import List, TypedDict, Optional, TYPE_CHECKING
|
from typing import TYPE_CHECKING, List, Optional, TypedDict
|
||||||
|
|
||||||
from sqlalchemy import and_
|
from sqlalchemy import and_
|
||||||
from sqlalchemy.orm import Mapped, aliased, mapped_column, relationship
|
from sqlalchemy.orm import Mapped, aliased, mapped_column, relationship
|
||||||
|
@ -22,7 +22,7 @@ class GroupDict(TypedDict):
|
||||||
|
|
||||||
|
|
||||||
class Group(AbstractConfiguration):
|
class Group(AbstractConfiguration):
|
||||||
group_name: Mapped[str] = db.Column(db.String(80), unique=True, nullable=False)
|
group_name: Mapped[str] = mapped_column(unique=True)
|
||||||
eotk: Mapped[bool]
|
eotk: Mapped[bool]
|
||||||
|
|
||||||
origins: Mapped[List["Origin"]] = relationship("Origin", back_populates="group")
|
origins: Mapped[List["Origin"]] = relationship("Origin", back_populates="group")
|
||||||
|
@ -93,19 +93,19 @@ class Pool(AbstractConfiguration):
|
||||||
|
|
||||||
|
|
||||||
class PoolGroup(db.Model): # type: ignore[name-defined,misc]
|
class PoolGroup(db.Model): # type: ignore[name-defined,misc]
|
||||||
pool_id = db.Column(db.Integer, db.ForeignKey("pool.id"), primary_key=True)
|
pool_id: Mapped[int] = mapped_column(db.ForeignKey("pool.id"), primary_key=True)
|
||||||
group_id = db.Column(db.Integer, db.ForeignKey("group.id"), primary_key=True)
|
group_id: Mapped[int] = mapped_column(db.ForeignKey("group.id"), primary_key=True)
|
||||||
|
|
||||||
|
|
||||||
class MirrorList(AbstractConfiguration):
|
class MirrorList(AbstractConfiguration):
|
||||||
pool_id = db.Column(db.Integer, db.ForeignKey("pool.id"))
|
pool_id: Mapped[int] = mapped_column(db.ForeignKey("pool.id"))
|
||||||
provider = db.Column(db.String(255), nullable=False)
|
provider: Mapped[str]
|
||||||
format = db.Column(db.String(20), nullable=False)
|
format: Mapped[str]
|
||||||
encoding = db.Column(db.String(20), nullable=False)
|
encoding: Mapped[str]
|
||||||
container = db.Column(db.String(255), nullable=False)
|
container: Mapped[str]
|
||||||
branch = db.Column(db.String(255), nullable=False)
|
branch: Mapped[str]
|
||||||
role = db.Column(db.String(255), nullable=True)
|
role: Mapped[Optional[str]]
|
||||||
filename = db.Column(db.String(255), nullable=False)
|
filename: Mapped[str]
|
||||||
|
|
||||||
pool = db.relationship("Pool", back_populates="lists")
|
pool = db.relationship("Pool", back_populates="lists")
|
||||||
|
|
||||||
|
@ -132,8 +132,8 @@ class MirrorList(AbstractConfiguration):
|
||||||
}
|
}
|
||||||
|
|
||||||
def destroy(self) -> None:
|
def destroy(self) -> None:
|
||||||
self.destroyed = datetime.utcnow()
|
self.destroyed = datetime.now(tz=timezone.utc)
|
||||||
self.updated = datetime.utcnow()
|
self.updated = datetime.now(tz=timezone.utc)
|
||||||
|
|
||||||
def url(self) -> str:
|
def url(self) -> str:
|
||||||
if self.provider == "gitlab":
|
if self.provider == "gitlab":
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import enum
|
import enum
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from typing import List
|
from typing import List, Optional
|
||||||
|
|
||||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||||
|
|
||||||
|
@ -37,12 +37,12 @@ class BridgeConf(AbstractConfiguration):
|
||||||
)
|
)
|
||||||
|
|
||||||
def destroy(self) -> None:
|
def destroy(self) -> None:
|
||||||
self.destroyed = datetime.utcnow()
|
self.destroyed = datetime.now(tz=timezone.utc)
|
||||||
self.updated = datetime.utcnow()
|
self.updated = datetime.now(tz=timezone.utc)
|
||||||
for bridge in self.bridges:
|
for bridge in self.bridges:
|
||||||
if bridge.destroyed is None:
|
if bridge.destroyed is None:
|
||||||
bridge.destroyed = datetime.utcnow()
|
bridge.destroyed = datetime.now(tz=timezone.utc)
|
||||||
bridge.updated = datetime.utcnow()
|
bridge.updated = datetime.now(tz=timezone.utc)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def csv_header(cls) -> List[str]:
|
def csv_header(cls) -> List[str]:
|
||||||
|
@ -52,13 +52,13 @@ class BridgeConf(AbstractConfiguration):
|
||||||
|
|
||||||
|
|
||||||
class Bridge(AbstractResource):
|
class Bridge(AbstractResource):
|
||||||
conf_id = db.Column(db.Integer, db.ForeignKey("bridge_conf.id"), nullable=False)
|
conf_id: Mapped[int] = mapped_column(db.ForeignKey("bridge_conf.id"))
|
||||||
cloud_account_id = db.Column(db.Integer, db.ForeignKey("cloud_account.id"))
|
cloud_account_id: Mapped[int] = mapped_column(db.ForeignKey("cloud_account.id"))
|
||||||
terraform_updated = db.Column(db.DateTime(), nullable=True)
|
terraform_updated: Mapped[Optional[datetime]] = mapped_column(db.DateTime(timezone=True), nullable=True)
|
||||||
nickname = db.Column(db.String(255), nullable=True)
|
nickname: Mapped[Optional[str]]
|
||||||
fingerprint = db.Column(db.String(255), nullable=True)
|
fingerprint: Mapped[Optional[str]]
|
||||||
hashed_fingerprint = db.Column(db.String(255), nullable=True)
|
hashed_fingerprint: Mapped[Optional[str]]
|
||||||
bridgeline = db.Column(db.String(255), nullable=True)
|
bridgeline: Mapped[Optional[str]]
|
||||||
|
|
||||||
conf = db.relationship("BridgeConf", back_populates="bridges")
|
conf = db.relationship("BridgeConf", back_populates="bridges")
|
||||||
cloud_account = db.relationship("CloudAccount", back_populates="bridges")
|
cloud_account = db.relationship("CloudAccount", back_populates="bridges")
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Optional, List, Union, Any, Dict, TypedDict, Literal
|
from typing import Any, Dict, List, Literal, Optional, TypedDict, Union
|
||||||
|
|
||||||
import tldextract
|
import tldextract
|
||||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||||
|
@ -10,10 +10,11 @@ from tldextract import extract
|
||||||
from werkzeug.datastructures import FileStorage
|
from werkzeug.datastructures import FileStorage
|
||||||
|
|
||||||
from app.brm.brn import BRN
|
from app.brm.brn import BRN
|
||||||
from app.brm.utils import thumbnail_uploaded_image, create_data_uri, normalize_color
|
from app.brm.utils import (create_data_uri, normalize_color,
|
||||||
|
thumbnail_uploaded_image)
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.models import AbstractConfiguration, AbstractResource, Deprecation
|
from app.models import AbstractConfiguration, AbstractResource, Deprecation
|
||||||
from app.models.base import Pool, Group
|
from app.models.base import Group, Pool
|
||||||
from app.models.onions import Onion
|
from app.models.onions import Onion
|
||||||
|
|
||||||
country_origin = db.Table(
|
country_origin = db.Table(
|
||||||
|
@ -93,14 +94,14 @@ class Origin(AbstractConfiguration):
|
||||||
.filter(
|
.filter(
|
||||||
Origin.id == self.id,
|
Origin.id == self.id,
|
||||||
Deprecation.resource_type == 'Proxy',
|
Deprecation.resource_type == 'Proxy',
|
||||||
Deprecation.deprecated_at >= datetime.utcnow() - timedelta(hours=168),
|
Deprecation.deprecated_at >= datetime.now(tz=timezone.utc) - timedelta(hours=168),
|
||||||
Deprecation.reason != "destroyed"
|
Deprecation.reason != "destroyed"
|
||||||
)
|
)
|
||||||
.distinct(Proxy.id)
|
.distinct(Proxy.id)
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
for deprecation in recent_deprecations:
|
for deprecation in recent_deprecations:
|
||||||
recency_factor += 1 / max((datetime.utcnow() - deprecation.deprecated_at).total_seconds() // 3600, 1)
|
recency_factor += 1 / max((datetime.now(tz=timezone.utc) - deprecation.deprecated_at).total_seconds() // 3600, 1)
|
||||||
frequency_factor += 1
|
frequency_factor += 1
|
||||||
risk_levels: Dict[str, int] = {}
|
risk_levels: Dict[str, int] = {}
|
||||||
for country in self.countries:
|
for country in self.countries:
|
||||||
|
@ -149,14 +150,14 @@ class Country(AbstractConfiguration):
|
||||||
.filter(
|
.filter(
|
||||||
Country.id == self.id,
|
Country.id == self.id,
|
||||||
Deprecation.resource_type == 'Proxy',
|
Deprecation.resource_type == 'Proxy',
|
||||||
Deprecation.deprecated_at >= datetime.utcnow() - timedelta(hours=168),
|
Deprecation.deprecated_at >= datetime.now(tz=timezone.utc) - timedelta(hours=168),
|
||||||
Deprecation.reason != "destroyed"
|
Deprecation.reason != "destroyed"
|
||||||
)
|
)
|
||||||
.distinct(Proxy.id)
|
.distinct(Proxy.id)
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
for deprecation in recent_deprecations:
|
for deprecation in recent_deprecations:
|
||||||
recency_factor += 1 / max((datetime.utcnow() - deprecation.deprecated_at).total_seconds() // 3600, 1)
|
recency_factor += 1 / max((datetime.now(tz=timezone.utc) - deprecation.deprecated_at).total_seconds() // 3600, 1)
|
||||||
frequency_factor += 1
|
frequency_factor += 1
|
||||||
return int(max(1, min(10, frequency_factor * recency_factor)))
|
return int(max(1, min(10, frequency_factor * recency_factor)))
|
||||||
|
|
||||||
|
@ -255,7 +256,7 @@ class StaticOrigin(AbstractConfiguration):
|
||||||
raise ValueError("clean_insights_backend must be a str, bool, or None")
|
raise ValueError("clean_insights_backend must be a str, bool, or None")
|
||||||
if db_session_commit:
|
if db_session_commit:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
self.updated = datetime.utcnow()
|
self.updated = datetime.now(tz=timezone.utc)
|
||||||
|
|
||||||
|
|
||||||
ResourceStatus = Union[Literal["active"], Literal["pending"], Literal["expiring"], Literal["destroyed"]]
|
ResourceStatus = Union[Literal["active"], Literal["pending"], Literal["expiring"], Literal["destroyed"]]
|
||||||
|
@ -274,7 +275,7 @@ class Proxy(AbstractResource):
|
||||||
provider: Mapped[str] = mapped_column(db.String(20), nullable=False)
|
provider: Mapped[str] = mapped_column(db.String(20), nullable=False)
|
||||||
psg: Mapped[Optional[int]] = mapped_column(db.Integer, nullable=True)
|
psg: Mapped[Optional[int]] = mapped_column(db.Integer, nullable=True)
|
||||||
slug: Mapped[Optional[str]] = mapped_column(db.String(20), nullable=True)
|
slug: Mapped[Optional[str]] = mapped_column(db.String(20), nullable=True)
|
||||||
terraform_updated: Mapped[Optional[datetime]] = mapped_column(db.DateTime(), nullable=True)
|
terraform_updated: Mapped[Optional[datetime]] = mapped_column(db.DateTime(timezone=True), nullable=True)
|
||||||
url: Mapped[Optional[str]] = mapped_column(db.String(255), nullable=True)
|
url: Mapped[Optional[str]] = mapped_column(db.String(255), nullable=True)
|
||||||
|
|
||||||
origin: Mapped[Origin] = relationship("Origin", back_populates="proxies")
|
origin: Mapped[Origin] = relationship("Origin", back_populates="proxies")
|
||||||
|
|
|
@ -3,28 +3,28 @@ import logging
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from flask import Blueprint, render_template, request, url_for, redirect
|
from flask import Blueprint, redirect, render_template, request, url_for
|
||||||
from flask.typing import ResponseReturnValue
|
from flask.typing import ResponseReturnValue
|
||||||
from markupsafe import Markup
|
from markupsafe import Markup
|
||||||
from sqlalchemy import desc, or_, func
|
from sqlalchemy import desc, func, or_
|
||||||
|
|
||||||
from app.alarms import alarms_for
|
from app.alarms import alarms_for
|
||||||
from app.models.activity import Activity
|
from app.models.activity import Activity
|
||||||
from app.models.alarms import Alarm, AlarmState
|
from app.models.alarms import Alarm, AlarmState
|
||||||
|
from app.models.base import Group
|
||||||
from app.models.bridges import Bridge
|
from app.models.bridges import Bridge
|
||||||
from app.models.mirrors import Origin, Proxy
|
from app.models.mirrors import Origin, Proxy
|
||||||
from app.models.base import Group
|
|
||||||
from app.models.onions import Eotk
|
from app.models.onions import Eotk
|
||||||
from app.portal.country import bp as country
|
|
||||||
from app.portal.cloud import bp as cloud
|
|
||||||
from app.portal.automation import bp as automation
|
from app.portal.automation import bp as automation
|
||||||
from app.portal.bridgeconf import bp as bridgeconf
|
|
||||||
from app.portal.bridge import bp as bridge
|
from app.portal.bridge import bp as bridge
|
||||||
|
from app.portal.bridgeconf import bp as bridgeconf
|
||||||
|
from app.portal.cloud import bp as cloud
|
||||||
|
from app.portal.country import bp as country
|
||||||
from app.portal.eotk import bp as eotk
|
from app.portal.eotk import bp as eotk
|
||||||
from app.portal.group import bp as group
|
from app.portal.group import bp as group
|
||||||
from app.portal.list import bp as list_
|
from app.portal.list import bp as list_
|
||||||
from app.portal.origin import bp as origin
|
|
||||||
from app.portal.onion import bp as onion
|
from app.portal.onion import bp as onion
|
||||||
|
from app.portal.origin import bp as origin
|
||||||
from app.portal.pool import bp as pool
|
from app.portal.pool import bp as pool
|
||||||
from app.portal.proxy import bp as proxy
|
from app.portal.proxy import bp as proxy
|
||||||
from app.portal.smart_proxy import bp as smart_proxy
|
from app.portal.smart_proxy import bp as smart_proxy
|
||||||
|
@ -57,7 +57,7 @@ def calculate_bridge_expiry(b: Bridge) -> str:
|
||||||
logging.warning("Bridge expiry requested by template for a bridge %s that was not expiring.", b.id)
|
logging.warning("Bridge expiry requested by template for a bridge %s that was not expiring.", b.id)
|
||||||
return "Not expiring"
|
return "Not expiring"
|
||||||
expiry = b.deprecated + timedelta(hours=b.conf.expiry_hours)
|
expiry = b.deprecated + timedelta(hours=b.conf.expiry_hours)
|
||||||
countdown = expiry - datetime.utcnow()
|
countdown = expiry - datetime.now(tz=timezone.utc)
|
||||||
if countdown.days == 0:
|
if countdown.days == 0:
|
||||||
return f"{countdown.seconds // 3600} hours"
|
return f"{countdown.seconds // 3600} hours"
|
||||||
return f"{countdown.days} days"
|
return f"{countdown.days} days"
|
||||||
|
@ -66,7 +66,7 @@ def calculate_bridge_expiry(b: Bridge) -> str:
|
||||||
@portal.app_template_filter("mirror_expiry")
|
@portal.app_template_filter("mirror_expiry")
|
||||||
def calculate_mirror_expiry(s: datetime) -> str:
|
def calculate_mirror_expiry(s: datetime) -> str:
|
||||||
expiry = s + timedelta(days=3)
|
expiry = s + timedelta(days=3)
|
||||||
countdown = expiry - datetime.utcnow()
|
countdown = expiry - datetime.now(tz=timezone.utc)
|
||||||
if countdown.days == 0:
|
if countdown.days == 0:
|
||||||
return f"{countdown.seconds // 3600} hours"
|
return f"{countdown.seconds // 3600} hours"
|
||||||
return f"{countdown.days} days"
|
return f"{countdown.days} days"
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from flask import render_template, flash, Response, Blueprint, current_app
|
from flask import Blueprint, Response, current_app, flash, render_template
|
||||||
from flask.typing import ResponseReturnValue
|
from flask.typing import ResponseReturnValue
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from sqlalchemy import exc, desc
|
from sqlalchemy import desc, exc
|
||||||
from wtforms import SubmitField, BooleanField
|
from wtforms import BooleanField, SubmitField
|
||||||
|
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.models.automation import Automation, AutomationLogs
|
from app.models.automation import Automation, AutomationLogs
|
||||||
from app.models.tfstate import TerraformState
|
from app.models.tfstate import TerraformState
|
||||||
from app.portal.util import view_lifecycle, response_404
|
from app.portal.util import response_404, view_lifecycle
|
||||||
|
|
||||||
bp = Blueprint("automation", __name__)
|
bp = Blueprint("automation", __name__)
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ def automation_edit(automation_id: int) -> ResponseReturnValue:
|
||||||
form = EditAutomationForm(enabled=automation.enabled)
|
form = EditAutomationForm(enabled=automation.enabled)
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
automation.enabled = form.enabled.data
|
automation.enabled = form.enabled.data
|
||||||
automation.updated = datetime.utcnow()
|
automation.updated = datetime.now(tz=timezone.utc)
|
||||||
try:
|
try:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash("Saved changes to bridge configuration.", "success")
|
flash("Saved changes to bridge configuration.", "success")
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from typing import Optional, List
|
from typing import List, Optional
|
||||||
|
|
||||||
from flask import render_template, url_for, flash, redirect, Response, Blueprint
|
from flask import (Blueprint, Response, flash, redirect, render_template,
|
||||||
|
url_for)
|
||||||
from flask.typing import ResponseReturnValue
|
from flask.typing import ResponseReturnValue
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from sqlalchemy import exc
|
from sqlalchemy import exc
|
||||||
from wtforms import SelectField, StringField, IntegerField, SubmitField
|
from wtforms import IntegerField, SelectField, StringField, SubmitField
|
||||||
from wtforms.validators import DataRequired, NumberRange
|
from wtforms.validators import DataRequired, NumberRange
|
||||||
|
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
|
@ -99,8 +100,8 @@ def bridgeconf_new(group_id: Optional[int] = None) -> ResponseReturnValue:
|
||||||
bridgeconf.max_number = form.max_number.data
|
bridgeconf.max_number = form.max_number.data
|
||||||
bridgeconf.expiry_hours = form.expiry_hours.data
|
bridgeconf.expiry_hours = form.expiry_hours.data
|
||||||
bridgeconf.provider_allocation = ProviderAllocation[form.provider_allocation.data]
|
bridgeconf.provider_allocation = ProviderAllocation[form.provider_allocation.data]
|
||||||
bridgeconf.created = datetime.utcnow()
|
bridgeconf.added = datetime.now(tz=timezone.utc)
|
||||||
bridgeconf.updated = datetime.utcnow()
|
bridgeconf.updated = datetime.now(tz=timezone.utc)
|
||||||
try:
|
try:
|
||||||
db.session.add(bridgeconf)
|
db.session.add(bridgeconf)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -137,7 +138,7 @@ def bridgeconf_edit(bridgeconf_id: int) -> ResponseReturnValue:
|
||||||
bridgeconf.max_number = form.max_number.data
|
bridgeconf.max_number = form.max_number.data
|
||||||
bridgeconf.expiry_hours = form.expiry_hours.data
|
bridgeconf.expiry_hours = form.expiry_hours.data
|
||||||
bridgeconf.provider_allocation = ProviderAllocation[form.provider_allocation.data]
|
bridgeconf.provider_allocation = ProviderAllocation[form.provider_allocation.data]
|
||||||
bridgeconf.updated = datetime.utcnow()
|
bridgeconf.updated = datetime.now(tz=timezone.utc)
|
||||||
try:
|
try:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash("Saved changes to bridge configuration.", "success")
|
flash("Saved changes to bridge configuration.", "success")
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
from flask import Blueprint, render_template, Response, flash
|
from flask import Blueprint, Response, flash, render_template
|
||||||
from flask.typing import ResponseReturnValue
|
from flask.typing import ResponseReturnValue
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import IntegerField, BooleanField, SubmitField
|
from wtforms import BooleanField, IntegerField, SubmitField
|
||||||
|
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.models.mirrors import Country
|
from app.models.mirrors import Country
|
||||||
|
@ -63,7 +63,7 @@ def country_edit(country_id: int) -> ResponseReturnValue:
|
||||||
country.risk_level_override = form.risk_level_override_number.data
|
country.risk_level_override = form.risk_level_override_number.data
|
||||||
else:
|
else:
|
||||||
country.risk_level_override = None
|
country.risk_level_override = None
|
||||||
country.updated = datetime.utcnow()
|
country.updated = datetime.now(tz=timezone.utc)
|
||||||
try:
|
try:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash("Saved changes to country.", "success")
|
flash("Saved changes to country.", "success")
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
from flask import render_template, url_for, flash, redirect, Response, Blueprint
|
import sqlalchemy
|
||||||
|
from flask import (Blueprint, Response, flash, redirect, render_template,
|
||||||
|
url_for)
|
||||||
from flask.typing import ResponseReturnValue
|
from flask.typing import ResponseReturnValue
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
import sqlalchemy
|
from wtforms import BooleanField, StringField, SubmitField
|
||||||
from wtforms import StringField, BooleanField, SubmitField
|
|
||||||
from wtforms.validators import DataRequired
|
from wtforms.validators import DataRequired
|
||||||
|
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
|
@ -72,7 +73,7 @@ def group_edit(group_id: int) -> ResponseReturnValue:
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
group.description = form.description.data
|
group.description = form.description.data
|
||||||
group.eotk = form.eotk.data
|
group.eotk = form.eotk.data
|
||||||
group.updated = datetime.utcnow()
|
group.updated = datetime.now(tz=timezone.utc)
|
||||||
try:
|
try:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash("Saved changes to group.", "success")
|
flash("Saved changes to group.", "success")
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import json
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from typing import Optional, Any
|
from typing import Any, Optional
|
||||||
|
|
||||||
from flask import render_template, url_for, flash, redirect, Blueprint, Response
|
from flask import (Blueprint, Response, flash, redirect, render_template,
|
||||||
|
url_for)
|
||||||
from flask.typing import ResponseReturnValue
|
from flask.typing import ResponseReturnValue
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from sqlalchemy import exc
|
from sqlalchemy import exc
|
||||||
|
@ -103,8 +104,8 @@ def list_new(group_id: Optional[int] = None) -> ResponseReturnValue:
|
||||||
list_.branch = form.branch.data
|
list_.branch = form.branch.data
|
||||||
list_.role = form.role.data
|
list_.role = form.role.data
|
||||||
list_.filename = form.filename.data
|
list_.filename = form.filename.data
|
||||||
list_.created = datetime.utcnow()
|
list_.added = datetime.now(tz=timezone.utc)
|
||||||
list_.updated = datetime.utcnow()
|
list_.updated = datetime.now(tz=timezone.utc)
|
||||||
try:
|
try:
|
||||||
db.session.add(list_)
|
db.session.add(list_)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -176,7 +177,7 @@ def list_edit(list_id: int) -> ResponseReturnValue:
|
||||||
list_.branch = form.branch.data
|
list_.branch = form.branch.data
|
||||||
list_.role = form.role.data
|
list_.role = form.role.data
|
||||||
list_.filename = form.filename.data
|
list_.filename = form.filename.data
|
||||||
list_.updated = datetime.utcnow()
|
list_.updated = datetime.now(tz=timezone.utc)
|
||||||
try:
|
try:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash("Saved changes to group.", "success")
|
flash("Saved changes to group.", "success")
|
||||||
|
|
|
@ -1,20 +1,22 @@
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from typing import Optional, List
|
from typing import List, Optional
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
from flask import flash, redirect, url_for, render_template, Response, Blueprint
|
from flask import (Blueprint, Response, flash, redirect, render_template,
|
||||||
|
url_for)
|
||||||
from flask.typing import ResponseReturnValue
|
from flask.typing import ResponseReturnValue
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from sqlalchemy import exc
|
from sqlalchemy import exc
|
||||||
from wtforms import StringField, SelectField, SubmitField, BooleanField, IntegerField
|
from wtforms import (BooleanField, IntegerField, SelectField, StringField,
|
||||||
|
SubmitField)
|
||||||
from wtforms.validators import DataRequired
|
from wtforms.validators import DataRequired
|
||||||
|
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.models.base import Group
|
from app.models.base import Group
|
||||||
from app.models.mirrors import Origin, Country
|
from app.models.mirrors import Country, Origin
|
||||||
from app.portal.util import response_404, view_lifecycle, LifecycleForm
|
from app.portal.util import LifecycleForm, response_404, view_lifecycle
|
||||||
|
|
||||||
bp = Blueprint("origin", __name__)
|
bp = Blueprint("origin", __name__)
|
||||||
|
|
||||||
|
@ -64,8 +66,8 @@ def origin_new(group_id: Optional[int] = None) -> ResponseReturnValue:
|
||||||
origin.auto_rotation = form.auto_rotate.data
|
origin.auto_rotation = form.auto_rotate.data
|
||||||
origin.smart = form.smart_proxy.data
|
origin.smart = form.smart_proxy.data
|
||||||
origin.assets = form.asset_domain.data
|
origin.assets = form.asset_domain.data
|
||||||
origin.created = datetime.utcnow()
|
origin.added = datetime.now(tz=timezone.utc)
|
||||||
origin.updated = datetime.utcnow()
|
origin.updated = datetime.now(tz=timezone.utc)
|
||||||
try:
|
try:
|
||||||
db.session.add(origin)
|
db.session.add(origin)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -106,7 +108,7 @@ def origin_edit(origin_id: int) -> ResponseReturnValue:
|
||||||
origin.risk_level_override = form.risk_level_override_number.data
|
origin.risk_level_override = form.risk_level_override_number.data
|
||||||
else:
|
else:
|
||||||
origin.risk_level_override = None
|
origin.risk_level_override = None
|
||||||
origin.updated = datetime.utcnow()
|
origin.updated = datetime.now(tz=timezone.utc)
|
||||||
try:
|
try:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash(f"Saved changes for origin {origin.domain_name}.", "success")
|
flash(f"Saved changes for origin {origin.domain_name}.", "success")
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
# mypy: ignore-errors
|
# mypy: ignore-errors
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
|
|
||||||
from flask import Blueprint, render_template
|
from flask import Blueprint, render_template
|
||||||
from flask.typing import ResponseReturnValue
|
from flask.typing import ResponseReturnValue
|
||||||
from sqlalchemy import func, and_, desc
|
from sqlalchemy import and_, desc, func
|
||||||
from sqlalchemy.orm import aliased
|
from sqlalchemy.orm import aliased
|
||||||
|
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.models import Deprecation
|
from app.models import Deprecation
|
||||||
from app.models.mirrors import Proxy, Origin, Country
|
from app.models.mirrors import Country, Origin, Proxy
|
||||||
|
|
||||||
report = Blueprint("report", __name__)
|
report = Blueprint("report", __name__)
|
||||||
|
|
||||||
|
|
||||||
def generate_subqueries():
|
def generate_subqueries():
|
||||||
DeprecationAlias = aliased(Deprecation)
|
DeprecationAlias = aliased(Deprecation)
|
||||||
now = datetime.utcnow()
|
now = datetime.now(tz=timezone.utc)
|
||||||
deprecations_24hr_subquery = (
|
deprecations_24hr_subquery = (
|
||||||
db.session.query(
|
db.session.query(
|
||||||
DeprecationAlias.resource_id,
|
DeprecationAlias.resource_id,
|
||||||
|
@ -98,7 +98,7 @@ def report_blocks() -> ResponseReturnValue:
|
||||||
Proxy.deprecated,
|
Proxy.deprecated,
|
||||||
Proxy.deprecation_reason
|
Proxy.deprecation_reason
|
||||||
).join(Origin, Origin.id == Proxy.origin_id
|
).join(Origin, Origin.id == Proxy.origin_id
|
||||||
).filter(and_(Proxy.deprecated > datetime.utcnow() - timedelta(days=1),
|
).filter(and_(Proxy.deprecated > datetime.now(tz=timezone.utc) - timedelta(days=1),
|
||||||
Proxy.deprecation_reason.like('block_%'))).all()
|
Proxy.deprecation_reason.like('block_%'))).all()
|
||||||
|
|
||||||
return render_template("report_blocks.html.j2",
|
return render_template("report_blocks.html.j2",
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from flask import render_template, flash, Response, Blueprint
|
from flask import Blueprint, Response, flash, render_template
|
||||||
from flask.typing import ResponseReturnValue
|
from flask.typing import ResponseReturnValue
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from sqlalchemy import exc
|
from sqlalchemy import exc
|
||||||
from wtforms import SubmitField, BooleanField
|
from wtforms import BooleanField, SubmitField
|
||||||
|
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.models.automation import Automation
|
from app.models.automation import Automation
|
||||||
from app.models.tfstate import TerraformState
|
from app.models.tfstate import TerraformState
|
||||||
from app.portal.util import view_lifecycle, response_404
|
from app.portal.util import response_404, view_lifecycle
|
||||||
|
|
||||||
bp = Blueprint("storage", __name__)
|
bp = Blueprint("storage", __name__)
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ def storage_edit(storage_key: str) -> ResponseReturnValue:
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
if form.force_unlock.data:
|
if form.force_unlock.data:
|
||||||
storage.lock = None
|
storage.lock = None
|
||||||
storage.updated = datetime.utcnow()
|
storage.updated = datetime.now(tz=timezone.utc)
|
||||||
try:
|
try:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash("Storage has been force unlocked.", "success")
|
flash("Storage has been force unlocked.", "success")
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from flask import Blueprint, flash, Response, render_template, redirect, url_for
|
from flask import (Blueprint, Response, flash, redirect, render_template,
|
||||||
|
url_for)
|
||||||
from flask.typing import ResponseReturnValue
|
from flask.typing import ResponseReturnValue
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from sqlalchemy import exc
|
from sqlalchemy import exc
|
||||||
from wtforms import StringField, SelectField, SubmitField
|
from wtforms import SelectField, StringField, SubmitField
|
||||||
from wtforms.validators import DataRequired
|
from wtforms.validators import DataRequired
|
||||||
|
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
|
@ -70,7 +71,7 @@ def webhook_edit(webhook_id: int) -> ResponseReturnValue:
|
||||||
webhook.description = form.description.data
|
webhook.description = form.description.data
|
||||||
webhook.format = form.description.data
|
webhook.format = form.description.data
|
||||||
webhook.url = form.description.data
|
webhook.url = form.description.data
|
||||||
webhook.updated = datetime.utcnow()
|
webhook.updated = datetime.now(tz=timezone.utc)
|
||||||
try:
|
try:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash("Saved changes to webhook.", "success")
|
flash("Saved changes to webhook.", "success")
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from datetime import datetime, timedelta
|
|
||||||
import logging
|
import logging
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from typing import Tuple, List, Callable, Optional, Any
|
from datetime import datetime, timedelta, timezone
|
||||||
|
from typing import Any, Callable, List, Optional, Tuple
|
||||||
|
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.models.activity import Activity
|
from app.models.activity import Activity
|
||||||
|
@ -25,14 +25,14 @@ class BlockBridgeAutomation(BaseAutomation):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def perform_deprecations(self, ids: List[str], bridge_select_func: Callable[[str], Optional[Bridge]]
|
def perform_deprecations(self, ids: List[str], bridge_select_func: Callable[[str], Optional[Bridge]]
|
||||||
) -> List[Tuple[str, str, str]]:
|
) -> List[Tuple[Optional[str], Any, Any]]:
|
||||||
rotated = []
|
rotated = []
|
||||||
for id_ in ids:
|
for id_ in ids:
|
||||||
bridge = bridge_select_func(id_)
|
bridge = bridge_select_func(id_)
|
||||||
if bridge is None:
|
if bridge is None:
|
||||||
continue
|
continue
|
||||||
logging.debug("Found %s blocked", bridge.hashed_fingerprint)
|
logging.debug("Found %s blocked", bridge.hashed_fingerprint)
|
||||||
if bridge.added > datetime.utcnow() - timedelta(hours=3):
|
if bridge.added > datetime.now(tz=timezone.utc) - timedelta(hours=3):
|
||||||
logging.debug("Not rotating a bridge less than 3 hours old")
|
logging.debug("Not rotating a bridge less than 3 hours old")
|
||||||
continue
|
continue
|
||||||
if bridge.deprecate(reason=self.short_name):
|
if bridge.deprecate(reason=self.short_name):
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
from collections import defaultdict
|
import fnmatch
|
||||||
from datetime import datetime, timedelta
|
|
||||||
import logging
|
import logging
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
import fnmatch
|
from collections import defaultdict
|
||||||
from typing import Tuple, List, Any, Optional, Dict
|
from datetime import datetime, timedelta, timezone
|
||||||
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.models.activity import Activity
|
from app.models.activity import Activity
|
||||||
|
@ -41,7 +41,7 @@ class BlockMirrorAutomation(BaseAutomation):
|
||||||
if not proxy.origin.auto_rotation:
|
if not proxy.origin.auto_rotation:
|
||||||
logging.debug("Proxy auto-rotation forbidden for origin")
|
logging.debug("Proxy auto-rotation forbidden for origin")
|
||||||
continue
|
continue
|
||||||
if proxy.added > datetime.utcnow() - timedelta(hours=3):
|
if proxy.added > datetime.now(tz=timezone.utc) - timedelta(hours=3):
|
||||||
logging.debug("Not rotating a proxy less than 3 hours old")
|
logging.debug("Not rotating a proxy less than 3 hours old")
|
||||||
continue
|
continue
|
||||||
if proxy.deprecate(reason=f"block_{source}"):
|
if proxy.deprecate(reason=f"block_{source}"):
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta, timezone
|
||||||
from datetime import timedelta
|
from typing import Any, Dict, Tuple
|
||||||
from typing import Dict, Tuple, Any
|
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
@ -13,8 +12,8 @@ from app.terraform import BaseAutomation
|
||||||
|
|
||||||
|
|
||||||
def check_origin(domain_name: str) -> Dict[str, Any]:
|
def check_origin(domain_name: str) -> Dict[str, Any]:
|
||||||
start_date = (datetime.utcnow() - timedelta(days=1)).strftime("%Y-%m-%dT%H%%3A%M")
|
start_date = (datetime.now(tz=timezone.utc) - timedelta(days=1)).strftime("%Y-%m-%dT%H%%3A%M")
|
||||||
end_date = datetime.utcnow().strftime("%Y-%m-%dT%H%%3A%M")
|
end_date = datetime.now(tz=timezone.utc).strftime("%Y-%m-%dT%H%%3A%M")
|
||||||
api_url = f"https://api.ooni.io/api/v1/measurements?domain={domain_name}&since={start_date}&until={end_date}"
|
api_url = f"https://api.ooni.io/api/v1/measurements?domain={domain_name}&since={start_date}&until={end_date}"
|
||||||
result: Dict[str, Dict[str, int]] = defaultdict(lambda: {"anomaly": 0, "confirmed": 0, "failure": 0, "ok": 0})
|
result: Dict[str, Dict[str, int]] = defaultdict(lambda: {"anomaly": 0, "confirmed": 0, "failure": 0, "ok": 0})
|
||||||
return _check_origin(api_url, result)
|
return _check_origin(api_url, result)
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import datetime
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from typing import Optional, Any, List, Sequence, Tuple
|
from datetime import datetime, timedelta, timezone
|
||||||
|
from typing import Any, List, Optional, Sequence, Tuple
|
||||||
|
|
||||||
from sqlalchemy import select, Row
|
from sqlalchemy import Row, select
|
||||||
|
|
||||||
from app import app
|
from app import app
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
|
@ -25,7 +25,7 @@ def active_bridges_by_provider(provider: CloudProvider) -> Sequence[BridgeResour
|
||||||
|
|
||||||
|
|
||||||
def recently_destroyed_bridges_by_provider(provider: CloudProvider) -> Sequence[BridgeResourceRow]:
|
def recently_destroyed_bridges_by_provider(provider: CloudProvider) -> Sequence[BridgeResourceRow]:
|
||||||
cutoff = datetime.datetime.utcnow() - datetime.timedelta(hours=72)
|
cutoff = datetime.now(tz=timezone.utc) - timedelta(hours=72)
|
||||||
stmt = select(Bridge, BridgeConf, CloudAccount).join_from(Bridge, BridgeConf).join_from(Bridge, CloudAccount).where(
|
stmt = select(Bridge, BridgeConf, CloudAccount).join_from(Bridge, BridgeConf).join_from(Bridge, CloudAccount).where(
|
||||||
CloudAccount.provider == provider,
|
CloudAccount.provider == provider,
|
||||||
Bridge.destroyed.is_not(None),
|
Bridge.destroyed.is_not(None),
|
||||||
|
@ -83,7 +83,7 @@ class BridgeAutomation(TerraformAutomation):
|
||||||
bridge = Bridge.query.filter(Bridge.id == output[len('bridge_hashed_fingerprint_'):]).first()
|
bridge = Bridge.query.filter(Bridge.id == output[len('bridge_hashed_fingerprint_'):]).first()
|
||||||
bridge.nickname = parts[0]
|
bridge.nickname = parts[0]
|
||||||
bridge.hashed_fingerprint = parts[1]
|
bridge.hashed_fingerprint = parts[1]
|
||||||
bridge.terraform_updated = datetime.datetime.utcnow()
|
bridge.terraform_updated = datetime.now(tz=timezone.utc)
|
||||||
if output.startswith('bridge_bridgeline_'):
|
if output.startswith('bridge_bridgeline_'):
|
||||||
parts = outputs[output]['value'].split(" ")
|
parts = outputs[output]['value'].split(" ")
|
||||||
if len(parts) < 4:
|
if len(parts) < 4:
|
||||||
|
@ -91,7 +91,7 @@ class BridgeAutomation(TerraformAutomation):
|
||||||
bridge = Bridge.query.filter(Bridge.id == output[len('bridge_bridgeline_'):]).first()
|
bridge = Bridge.query.filter(Bridge.id == output[len('bridge_bridgeline_'):]).first()
|
||||||
del parts[3]
|
del parts[3]
|
||||||
bridge.bridgeline = " ".join(parts)
|
bridge.bridgeline = " ".join(parts)
|
||||||
bridge.terraform_updated = datetime.datetime.utcnow()
|
bridge.terraform_updated = datetime.now(tz=timezone.utc)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import datetime
|
|
||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
from typing import Tuple, List
|
from datetime import datetime, timedelta, timezone
|
||||||
|
from typing import List, Tuple
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.models.bridges import BridgeConf, Bridge, ProviderAllocation
|
from app.models.bridges import Bridge, BridgeConf, ProviderAllocation
|
||||||
from app.models.cloud import CloudProvider, CloudAccount
|
from app.models.cloud import CloudAccount, CloudProvider
|
||||||
from app.terraform import BaseAutomation
|
from app.terraform import BaseAutomation
|
||||||
|
|
||||||
BRIDGE_PROVIDERS = [
|
BRIDGE_PROVIDERS = [
|
||||||
|
@ -33,8 +33,8 @@ def create_bridges_in_account(bridgeconf: BridgeConf, account: CloudAccount, cou
|
||||||
bridge.pool_id = bridgeconf.pool.id
|
bridge.pool_id = bridgeconf.pool.id
|
||||||
bridge.conf_id = bridgeconf.id
|
bridge.conf_id = bridgeconf.id
|
||||||
bridge.cloud_account = account
|
bridge.cloud_account = account
|
||||||
bridge.added = datetime.datetime.utcnow()
|
bridge.added = datetime.now(tz=timezone.utc)
|
||||||
bridge.updated = datetime.datetime.utcnow()
|
bridge.updated = datetime.now(tz=timezone.utc)
|
||||||
logging.debug("Creating bridge %s", bridge)
|
logging.debug("Creating bridge %s", bridge)
|
||||||
db.session.add(bridge)
|
db.session.add(bridge)
|
||||||
created += 1
|
created += 1
|
||||||
|
@ -129,7 +129,7 @@ class BridgeMetaAutomation(BaseAutomation):
|
||||||
for bridge in deprecated_bridges:
|
for bridge in deprecated_bridges:
|
||||||
if bridge.deprecated is None:
|
if bridge.deprecated is None:
|
||||||
continue # Possible due to SQLAlchemy lazy loading
|
continue # Possible due to SQLAlchemy lazy loading
|
||||||
cutoff = datetime.datetime.utcnow() - datetime.timedelta(hours=bridge.conf.expiry_hours)
|
cutoff = datetime.now(tz=timezone.utc) - timedelta(hours=bridge.conf.expiry_hours)
|
||||||
if bridge.deprecated < cutoff:
|
if bridge.deprecated < cutoff:
|
||||||
logging.debug("Destroying expired bridge")
|
logging.debug("Destroying expired bridge")
|
||||||
bridge.destroy()
|
bridge.destroy()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import datetime
|
|
||||||
import os
|
import os
|
||||||
|
from datetime import datetime, timezone
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from app import app
|
from app import app
|
||||||
|
@ -22,12 +22,12 @@ def update_eotk_instance(group_id: int,
|
||||||
).first()
|
).first()
|
||||||
if instance is None:
|
if instance is None:
|
||||||
instance = Eotk()
|
instance = Eotk()
|
||||||
instance.added = datetime.datetime.utcnow()
|
instance.added = datetime.now(tz=timezone.utc)
|
||||||
instance.group_id = group_id
|
instance.group_id = group_id
|
||||||
instance.provider = "aws"
|
instance.provider = "aws"
|
||||||
instance.region = region
|
instance.region = region
|
||||||
db.session.add(instance)
|
db.session.add(instance)
|
||||||
instance.updated = datetime.datetime.utcnow()
|
instance.updated = datetime.now(tz=timezone.utc)
|
||||||
instance.instance_id = instance_id
|
instance.instance_id = instance_id
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import datetime
|
from datetime import datetime, timezone
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
|
@ -122,7 +122,7 @@ class ProxyCloudfrontAutomation(ProxyAutomation):
|
||||||
proxy = Proxy.query.filter(Proxy.id == mod['address'][len('module.cloudfront_'):]).first()
|
proxy = Proxy.query.filter(Proxy.id == mod['address'][len('module.cloudfront_'):]).first()
|
||||||
proxy.url = "https://" + res['values']['domain_name']
|
proxy.url = "https://" + res['values']['domain_name']
|
||||||
proxy.slug = res['values']['id']
|
proxy.slug = res['values']['id']
|
||||||
proxy.terraform_updated = datetime.datetime.utcnow()
|
proxy.terraform_updated = datetime.now(tz=timezone.utc)
|
||||||
break
|
break
|
||||||
# EC2 instances (smart proxies)
|
# EC2 instances (smart proxies)
|
||||||
for g in state["values"]["root_module"]["child_modules"]:
|
for g in state["values"]["root_module"]["child_modules"]:
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
import datetime
|
|
||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Type
|
from datetime import datetime, timedelta, timezone
|
||||||
|
from typing import Any, Dict, List, Optional
|
||||||
from typing import OrderedDict as OrderedDictT
|
from typing import OrderedDict as OrderedDictT
|
||||||
|
from typing import Tuple, Type
|
||||||
|
|
||||||
from tldextract import tldextract
|
from tldextract import tldextract
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.models.base import Pool
|
from app.models.base import Pool
|
||||||
from app.models.mirrors import Proxy, Origin
|
from app.models.mirrors import Origin, Proxy
|
||||||
from app.terraform import BaseAutomation
|
from app.terraform import BaseAutomation
|
||||||
from app.terraform.proxy import ProxyAutomation
|
from app.terraform.proxy import ProxyAutomation
|
||||||
from app.terraform.proxy.azure_cdn import ProxyAzureCdnAutomation
|
from app.terraform.proxy.azure_cdn import ProxyAzureCdnAutomation
|
||||||
|
@ -155,7 +156,7 @@ def auto_deprecate_proxies() -> None:
|
||||||
.all())
|
.all())
|
||||||
logging.debug("Max age: %s", max_age_proxies)
|
logging.debug("Max age: %s", max_age_proxies)
|
||||||
for proxy in max_age_proxies:
|
for proxy in max_age_proxies:
|
||||||
max_age_cutoff = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(
|
max_age_cutoff = datetime.now(timezone.utc) - timedelta(
|
||||||
days=1, seconds=86400 * random.random()) # nosec: B311
|
days=1, seconds=86400 * random.random()) # nosec: B311
|
||||||
if proxy.added < max_age_cutoff:
|
if proxy.added < max_age_cutoff:
|
||||||
proxy.deprecate(reason="max_age_reached")
|
proxy.deprecate(reason="max_age_reached")
|
||||||
|
@ -168,7 +169,7 @@ def destroy_expired_proxies() -> None:
|
||||||
This function finds all proxies that are not already destroyed and have been deprecated for more than 4 days.
|
This function finds all proxies that are not already destroyed and have been deprecated for more than 4 days.
|
||||||
It then destroys these proxies.
|
It then destroys these proxies.
|
||||||
"""
|
"""
|
||||||
expiry_cutoff = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=4)
|
expiry_cutoff = datetime.now(timezone.utc) - timedelta(days=4)
|
||||||
proxies = Proxy.query.filter(
|
proxies = Proxy.query.filter(
|
||||||
Proxy.destroyed.is_(None),
|
Proxy.destroyed.is_(None),
|
||||||
Proxy.deprecated < expiry_cutoff
|
Proxy.deprecated < expiry_cutoff
|
||||||
|
@ -200,7 +201,7 @@ def promote_hot_spare_proxy(pool_id: int, origin: Origin) -> bool:
|
||||||
if not proxy:
|
if not proxy:
|
||||||
return False
|
return False
|
||||||
proxy.pool_id = pool_id
|
proxy.pool_id = pool_id
|
||||||
proxy.added = datetime.datetime.utcnow()
|
proxy.added = datetime.now(tz=timezone.utc)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -281,8 +282,8 @@ class ProxyMetaAutomation(BaseAutomation):
|
||||||
# The random usage below is good enough for its purpose: to create a slug that
|
# The random usage below is good enough for its purpose: to create a slug that
|
||||||
# hasn't been used recently.
|
# hasn't been used recently.
|
||||||
proxy.slug = random_slug(origin.domain_name)
|
proxy.slug = random_slug(origin.domain_name)
|
||||||
proxy.added = datetime.datetime.utcnow()
|
proxy.added = datetime.now(tz=timezone.utc)
|
||||||
proxy.updated = datetime.datetime.utcnow()
|
proxy.updated = datetime.now(tz=timezone.utc)
|
||||||
logging.debug("Creating proxy %s", proxy)
|
logging.debug("Creating proxy %s", proxy)
|
||||||
db.session.add(proxy)
|
db.session.add(proxy)
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from typing import List, Any
|
from typing import Any, List
|
||||||
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.models.base import Group
|
from app.models.base import Group
|
||||||
from app.models.cloud import CloudProvider, CloudAccount
|
from app.models.cloud import CloudAccount, CloudProvider
|
||||||
from app.models.mirrors import StaticOrigin
|
from app.models.mirrors import StaticOrigin
|
||||||
from app.terraform.terraform import TerraformAutomation
|
from app.terraform.terraform import TerraformAutomation
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ def import_state(state: Any) -> None:
|
||||||
static.origin_domain_name = res['values']['domain_name']
|
static.origin_domain_name = res['values']['domain_name']
|
||||||
logging.debug("and found static origin: %s to update with domain name: %s", static.id,
|
logging.debug("and found static origin: %s to update with domain name: %s", static.id,
|
||||||
static.origin_domain_name)
|
static.origin_domain_name)
|
||||||
static.terraform_updated = datetime.utcnow()
|
static.terraform_updated = datetime.now(tz=timezone.utc)
|
||||||
break
|
break
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
"""enforce more not null constraints
|
||||||
|
|
||||||
|
Revision ID: 54b31e87fe33
|
||||||
|
Revises: c14f25f364c5
|
||||||
|
Create Date: 2024-12-06 16:06:59.182836
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '54b31e87fe33'
|
||||||
|
down_revision = 'c14f25f364c5'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
with op.batch_alter_table('activity', schema=None) as batch_op:
|
||||||
|
batch_op.alter_column('text',
|
||||||
|
existing_type=sa.TEXT(),
|
||||||
|
type_=sa.String(),
|
||||||
|
existing_nullable=False)
|
||||||
|
|
||||||
|
with op.batch_alter_table('automation_logs', schema=None) as batch_op:
|
||||||
|
batch_op.alter_column('logs',
|
||||||
|
existing_type=sa.TEXT(),
|
||||||
|
type_=sa.String(),
|
||||||
|
nullable=False)
|
||||||
|
|
||||||
|
with op.batch_alter_table('bridge', schema=None) as batch_op:
|
||||||
|
batch_op.alter_column('cloud_account_id',
|
||||||
|
existing_type=sa.INTEGER(),
|
||||||
|
nullable=False)
|
||||||
|
|
||||||
|
with op.batch_alter_table('mirror_list', schema=None) as batch_op:
|
||||||
|
batch_op.alter_column('pool_id',
|
||||||
|
existing_type=sa.INTEGER(),
|
||||||
|
nullable=False)
|
||||||
|
|
||||||
|
with op.batch_alter_table('webhook', schema=None) as batch_op:
|
||||||
|
batch_op.alter_column('format',
|
||||||
|
existing_type=sa.VARCHAR(length=20),
|
||||||
|
nullable=False)
|
||||||
|
batch_op.alter_column('url',
|
||||||
|
existing_type=sa.VARCHAR(length=255),
|
||||||
|
nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
with op.batch_alter_table('webhook', schema=None) as batch_op:
|
||||||
|
batch_op.alter_column('url',
|
||||||
|
existing_type=sa.VARCHAR(length=255),
|
||||||
|
nullable=True)
|
||||||
|
batch_op.alter_column('format',
|
||||||
|
existing_type=sa.VARCHAR(length=20),
|
||||||
|
nullable=True)
|
||||||
|
|
||||||
|
with op.batch_alter_table('mirror_list', schema=None) as batch_op:
|
||||||
|
batch_op.alter_column('pool_id',
|
||||||
|
existing_type=sa.INTEGER(),
|
||||||
|
nullable=True)
|
||||||
|
|
||||||
|
with op.batch_alter_table('bridge', schema=None) as batch_op:
|
||||||
|
batch_op.alter_column('cloud_account_id',
|
||||||
|
existing_type=sa.INTEGER(),
|
||||||
|
nullable=True)
|
||||||
|
|
||||||
|
with op.batch_alter_table('automation_logs', schema=None) as batch_op:
|
||||||
|
batch_op.alter_column('logs',
|
||||||
|
existing_type=sa.String(),
|
||||||
|
type_=sa.TEXT(),
|
||||||
|
nullable=True)
|
||||||
|
|
||||||
|
with op.batch_alter_table('activity', schema=None) as batch_op:
|
||||||
|
batch_op.alter_column('text',
|
||||||
|
existing_type=sa.String(),
|
||||||
|
type_=sa.TEXT(),
|
||||||
|
existing_nullable=False)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue