Skip to content

Commit 673893e

Browse files
committed
Make it easier and faster to iterate on task-sdk-integration-tests
There were a few of things that made task-sdk-tests iteration a bit unobvious and slower. With this PR, we should be able to iterate over task-sdk-integration-tests WAY faster and get more contributors involved in contributing to those. * It was not clear that prerequisite of running the tests was building PROD image for Pyton 3.10. This is now clear in the documentation. * PROD images can be built in two different modes - from sources with --installation-method equal to . or from packages with the --installatio-method equal to "apache-airflow". This was not clearly communicated during build and it is now printed at output * It was not clear that when you build PROD images from sources, you should first compile ui assets, because otehrwise the assets are not added as part of the image. With this PR the `breeze prod-image build` command checks if the .vite manifest is present in the right `dist` folders and will error out, suggesting to run `breeze compile-ui-assets` before. * If the PROD image has not been built before, breeze will propose to build it and even do it automatically if the answer is not provided within 20 seconds. * when building PROD images from sources, it is faster to rebuild the images with `uv` than with `pip`. the --use-uv parameter now defaults to False when building from packages and to True when building from sources. * There was an error in .dockerignore where generated dist files were not added to context when PROD image was built from sources. This resulted in "permission denied' when such PROD images were used to run tests. * The test compose had fallback of Airflow 3.0.3 which would be misleading if it happened. Now, AIRFLOW_IMAGE_NAME is mandatory * We are now mounting sources of Airflow to inside the image by default and skip it in CI. This mounting happens in local environment where PROD image is built usually from sources, and it is disabled in CI by using --skip-mounting-local-volumes flag. We also do not stop docker compose by default when runnig it locally in order to make fast iteration the default. * We pass host operating system when starting the compose, and we only change ownership on Linux - this is a long running operation on MacOS because mounted filesystem is slow, but it's also not needed on MacOS because the file system also maps ownershipt and files created by Airflow are created with local user id. * We pass local user id to containers to make sure that the files created on linux are created by the local user (logs and the like). * We are now detecting whether docker-compose is running and when we run with locally mounted sources, we reuse those running containers. When we don't mount local sources, we shut-down the compose before running to make sure we do not have sources mounted - and we close the compose by default when we do not mount local sources. * When sources are mounted we are only enabling DEV_MODE inside the containers so that components are hot-reloading (new feature added in apache#57741 last weeks. This way you do not have to restart anything when sources are changed and you can re-run the tests when docker compose is running. * The environment are passsed now via .env file so that you can easily reproduce docke compose command locally * The docker compose files are not copied any more, they are moved directly to the top of 'task-sdk-integraiton-tests' and used from there. * A `--down` flag is added to breeze testing task-sdk-integration-tests that tears down running docker compose. * Additional diagnostics added to show what's going on. * Handling verbose option from breeze by adding more debugging information * Updated documentation about the tests to be more comprehensive about the options, updated file structure etc. * Small QOL immprovement - if expected dags are not yet parsed by dag file processor, when test starts, getting their status will return 404 "Not Found". In such case our tests implemented a short retry scheme with tenacity
1 parent f62b813 commit 673893e

22 files changed

+696
-232
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ airflow/www/node_modules
140140

141141
# But ensure UI dist files are included
142142
!airflow-core/src/airflow/ui/dist
143+
!airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/dist
143144
!providers/fab/src/airflow/providers/fab/www/dist
144145
!providers/edge3/src/airflow/providers/edge3/plugins/www/dist
145146

.github/workflows/additional-prod-image-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ jobs:
190190
make-mnt-writeable-and-cleanup: true
191191
id: breeze
192192
- name: "Run Task SDK integration tests"
193-
run: breeze testing task-sdk-integration-tests
193+
run: breeze testing task-sdk-integration-tests --skip-mounting-local-volumes
194194

195195
test-e2e-integration-tests-basic:
196196
name: "Test e2e integration tests with PROD image"

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,3 +281,6 @@ _e2e_test_report.json
281281

282282
# UV cache
283283
.uv-cache/
284+
285+
# Allow logs folder in task-sdk-integration-tests (they logs folders are ignored above in general)
286+
!/task-sdk-integration-tests/logs/

contributing-docs/testing/task_sdk_integration_tests.rst

