-
-
Notifications
You must be signed in to change notification settings - Fork 343
Fix running e2e backend #2710
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
Fix running e2e backend #2710
Changes from 21 commits
cafa25f
fd0aa86
9047b0b
1556873
f506341
c35e43f
a80f661
d2d8a58
a96185f
74bdb6c
9893384
3b4c9e2
f747481
ddae26b
95b1391
4d2e344
fd2219d
3ea8fc7
5e2c36c
d3f9aef
a1570cf
ddc7e32
e1d7272
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,23 +7,18 @@ runs: | |
| steps: | ||
| - name: Wait for database to be ready | ||
| run: | | ||
| until docker exec ${{ job.services.db.id }} pg_isready -U nest_user_e2e -d nest_db_e2e; do | ||
| echo "Waiting for database..." | ||
| sleep 5 | ||
| done | ||
| timeout 5m bash -c ' | ||
| until docker exec ${{ job.services.db.id }} pg_isready -U nest_user_e2e -d nest_db_e2e; do | ||
| echo "Waiting for database..." | ||
| sleep 5 | ||
| done | ||
| ' | ||
| shell: bash | ||
|
|
||
| - name: Install PostgreSQL client | ||
| run: sudo apt-get install -y postgresql-client | ||
| shell: bash | ||
|
|
||
| - name: Load Postgres data | ||
| env: | ||
| PGPASSWORD: nest_user_e2e_password | ||
| run: | | ||
| gunzip -c backend/data/nest-e2e.sql.gz | psql -h localhost -U nest_user_e2e -d nest_db_e2e | ||
| shell: bash | ||
|
|
||
| - name: Build backend e2e image | ||
| uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 | ||
| with: | ||
|
|
@@ -43,17 +38,28 @@ runs: | |
| --env-file backend/.env.e2e.example \ | ||
| --network host \ | ||
| -p 9000:9000 \ | ||
| -e DJANGO_DB_HOST=localhost \ | ||
| owasp/nest:test-backend-e2e-latest \ | ||
| sh -c ' | ||
| python manage.py migrate && | ||
| gunicorn wsgi:application --bind 0.0.0.0:9000 | ||
| ' | ||
| shell: bash | ||
|
|
||
| - name: Waiting for the backend to be ready | ||
| run: | | ||
| until wget --spider http://localhost:9000/a; do | ||
| echo "Waiting for backend..." | ||
| sleep 5 | ||
| done | ||
| timeout 5m bash -c ' | ||
| until wget --spider http://localhost:9000/a; do | ||
| echo "Waiting for backend..." | ||
| sleep 5 | ||
| done | ||
| ' | ||
| echo "Backend is up!" | ||
| shell: bash | ||
|
|
||
| - name: Load Postgres data | ||
| env: | ||
| PGPASSWORD: nest_user_e2e_password | ||
| run: | | ||
| gunzip -c backend/data/nest.sql.gz | psql -h localhost -U nest_user_e2e -d nest_db_e2e | ||
| shell: bash | ||
|
Comment on lines
+60
to
+65
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SQL import: ensure it’s part of the “readiness” contract 🤖 Prompt for AI Agents |
||
ahmedxgouda marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| """Dump masked data from the database into a compressed file.""" | ||
|
|
||
| import contextlib | ||
| import os | ||
| from pathlib import Path | ||
| from subprocess import CalledProcessError, run | ||
|
|
||
| from django.conf import settings | ||
| from django.core.management.base import BaseCommand, CommandError | ||
| from psycopg2 import ProgrammingError, connect, sql | ||
|
|
||
| DB = settings.DATABASES["default"] | ||
| HOST = DB.get("HOST", "localhost") | ||
| PORT = str(DB.get("PORT", "5432")) | ||
| USERNAME = DB.get("USER", "") | ||
| PASSWORD = DB.get("PASSWORD", "") | ||
| NAME = DB.get("NAME", "") | ||
|
|
||
|
|
||
| class Command(BaseCommand): | ||
| help = "Create a dump of selected db tables." | ||
|
|
||
| def add_arguments(self, parser): | ||
| parser.add_argument( | ||
| "--output", | ||
| default=str(Path(settings.BASE_DIR) / "data" / "nest.sql.gz"), | ||
| help="Output dump path (default: data/nest.sql.gz)", | ||
| ) | ||
| parser.add_argument( | ||
| "-t", | ||
| "--table", | ||
| action="append", | ||
| dest="tables", | ||
| default=[ | ||
| "public.owasp_*", | ||
| "public.github_*", | ||
| "public.slack_members", | ||
| "public.slack_workspaces", | ||
| "public.slack_conversations", | ||
| "public.slack_messages", | ||
| ], | ||
| help=( | ||
| "Table pattern to include. " | ||
| "Defaults: public.owasp_*, public.github_*, public.slack_members, " | ||
| "public.slack_workspaces, public.slack_conversations, public.slack_messages." | ||
| ), | ||
| ) | ||
|
|
||
| def handle(self, *args, **options): | ||
| output_path = Path(options["output"]).resolve() | ||
| tables = options["tables"] or [] | ||
| output_path.parent.mkdir(parents=True, exist_ok=True) | ||
|
|
||
| temp_db = f"temp_{NAME}" | ||
| env = os.environ.copy() | ||
| env["PGPASSWORD"] = PASSWORD | ||
| self.stdout.write(self.style.NOTICE(f"Creating temporary database: {temp_db}")) | ||
| try: | ||
| # 1) Create temp DB from template | ||
| self._execute_sql( | ||
| "postgres", | ||
| [f"CREATE DATABASE {temp_db} TEMPLATE {NAME};"], | ||
| ) | ||
| # 2) Get tables with email field | ||
| self.stdout.write(self.style.NOTICE("Fetching tables with email fields…")) | ||
|
|
||
| table_list = self._execute_sql( | ||
| temp_db, | ||
| [self._table_list_query()], | ||
| ) | ||
|
|
||
| # 3) Hide email fields | ||
| self.stdout.write(self.style.NOTICE("Hiding email fields in temp DB…")) | ||
| self._execute_sql(temp_db, self._hide_emails_queries([row[0] for row in table_list])) | ||
| # 4) Dump selected tables | ||
| self.stdout.write(self.style.NOTICE(f"Creating dump at: {output_path}")) | ||
| dump_cmd = [ | ||
| "pg_dump", | ||
| "-h", | ||
| HOST, | ||
| "-p", | ||
| PORT, | ||
| "-U", | ||
| USERNAME, | ||
| "-d", | ||
| temp_db, | ||
| "--compress=9", | ||
| "--clean", | ||
| ] | ||
| dump_cmd += [f"--table={table}" for table in tables] | ||
| dump_cmd += ["-f", str(output_path)] | ||
|
|
||
| run(dump_cmd, check=True, env=env) | ||
| self.stdout.write(self.style.SUCCESS(f"Dump created: {output_path}")) | ||
| except CalledProcessError as e: | ||
| message = f"Command failed: {e.cmd}" | ||
| raise CommandError(message) from e | ||
| finally: | ||
| # 4) Drop temp DB | ||
| self.stdout.write(self.style.NOTICE(f"Dropping temporary database: {temp_db}")) | ||
| try: | ||
| self._execute_sql( | ||
| "postgres", | ||
| [f"DROP DATABASE IF EXISTS {temp_db};"], | ||
| ) | ||
| except CalledProcessError: | ||
| # Best-effort cleanup | ||
| self.stderr.write( | ||
| self.style.WARNING(f"Failed to drop temp DB {temp_db} (ignored).") | ||
| ) | ||
|
|
||
| def _table_list_query(self) -> str: | ||
| return """ | ||
| SELECT table_name | ||
| FROM information_schema.columns | ||
| WHERE table_schema = 'public' AND column_name = 'email'; | ||
| """ | ||
|
|
||
| def _hide_emails_queries(self, tables: list[str]) -> list[str]: | ||
| return [ | ||
| sql.SQL("UPDATE {table} SET email = '';").format(table=sql.Identifier(table)) | ||
| for table in tables | ||
| ] | ||
|
|
||
| def _execute_sql( | ||
| self, | ||
| dbname: str, | ||
| sql_queries: list[str], | ||
| ): | ||
| connection = connect( | ||
| dbname=dbname, | ||
| user=USERNAME, | ||
| password=PASSWORD, | ||
| host=HOST, | ||
| port=PORT, | ||
| ) | ||
| connection.autocommit = True | ||
| rows = [] | ||
| with connection.cursor() as cursor: | ||
| for sql in sql_queries: | ||
| cursor.execute(sql) | ||
| with contextlib.suppress(ProgrammingError): | ||
| rows.extend(cursor.fetchall()) | ||
| connection.close() | ||
| return rows |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add
apt-get updateto reduce CI flakinessOn GitHub-hosted runners,
apt-get installcan intermittently fail without anapt-get updatefirst.📝 Committable suggestion
🤖 Prompt for AI Agents