Skip to content

Commit 411a2af

Browse files
Merge branch 'devel' into devel
2 parents 83713ac + 325b6d3 commit 411a2af

File tree

11 files changed

+148
-72
lines changed

11 files changed

+148
-72
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,8 @@ jobs:
139139
build-ui: false
140140
github-token: ${{ secrets.GITHUB_TOKEN }}
141141

142-
- name: Run smoke test
143-
run: ansible-playbook tools/docker-compose/ansible/smoke-test.yml -v
142+
- name: Run live dev env tests
143+
run: docker exec tools_awx_1 /bin/bash -c "make live_test"
144144

145145
awx-operator:
146146
runs-on: ubuntu-latest

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,9 @@ test:
350350
cd awxkit && $(VENV_BASE)/awx/bin/tox -re py3
351351
awx-manage check_migrations --dry-run --check -n 'missing_migration_file'
352352

353+
live_test:
354+
cd awx/main/tests/live && py.test tests/
355+
353356
## Run all API unit tests with coverage enabled.
354357
test_coverage:
355358
$(MAKE) test PYTEST_ARGS="--create-db --cov --cov-report=xml --junitxml=reports/junit.xml"

awx/main/tests/README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
## Test Environments
2+
3+
Several of the subfolders of `awx/main/tests/` indicate a different required _environment_
4+
where you can run the tests. Those folders are:
5+
6+
- `functional/` - requires a test database and no other services running
7+
- `live/` - must run in `tools_awx_1` container launched by `make docker-compose`
8+
- `unit/` - does not require a test database or any active services
9+
10+
### Functional and unit test environment
11+
12+
The functional and unit tests have an invocation in `make test`,
13+
and this attaches several other things like schema that piggybacks on requests.
14+
These tests are ran from the root AWX folder.
15+
16+
#### Functional tests
17+
18+
Only tests in the `functional/` folder should use the `@pytest.mark.django_db` decorator.
19+
This is the only difference between the functional and unit folders,
20+
the test environment is otherwise the same for both.
21+
22+
Functional tests use a sqlite3 database, so the postgres service is not necessary.
23+
24+
### Live tests
25+
26+
The live tests have an invocation in `make live_test` which will change
27+
directory before running, which is required to pick up a different pytest
28+
configuration.
29+
30+
This will use the postges container from `make docker-compose` for the database,
31+
and will disable the pytest-django features of running with a test database
32+
and running tests in transactions.
33+
This means that any changes done in the course of the test could potentially
34+
be seen in your browser via the API or UI, and anything the test fails
35+
to clean up will remain in the database.
36+
37+
### Folders that should not contain tests
38+
39+
- `data/` - just files other tests use
40+
- `docs/` - utilities for schema generation
41+
- `factories/` - general utilities
42+
- `manual/` - python files to be ran directly

awx/main/tests/live/pytest.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# This file is needed to undo the pytest settings from the project root
2+
[pytest]
3+
addopts = -p no:django -p awx.main.tests.live.pytest_django_config
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import django
2+
3+
from awx import prepare_env
4+
5+
6+
def pytest_load_initial_conftests(args):
7+
"""Replacement for same-named method in pytest_django plugin
8+
9+
Instead of setting up a test database, this just sets up Django normally
10+
this will give access to the postgres database as-is, for better and worse"""
11+
prepare_env()
12+
django.setup()

awx/main/tests/live/tests/conftest.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import time
2+
3+
# These tests are invoked from the awx/main/tests/live/ subfolder
4+
# so any fixtures from higher-up conftest files must be explicitly included
5+
from awx.main.tests.functional.conftest import * # noqa
6+
7+
8+
def wait_to_leave_status(job, status, timeout=25, sleep_time=0.1):
9+
"""Wait until the job does NOT have the specified status with some timeout
10+
11+
the default timeout of 25 if chosen because the task manager runs on a 20 second
12+
schedule, and the API does not guarentee working jobs faster than this
13+
"""
14+
start = time.time()
15+
while time.time() - start < timeout:
16+
job.refresh_from_db()
17+
if job.status != status:
18+
return
19+
time.sleep(sleep_time)
20+
raise RuntimeError(f'Job failed to exit {status} in {timeout} seconds. job_explanation={job.job_explanation} tb={job.result_traceback}')
21+
22+
23+
def wait_for_job(job, final_status='successful', running_timeout=800):
24+
wait_to_leave_status(job, 'pending')
25+
wait_to_leave_status(job, 'waiting')
26+
wait_to_leave_status(job, 'running', timeout=running_timeout)
27+
28+
assert job.status == final_status, f'Job was not successful id={job.id} status={job.status}'
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from awx.api.versioning import reverse
2+
3+
from awx.main.models import JobTemplate, Job
4+
5+
from awx.main.tests.live.tests.conftest import wait_for_job
6+
7+
8+
def test_launch_demo_jt(post, admin):
9+
jt = JobTemplate.objects.get(name='Demo Job Template')
10+
11+
url = reverse('api:job_template_launch', kwargs={'pk': jt.id})
12+
13+
r = post(url=url, data={}, user=admin, expect=201)
14+
job = Job.objects.get(pk=r.data['id'])
15+
wait_for_job(job)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from datetime import timedelta
2+
3+
from django.utils.timezone import now
4+
from django.db import connection
5+
6+
from awx.main.utils.common import create_partition, table_exists
7+
8+
9+
def test_table_when_it_exists():
10+
with connection.cursor() as cursor:
11+
assert table_exists(cursor, 'main_job')
12+
13+
14+
def test_table_when_it_does_not_exists():
15+
with connection.cursor() as cursor:
16+
assert not table_exists(cursor, 'main_not_a_table_check')
17+
18+
19+
def test_create_partition_race_condition(mocker):
20+
mocker.patch('awx.main.utils.common.table_exists', return_value=False)
21+
22+
create_partition('main_jobevent', start=now() - timedelta(days=2))
23+
create_partition('main_jobevent', start=now() - timedelta(days=2))