Lines changed: 185 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,61 @@ Why Task SDK Integration?
6868
Running Task SDK Integration Tests
6969
----------------------------------
7070

71-
There are multiple ways to run Task SDK Integration Tests depending based on your preferences.
71+
Prerequisite - build PROD image
72+
...............................
73+
74+
.. note::
75+
76+
The task-sdk integration tests are using locally build production images started in docker-compose by
77+
Pytest. This means that while the tests are running in the environment that you start it from (usually
78+
local development environment), you need to first build the images that you want to test against.
79+
80+
You also need to make sure that your assets are built first.
81+
.. code-block:: bash
82+
83+
# From the Airflow repository root
84+
breeze compile-ui-assets
85+
86+
Then, you should build the base image once before running the tests. You can do it using Breeze:
87+
88+
.. code-block:: bash
89+
90+
# From the Airflow repository root
91+
breeze prod-image build --python 3.10
92+
93+
The first build may take a while as it needs to download base image, build Python, install dependencies
94+
and set up the environment. Subsequent builds will be much faster as they will use cached layers.
95+
96+
You can choose other Python versions supported by Airflow by changing the ``--python`` argument.
97+
98+
If you use ``breeze`` to run the integration tests and you do not have the image built before,
99+
``breeze`` will prompt you to build it, and the building will proceed automatically after 20 seconds
100+
if you do not answer ``no``.
101+
102+
This will build the right image ``ghcr.io/apache/airflow/main/prod/python3.10.latest`` (with the right
103+
Python version) that will be used to run the tests. The ``breeze prod image build`` command by default -
104+
when run from sources of airflow - will use the local sources and build the image using ``uv``
105+
to speed up the build process. Also when building from sources it will check if the assets are built
106+
and will error if they are not. However it will not check if the assets are up to date - so make sure
107+
to run the ``breeze compile-ui-assets`` command above if you have changed any UI sources
108+
and did not build your assets after that.
109+
110+
.. tip::
111+
112+
Note that you do not need to rebuild the image every time you run the tests and change Python sources -
113+
because the docker-compose setup we use in tests will automatically mount the local Python sources into the
114+
container, so you can iterate quickly without rebuilding the image. However, if you want to test changes
115+
that require new image (like modifying dependencies, system packages, rebuilding UI etc.) you will need
116+
to rebuild the image with the ``breeze prod image build`` command.
117+
118+
After you build the image, there are several ways to run Task SDK Integration Tests,
119+
depending based on your preferences.
72120

73121
Using Breeze
74122
............
75123

76124
The simplest way to run Task SDK Integration Tests is using Breeze, which provides CI like
77-
reproducibility:
125+
reproducibility
78126

79127
.. code-block:: bash
80128
@@ -87,36 +135,103 @@ reproducibility:
87135
# Run with custom Docker image
88136
DOCKER_IMAGE=my-custom-airflow-image:latest breeze testing task-sdk-integration-tests
89137
90-
Running in Your Current Virtual Environment
91-
...........................................
138+
Using uv
139+
........
92140

93-
Since you're already working in the Airflow repository, you can run Task SDK Integration Tests
94-
directly:
141+
Since you're already working in the Airflow repository, you can run Task SDK Integration Tests directly.
142+
Make sure you have ``uv`` installed in your environment and make sure to run all those commands
143+
in the ``task-sdk-integration-tests`` directory. You can also run ``uv sync`` in the directory
144+
first to make sure that your virtual environment is up to date.
145+
146+
.. code-block::
147+
148+
# Navigate to task-sdk-integration-tests directory
149+
cd task-sdk-integration-tests
150+
151+
# Sync dependencies
152+
uv sync
153+
154+
All the ``uv`` and ``docker compose`` commands below should be run from within the
155+
``task-sdk-integration-tests`` directory.
95156

96157
**Run Tests**
97158

98159
.. code-block:: bash
99160
100161
# Navigate to task-sdk-integration-tests directory and run tests
101-
cd task-sdk-integration-tests/
102162
uv run pytest -s
103163
104164
# Run specific test file
105-
cd task-sdk-integration-tests/
106165
uv run pytest tests/task_sdk_tests/test_task_sdk_health.py -s
107166
108-
# Keep containers running for debugging
109-
cd task-sdk-integration-tests/
110-
SKIP_DOCKER_COMPOSE_DELETION=1 uv run pytest -s
111-
112167
**Optional: Set Custom Docker Image**
113168

