Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TP2000-1463 Support SQLite dump on DBT Platform #1281

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
f28f713
Switch to python-dotenv.
paulpepper-trade Aug 19, 2024
e6464f7
Support SQLite migrations under /tmp
paulpepper-trade Aug 20, 2024
fd90d49
Black formatting.
paulpepper-trade Aug 20, 2024
4315abb
Merge branch 'master' into TP2000-1463--sqlite-dump-incompatible-with…
paulpepper-trade Aug 21, 2024
8359f01
Unit test SQLiteMigrator.
paulpepper-trade Aug 21, 2024
b0f1b13
Corrected param name.
paulpepper-trade Aug 21, 2024
2bed3a0
Corrected comment typo.
paulpepper-trade Aug 21, 2024
5082047
Merge branch 'master' into TP2000-1463--sqlite-dump-incompatible-with…
paulpepper-trade Aug 22, 2024
ce7a212
More logging and more path control.
paulpepper-trade Aug 23, 2024
cd93d9d
Merge branch 'master' into TP2000-1463--sqlite-dump-incompatible-with…
paulpepper-trade Aug 29, 2024
119c1fe
Docstring tweak
paulpepper-trade Aug 29, 2024
4b6e2f7
Merge branch 'master' into TP2000-1463--sqlite-dump-incompatible-with…
paulpepper-trade Sep 3, 2024
d0ada40
Add SQLite validation capability.
paulpepper-trade Sep 9, 2024
4f2b360
Add --env-info flag for platform diagnostics.
paulpepper-trade Sep 9, 2024
1bccf2c
Show env diagnostics during sqlite dump.
paulpepper-trade Sep 9, 2024
d46889b
Pull out path var.
paulpepper-trade Sep 9, 2024
b1c6bd4
Get current user name with env info.
paulpepper-trade Sep 10, 2024
d6b5539
Ensure write perms on migraton directories.
paulpepper-trade Sep 10, 2024
fd7ec35
Allow logging migrations output
paulpepper-trade Sep 10, 2024
b4b01c4
Add comment
paulpepper-trade Sep 10, 2024
d5f1e4e
Cater for SQLite DB in config
paulpepper-trade Sep 10, 2024
7d4ace1
Correctly set DATABASE_CREDENTIALS as a string
paulpepper-trade Sep 10, 2024
178b751
Remove SQLITE_LOG_MIGRATIONS debugging
paulpepper-trade Sep 11, 2024
809e34e
Remove unnecessary logging
paulpepper-trade Sep 11, 2024
004272a
Replace APP_UPDATED_TIME with UPTIME for DBT Platoform compatibility.
paulpepper-trade Sep 16, 2024
0f4af90
Merge branch 'master' into TP2000-1463--sqlite-dump-incompatible-with…
paulpepper-trade Sep 16, 2024
c4c3192
Merge branch 'master' into TP2000-1463--sqlite-dump-incompatible-with…
paulpepper-trade Sep 17, 2024
9d37d7c
Typo fix
paulpepper-trade Sep 17, 2024
de3a6ff
Merge branch 'master' into TP2000-1463--sqlite-dump-incompatible-with…
paulpepper-trade Sep 17, 2024
47199a9
Remove unused function.
paulpepper-trade Sep 17, 2024
98b3e41
Add missing PaaS setting.
paulpepper-trade Sep 17, 2024
397c700
Added is_cloud_foundry() util function.
paulpepper-trade Sep 17, 2024
0b7c109
Factored out function to get uptime.
paulpepper-trade Sep 17, 2024
9842393
Merge branch 'master' into TP2000-1463--sqlite-dump-incompatible-with…
paulpepper-trade Sep 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions common/jinja2/common/app_info.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@
{"text": "Environment variable"},
],
[
{"text": "APP_UPDATED_TIME"},
{"text": APP_UPDATED_TIME},
{"text": "Estimated application deploy time"},
{"text": "UPTIME"},
{"text": UPTIME},
{"text": "Time this instance has been in service"},
],
[
{"text": "LAST_TRANSACTION_TIME"},
Expand Down
59 changes: 59 additions & 0 deletions common/tests/test_util.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import os
from unittest import mock

Expand All @@ -14,6 +15,64 @@
pytestmark = pytest.mark.django_db


@pytest.mark.parametrize(
"environment_key, expected_result",
(
(
{
"engine": "engine",
"username": "username",
"password": "password",
"host": "host",
"port": 1234,
"dbname": "dbname",
},
"engine://username:password@host:1234/dbname",
),
(
{
"engine": "engine",
"username": "username",
"host": "host",
"dbname": "dbname",
},
"engine://username@host/dbname",
),
(
{
"engine": "engine",
"host": "host",
"dbname": "dbname",
},
"engine://host/dbname",
),
(
{
"engine": "engine",
"password": "password",
"port": 1234,
"dbname": "dbname",
},
"engine:///dbname",
),
(
{
"engine": "engine",
"dbname": "dbname",
},
"engine:///dbname",
),
),
)
def test_database_url_from_env(environment_key, expected_result):
with mock.patch.dict(
os.environ,
{"DATABASE_CREDENTIALS": json.dumps(environment_key)},
clear=True,
):
assert util.database_url_from_env("DATABASE_CREDENTIALS") == expected_result


@pytest.mark.parametrize(
"value, expected",
[
Expand Down
44 changes: 44 additions & 0 deletions common/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

import json
import os
import re
import typing
Expand Down Expand Up @@ -54,10 +55,53 @@
major, minor, patch = python_version_tuple()


def is_cloud_foundry():
"""Return True if the deployment environment contains a `VCAP_SERVICES` env
var, indicating a CloudFoundry environment, False otherwise."""
return "VCAP_SERVICES" in os.environ


def classproperty(fn):
return classmethod(property(fn))


def database_url_from_env(environment_key: str) -> str:
"""
Return a database URL string from the environment variable identified by
`environment_key`. The environment variable should be parsable as a
JSON-like string and may contain the keys:

"engine" (Required) - database engine id. For instance "postgres" or "sqlite".
"username" (Optional if "password" is not present) - database user name.
"password" (Optional) - database user's password.
"host" (Optional if "port" is not present) - database hostname.
"port" (Optional) - database host port.
"dbname" (Required) - database name.

If all keys are present, then the returned result would be a string of the
form:

<engine>://<username>:<password>@<host>:<port>/<dbname>

This is a plug-in, less naive version of
`dbt_copilot_python.database.database_url_from_env()` making `username`,
`password`, `host` and `port` an optional as described above.
"""
config = json.loads(os.environ[environment_key])

username = config.get("username", "")
password = config.get("password")
host = config.get("host", "")
port = config.get("port")

config["username"] = username
config["password"] = f":{password}" if username and password else ""
config["host"] = f"@{host}" if (username or password) and host else host
config["port"] = f":{port}" if host and port else ""

return "{engine}://{username}{password}{host}{port}/{dbname}".format(**config)


def is_truthy(value: Union[str, bool]) -> bool:
"""
Check whether a string represents a True boolean value.
Expand Down
36 changes: 33 additions & 3 deletions common/views/pages.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""Common views."""

import logging
import os
import time
from datetime import datetime
from datetime import timedelta
from typing import Dict
from typing import List
from typing import Optional
Expand Down Expand Up @@ -36,6 +38,7 @@
from common.celery import app as celery_app
from common.forms import HomeSearchForm
from common.models import Transaction
from common.util import is_cloud_foundry
from exporter.sqlite.util import sqlite_dumps
from footnotes.models import Footnote
from geo_areas.models import GeographicalArea
Expand All @@ -47,6 +50,8 @@
from workbaskets.models import WorkBasket
from workbaskets.models import WorkflowStatus

logger = logging.getLogger(__name__)


class HomeView(LoginRequiredMixin, FormView):
template_name = "common/homepage.jinja"
Expand Down Expand Up @@ -322,6 +327,33 @@ def get(self, request, *args, **kwargs) -> HttpResponse:
)


def get_uptime() -> str:
"""
Return approximate system uptime in a platform-independent way as a string
in the following format:
"<days> days, <hours> hours, <mins> minutes"
"""
try:
if is_cloud_foundry():
# CF recycles Garden containers so time.monotonic() returns a
# misleading value. However, file modified time is set on deployment.
uptime = timedelta(seconds=(time.time() - os.path.getmtime(__file__)))
else:
# time.monotonic() doesn't count time spent in hibernation, so may
# be inaccurate on systems that hibernate.
uptime = timedelta(seconds=time.monotonic())

formatted_uptime = (
f"{uptime.days} days, {uptime.seconds // 3600} hours, "
f"{uptime.seconds // 60 % 60} minutes"
)
except Exception as e:
logger.error(e)
formatted_uptime = "Error getting uptime"

return formatted_uptime


class AppInfoView(
LoginRequiredMixin,
TemplateView,
Expand Down Expand Up @@ -416,9 +448,7 @@ def get_context_data(self, **kwargs):
if self.request.user.is_superuser:
data["GIT_BRANCH"] = os.getenv("GIT_BRANCH", "Unavailable")
data["GIT_COMMIT"] = os.getenv("GIT_COMMIT", "Unavailable")
data["APP_UPDATED_TIME"] = AppInfoView.timestamp_to_datetime_string(
os.path.getmtime(__file__),
)
data["UPTIME"] = get_uptime()
last_transaction = Transaction.objects.order_by("updated_at").last()
data["LAST_TRANSACTION_TIME"] = (
format(
Expand Down
4 changes: 2 additions & 2 deletions exporter/sqlite/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ def make_export(connection: apsw.Connection):
Path(temp_sqlite_db.name),
)
plan = make_export_plan(plan_runner)
# make_tamato_database() creates a Connection instance that needs
# closing once an in-memory plan has been created from it.
# Runner.make_tamato_database() (above) creates a Connection instance
# that needs closing once an in-memory plan has been created from it.
plan_runner.database.close()

export_runner = runner.Runner(connection)
Expand Down
Loading
Loading