awx/main/utils/common.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,17 @@ def deepmerge(a, b):
11381138
return b
11391139

11401140

1141+
def table_exists(cursor, table_name):
1142+
cursor.execute(f"SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = '{table_name}');")
1143+
row = cursor.fetchone()
1144+
if row is not None:
1145+
for val in row: # should only have 1
1146+
if val is True:
1147+
logger.debug(f'Event partition table {table_name} already exists')
1148+
return True
1149+
return False
1150+
1151+
11411152
def create_partition(tblname, start=None):
11421153
"""Creates new partition table for events. By default it covers the current hour."""
11431154
if start is None:
@@ -1154,13 +1165,8 @@ def create_partition(tblname, start=None):
11541165
try:
11551166
with transaction.atomic():
11561167
with connection.cursor() as cursor:
1157-
cursor.execute(f"SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = '{tblname}_{partition_label}');")
1158-
row = cursor.fetchone()
1159-
if row is not None:
1160-
for val in row: # should only have 1
1161-
if val is True:
1162-
logger.debug(f'Event partition table {tblname}_{partition_label} already exists')
1163-
return
1168+
if table_exists(cursor, f"{tblname}_{partition_label}"):
1169+
return
11641170

11651171
cursor.execute(
11661172
f'CREATE TABLE {tblname}_{partition_label} (LIKE {tblname} INCLUDING DEFAULTS INCLUDING CONSTRAINTS); '
@@ -1172,9 +1178,11 @@ def create_partition(tblname, start=None):
11721178
cause = e.__cause__
11731179
if cause and hasattr(cause, 'sqlstate'):
11741180
sqlstate = cause.sqlstate
1181+
if sqlstate is None:
1182+
raise
11751183
sqlstate_cls = psycopg.errors.lookup(sqlstate)
11761184

1177-
if psycopg.errors.DuplicateTable == sqlstate_cls or psycopg.errors.UniqueViolation == sqlstate_cls:
1185+
if sqlstate_cls in (psycopg.errors.DuplicateTable, psycopg.errors.DuplicateObject, psycopg.errors.UniqueViolation):
11781186
logger.info(f'Caught known error due to partition creation race: {e}')
11791187
else:
11801188
logger.error('SQL Error state: {} - {}'.format(sqlstate, sqlstate_cls))
@@ -1183,6 +1191,8 @@ def create_partition(tblname, start=None):
11831191
cause = e.__cause__
11841192
if cause and hasattr(cause, 'sqlstate'):
11851193
sqlstate = cause.sqlstate
1194+
if sqlstate is None:
1195+
raise
11861196
sqlstate_str = psycopg.errors.lookup(sqlstate)
11871197
logger.error('SQL Error state: {} - {}'.format(sqlstate, sqlstate_str))
11881198
raise

tools/docker-compose/ansible/smoke-test.yml

Lines changed: 0 additions & 60 deletions
This file was deleted.

tools/docker-compose/bootstrap_development.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ if [ ! -d "/awx_devel/awx/ui/build/awx" ]; then
3030
cp /awx_devel/awx/ui/placeholder_index_awx.html /awx_devel/awx/ui/build/awx/index_awx.html
3131
fi
3232

33-
if output=$(awx-manage createsuperuser --noinput --username=admin --email=admin@localhost 2> /dev/null); then
33+
if output=$(ANSIBLE_REVERSE_RESOURCE_SYNC=false awx-manage createsuperuser --noinput --username=admin --email=admin@localhost 2> /dev/null); then
3434
echo $output
3535
fi
3636
echo "Admin password: ${DJANGO_SUPERUSER_PASSWORD}"
3737

38-
awx-manage create_preload_data
38+
ANSIBLE_REVERSE_RESOURCE_SYNC=false awx-manage create_preload_data
3939
awx-manage register_default_execution_environments
4040

4141
awx-manage provision_instance --hostname="$(hostname)" --node_type="$MAIN_NODE_TYPE"

0 commit comments

Comments
 (0)