114169
.. code-block:: bash
115170
116171
# Use a different Airflow image for testing
117-
cd task-sdk-integration-tests/
118172
DOCKER_IMAGE=my-custom-airflow:latest uv run pytest -s
119173
174+
By default when you run your tests locally, the Docker Compose deployment is kept between the sessions,
175+
your local sources are mounted into the containers and the Airflow services are restarted automatically
176+
(hot reloaded) when Python sources change.
177+
178+
This allows for quick iterations without rebuilding the image or restarting the containers.
179+
180+
Generated .env file
181+
...................
182+
183+
When you run the tests an .env file is generated in the task-sdk-integration-tests directory.
184+
This file contains environment variables used by docker-compose to configure the services.
185+
You can inspect or modify this file if you need to change any configurations so that you can
186+
also debug issues by running ``docker compose`` commands directly.
187+
188+
When running the tests with VERBOSE=1 environment variable set or --verbose flag passed to breeze command,
189+
the docker-compose commands used to start the services are also printed to the console and you can copy
190+
them to run them directly.
191+
192+
Stopping docker-compose
193+
.......................
194+
195+
When you finish testing (or when you updated dependencies and rebuild your images),
196+
you likely want to stop the running containers. You can stop the the running containers by running:
197+
198+
.. code-block:: bash
199+
200+
# Stop and remove containers
201+
docker-compose down -v --remove-orphans
202+
203+
amd with breeze:
204+
205+
206+
.. code-block:: bash
207+
208+
# Using Breeze to stop docker compose
209+
breeze testing task-sdk-integration-tests --down
210+
211+
Docker compose will be automatically started again next time you run the tests.
212+
213+
Running tests in the way CI does it
214+
....................................
215+
216+
Our CI runs the tests in a clean environment every time without mounting local sources. This means that
217+
any changes you have locally will not be visible inside the containers. You can reproduce it locally by adding
218+
--skip-mounting-local-volumes to breeze command or by setting SKIP_MOUNTING_LOCAL_VOLUMES=1 in your
219+
environment when running tests locally. Before that however make sure that your PROD image is rebuilt
220+
using latest sources. When you disable mounting local volumes, the containers will be stopped by default
221+
when the tests end, you can disable that by setting SKIP_DOCKER_COMPOSE_DELETION=1 in your environment
222+
or passing --skip-docker-compose-deletion to breeze command.
223+
224+
.. code-block:: bash
225+
226+
# Keep containers running for debugging
227+
SKIP_MOUNTING_LOCAL_VOLUMES=1 uv run pytest -s
228+
229+
or
230+
231+
.. code-block:: bash
232+
233+
# Using Breeze to keep containers running
234+
breeze testing task-sdk-integration-tests --skip-mounting-local-volumes
120235
121236
Debugging Failed Tests
122237
......................
@@ -127,38 +242,34 @@ and the Docker Compose deployment is shut down. To debug issues more effectively
127242
.. code-block:: bash
128243
129244
# Run with maximum verbosity
130-
cd task-sdk-integration-tests/
131245
uv run pytest tests/task_sdk_tests/ -vvv -s --tb=long
132246
133-
# Keep containers running for inspection (local environment)
134-
cd task-sdk-integration-tests/
135-
SKIP_DOCKER_COMPOSE_DELETION=1 uv run pytest tests/task_sdk_tests/test_task_sdk_health.py::test_task_sdk_health
247+
# Print logs from all services
248+
docker-compose logs
136249
137-
# Keep containers running for inspection (using Breeze)
138-
breeze testing task-sdk-integration-tests --skip-docker-compose-deletion
139-
140-
# Inspect container logs (when containers are still running)
141-
cd task-sdk-integration-tests/docker
142-
docker-compose logs airflow-apiserver
143-
docker-compose logs airflow-scheduler
144-
docker-compose logs postgres
250+
# Inspect container logs (when containers are still running - which is default)
251+
# The -f flag follows the logs in real-time so you can open several terminals to monitor different services
252+
docker-compose logs -f airflow-apiserver
253+
docker-compose logs -f airflow-scheduler
254+
docker-compose logs -f postgres
145255
146256
# Access running containers for interactive debugging
147-
docker-compose exec airflow-apiserver bash
257+
docker-compose exec -it airflow-apiserver bash
148258
149-
.. tip::
150-
**Container Cleanup Control**: By default, the Docker Compose deployment is deleted after tests
151-
complete to keep your system clean. To keep containers running for debugging:
259+
Every time you save airflow source code, the components running inside the container will be restarted
260+
automatically (hot reloaded). You can disable this behaviour by setting SKIP_MOUNTING_LOCAL_VOLUMES=1
261+
as described above (but then your sources will not be mounted).
152262

153-
- **Local environment**: Export ``SKIP_DOCKER_COMPOSE_DELETION=1`` before running tests
154-
- **Breeze environment**: Use the ``--skip-docker-compose-deletion`` flag
155263

156-
Remember to manually clean up containers when done: ``cd task-sdk-integration-tests/docker && docker-compose down -v``
264+
Using Other Airflow Images
265+
..........................
157266

158-
Testing Custom Airflow Images
159-
..............................
267+
To test the Task SDK against other Airflow images (for example with different Python or custom build
268+
images) - you can specify it by setting the DOCKER_IMAGE environment variable before running the tests.
160269

161-
To test the Task SDK against custom Airflow builds:
270+
Note that when you are running tests using breeze, the right image is built automatically for you
271+
for the Python version you specify in breeze command. So you do not need to set DOCKER_IMAGE
272+
when using breeze. Using custom image is only supported when you are running tests using uv directly.
162273

163274
.. code-block:: bash
164275
@@ -168,8 +279,7 @@ To test the Task SDK against custom Airflow builds:
168279
169280
# Use custom image for integration tests
170281
export DOCKER_IMAGE=my-custom-airflow:latest
171-
cd task-sdk-integration-tests
172-
uv run pytest tests/task_sdk_tests/
282+
uv run pytest tests/task_sdk_tests/ -s
173283
174284
Common Issues and Solutions
175285
---------------------------
@@ -194,8 +304,9 @@ The Task SDK Integration Tests are organized as follows:
194304
195305
task-sdk-integration-tests/
196306
├── pyproject.toml # Test package configuration and dependencies
197-
├── docker/
198-
│ └── docker-compose.yaml # Airflow services configuration
307+
├── docker-compose.yaml # Airflow services configuration
308+
├── docker-compose-local.yaml # local mounted volumes configuration
309+
├── .env # Generated (.gitignored) environment variables for docker-compose
199310
└── tests/
200311
└── task_sdk_tests/
201312
├── conftest.py # Test configuration and setup
@@ -206,10 +317,44 @@ The Task SDK Integration Tests are organized as follows:
206317
**Key Files:**
207318

208319
- **docker-compose.yaml**: Defines the complete Airflow environment (postgres, scheduler, api-server)
320+
- **docker-compose.yaml**: Defines mounts used to mount local sources to the containers for local testing
321+
- **.env**: Generated environment variables for docker-compose configuration
209322
- **test_task_sdk_health.py**: Main test that verifies Task SDK can communicate with Airflow API
210323
- **conftest.py**: Handles Task SDK installation and test environment setup
211324
- **constants.py**: Configuration constants for Docker images, ports, and API versions
212325

213-
-----
326+
Manual docker compose commands
327+
------------------------------
328+
329+
The commands run by pytest to start the docker compose environment are printed to the console
330+
when running the tests with VERBOSE=1 environment variable set or --verbose flag passed to breeze command.
331+
332+
However some basic commands that you can use to manage the docker compose environment manually are:
333+
334+
.. code-block:: bash
335+
336+
# Start the environment without manually mounted volumes
337+
docker-compose up -d -f docker-compose.yaml
338+
339+
# Start the environment with manually mounted volumes
340+
docker-compose up -d -f docker-compose.yaml -f docker-compose-local.yaml
341+
342+
# Stop the environment
343+
docker-compose down -v --remove-orphans
344+
345+
# See running commands
346+
docker-compose ps
347+
348+
# Access running containers for interactive debugging
349+
docker-compose exec -it airflow-apiserver bash
350+
351+
# Print logs from all services
352+
docker-compose logs
353+
354+
# Print logs from specific service
355+
# The -f flag follows the logs in real-time so you can open several terminals to monitor different services
356+
docker-compose logs -f airflow-apiserver
357+
358+
214359
215360
For other kinds of tests look at `Testing document <../09_testing.rst>`__

0 commit comments

Comments
 (0)