Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
!airflow-ctl-tests
!shared/
!airflow-e2e-tests
!providers-e2e-tests

# Add scripts so that we can use them inside the container
!scripts
Expand Down
33 changes: 19 additions & 14 deletions .github/workflows/additional-prod-image-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,16 @@ on: # yamllint disable-line rule:truthy
description: "Whether to run Go SDK e2e tests (true/false)"
required: true
type: string
run-providers-e2e-tests-openlineage:
run-openlineage-e2e-tests:
description: "Whether to run OpenLineage e2e tests (true/false)"
required: true
type: string
run-openlineage-e2e-compat-tests:
description: "Whether to run the OpenLineage older-Airflow compat matrix (true/false)"
required: true
type: string
providers-compatibility-tests-matrix:
description: "JSON matrix of released Airflow versions for provider e2e compat tests."
description: "JSON matrix of released Airflow versions for the OpenLineage e2e compat tests."
required: true
type: string
constraints-branch:
Expand Down Expand Up @@ -329,34 +333,35 @@ jobs:
e2e_test_mode: "go_sdk"
if: inputs.canary-run == 'true' || inputs.run-go-sdk-e2e-tests == 'true'

test-providers-e2e-tests-openlineage:
name: "Provider E2E tests openlineage"
uses: ./.github/workflows/providers-e2e-tests.yml
test-e2e-integration-tests-openlineage:
name: "OpenLineage e2e tests with PROD image"
uses: ./.github/workflows/airflow-e2e-tests.yml
with:
provider: "openlineage"
provider-display-name: "OpenLineage"
workflow-name: "OpenLineage e2e test"
runners: ${{ inputs.runners }}
platform: ${{ inputs.platform }}
default-python-version: "${{ inputs.default-python-version }}"
use-uv: ${{ inputs.use-uv }}
if: inputs.canary-run == 'true' || inputs.run-providers-e2e-tests-openlineage == 'true'
e2e_test_mode: "openlineage"
if: inputs.canary-run == 'true' || inputs.run-openlineage-e2e-tests == 'true'

test-providers-e2e-tests-openlineage-compat:
Comment thread
shahar1 marked this conversation as resolved.
name: "Provider E2E tests openlineage compat"
# Older-Airflow compatibility: costly, so it does not run on every OpenLineage PR — only on canary
# (scheduled / main) or when a maintainer sets the "full tests needed" label (both folded into the
# run-openlineage-e2e-compat-tests selective-check output). PRs otherwise run the PROD job only.
test-e2e-integration-tests-openlineage-compat:
name: "OpenLineage e2e compat tests with PROD image"
strategy:
fail-fast: false
matrix:
compat: ${{ fromJSON(inputs.providers-compatibility-tests-matrix) }}
uses: ./.github/workflows/providers-e2e-tests.yml
uses: ./.github/workflows/openlineage-e2e-compat-tests.yml
with:
provider: "openlineage"
provider-display-name: "OpenLineage"
runners: ${{ inputs.runners }}
platform: ${{ inputs.platform }}
default-python-version: "${{ inputs.default-python-version }}"
use-uv: ${{ inputs.use-uv }}
airflow-version: ${{ matrix.compat.airflow-version }}
if: inputs.canary-run == 'true' || inputs.run-providers-e2e-tests-openlineage == 'true'
if: inputs.run-openlineage-e2e-compat-tests == 'true'

test-ui-e2e-chromium:
name: "Chromium UI e2e tests with PROD image"
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/ci-amd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ jobs:
run-event-driven-e2e-tests: ${{ steps.selective-checks.outputs.run-event-driven-e2e-tests }}
run-java-sdk-e2e-tests: ${{ steps.selective-checks.outputs.run-java-sdk-e2e-tests }}
run-go-sdk-e2e-tests: ${{ steps.selective-checks.outputs.run-go-sdk-e2e-tests }}
run-providers-e2e-tests-openlineage: ${{ steps.selective-checks.outputs.run-providers-e2e-tests-openlineage }}
run-openlineage-e2e-tests: ${{ steps.selective-checks.outputs.run-openlineage-e2e-tests }}
run-openlineage-e2e-compat-tests: ${{ steps.selective-checks.outputs.run-openlineage-e2e-compat-tests }}
run-system-tests: ${{ steps.selective-checks.outputs.run-system-tests }}
run-task-sdk-tests: ${{ steps.selective-checks.outputs.run-task-sdk-tests }}
run-task-sdk-integration-tests: ${{ steps.selective-checks.outputs.run-task-sdk-integration-tests }}
Expand Down Expand Up @@ -920,7 +921,9 @@ jobs:
run-event-driven-e2e-tests: ${{ needs.build-info.outputs.run-event-driven-e2e-tests }}
run-java-sdk-e2e-tests: ${{ needs.build-info.outputs.run-java-sdk-e2e-tests }}
run-go-sdk-e2e-tests: ${{ needs.build-info.outputs.run-go-sdk-e2e-tests }}
run-providers-e2e-tests-openlineage: ${{ needs.build-info.outputs.run-providers-e2e-tests-openlineage }}
run-openlineage-e2e-tests: ${{ needs.build-info.outputs.run-openlineage-e2e-tests }}
run-openlineage-e2e-compat-tests: ${{ needs.build-info.outputs.run-openlineage-e2e-compat-tests }}
# yamllint disable rule:line-length
providers-compatibility-tests-matrix: >
${{ needs.build-info.outputs.providers-compatibility-tests-matrix }}
use-uv: ${{ needs.build-info.outputs.use-uv }}
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/ci-arm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ jobs:
run-event-driven-e2e-tests: ${{ steps.selective-checks.outputs.run-event-driven-e2e-tests }}
run-java-sdk-e2e-tests: ${{ steps.selective-checks.outputs.run-java-sdk-e2e-tests }}
run-go-sdk-e2e-tests: ${{ steps.selective-checks.outputs.run-go-sdk-e2e-tests }}
run-providers-e2e-tests-openlineage: ${{ steps.selective-checks.outputs.run-providers-e2e-tests-openlineage }}
run-openlineage-e2e-tests: ${{ steps.selective-checks.outputs.run-openlineage-e2e-tests }}
run-openlineage-e2e-compat-tests: ${{ steps.selective-checks.outputs.run-openlineage-e2e-compat-tests }}
run-system-tests: ${{ steps.selective-checks.outputs.run-system-tests }}
run-task-sdk-tests: ${{ steps.selective-checks.outputs.run-task-sdk-tests }}
run-task-sdk-integration-tests: ${{ steps.selective-checks.outputs.run-task-sdk-integration-tests }}
Expand Down Expand Up @@ -909,7 +910,9 @@ jobs:
run-event-driven-e2e-tests: ${{ needs.build-info.outputs.run-event-driven-e2e-tests }}
run-java-sdk-e2e-tests: ${{ needs.build-info.outputs.run-java-sdk-e2e-tests }}
run-go-sdk-e2e-tests: ${{ needs.build-info.outputs.run-go-sdk-e2e-tests }}
run-providers-e2e-tests-openlineage: ${{ needs.build-info.outputs.run-providers-e2e-tests-openlineage }}
run-openlineage-e2e-tests: ${{ needs.build-info.outputs.run-openlineage-e2e-tests }}
run-openlineage-e2e-compat-tests: ${{ needs.build-info.outputs.run-openlineage-e2e-compat-tests }}
# yamllint disable rule:line-length
providers-compatibility-tests-matrix: >
${{ needs.build-info.outputs.providers-compatibility-tests-matrix }}
use-uv: ${{ needs.build-info.outputs.use-uv }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,56 +17,13 @@
#
---

name: Providers E2E Tests
name: OpenLineage E2E Compat Tests

permissions:
contents: read
on: # yamllint disable-line rule:truthy
workflow_dispatch:
inputs:
provider:
description: "The provider whose e2e suite to run (a directory under providers-e2e-tests/)."
type: string
default: 'openlineage'
provider-display-name:
description: "Display name for the provider, used only in the job name."
type: string
default: 'OpenLineage'
runners:
description: "The array of labels (in json form) determining runners."
type: string
default: '["ubuntu-24.04"]'
platform:
description: "Platform for the build - 'linux/amd64' or 'linux/arm64'"
type: string
default: 'linux/amd64'
default-python-version:
description: "Which version of python should be used by default"
type: string
default: '3.10'
use-uv:
description: "Whether to use uv to build the image (true/false)"
type: string
default: 'true'
docker-image-tag:
description: "Tag of the Docker image to test"
type: string
required: true
airflow-version:
description: "Released Airflow version to test with current providers (empty = default PROD image)"
type: string
default: ""

workflow_call:
inputs:
provider:
description: "The provider whose e2e suite to run (a directory under providers-e2e-tests/)."
type: string
default: 'openlineage'
provider-display-name:
description: "Display name for the provider, used only in the job name."
type: string
default: 'OpenLineage'
runners:
description: "The array of labels (in json form) determining runners."
required: true
Expand All @@ -83,44 +40,36 @@ on: # yamllint disable-line rule:truthy
description: "Whether to use uv to build the image (true/false)"
required: true
type: string
docker-image-tag:
description: "Tag of the Docker image to test"
type: string
default: ""
airflow-version:
description: "Released Airflow version to test with current providers (empty = default PROD image)"
description: "Released Airflow version to test with current providers from main."
required: true
type: string
default: ""

jobs:
test-providers-e2e-tests:
test-openlineage-e2e-compat:
timeout-minutes: 60
name: "Provider E2E tests \
${{ inputs.provider-display-name }}\
${{ inputs.airflow-version != '' && format(' Compat {0}', inputs.airflow-version) || '' }}"
name: "OpenLineage e2e compat ${{ inputs.airflow-version }}"
runs-on: ${{ fromJSON(inputs.runners) }}
env:
PYTHON_MAJOR_MINOR_VERSION: "${{ inputs.default-python-version }}"
PROVIDER: ${{ inputs.provider }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_USERNAME: ${{ github.actor }}
VERBOSE: "true"
steps:
# Gating on job-level `if:` instead of per-step would skip the whole job — and GitHub Actions
# does not evaluate a dynamic `name:` (the one above, using `inputs.*`) for a skipped job, so it
# renders the raw unevaluated template instead. Gating each step keeps the job itself "running"
# (and its name properly resolved), ending in success with every real step skipped for AF2.
# OpenLineage requires Airflow 3.x. Gating each step (rather than the whole job) keeps the job
# itself "running" so its dynamic name resolves, ending in success with every real step skipped
# for the Airflow 2.x matrix entry.
- name: "Skip notice for Airflow 2.x"
if: ${{ startsWith(inputs.airflow-version, '2.') }}
run: echo "Skipping $PROVIDER e2e tests — not supported against Airflow 2.x."
run: echo "Skipping OpenLineage e2e tests — not supported against Airflow 2.x."
- name: "Cleanup repo"
if: ${{ !startsWith(inputs.airflow-version, '2.') }}
shell: bash
run: sudo rm -rf ${GITHUB_WORKSPACE}/*
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
if: ${{ !startsWith(inputs.airflow-version, '2.') }}
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 2
persist-credentials: false
Expand All @@ -134,24 +83,23 @@ jobs:
use-uv: ${{ inputs.use-uv }}
make-mnt-writeable-and-cleanup: true
id: breeze
- name: "Run ${{ inputs.provider }} e2e tests"
- name: "Run OpenLineage e2e compat tests against Airflow ${{ inputs.airflow-version }}"
if: ${{ !startsWith(inputs.airflow-version, '2.') }}
run: >
breeze testing providers-e2e-tests "$PROVIDER"
${AIRFLOW_VERSION:+--airflow-version ${AIRFLOW_VERSION}}
--skip-mounting-local-volumes
breeze testing airflow-e2e-tests
--e2e-test-mode openlineage
--airflow-version "${AIRFLOW_VERSION}"
env:
DOCKER_IMAGE: "${{ inputs.docker-image-tag }}"
AIRFLOW_VERSION: "${{ inputs.airflow-version }}"
- name: Zip logs
if: ${{ always() && !startsWith(inputs.airflow-version, '2.') }}
run: |
cd "./providers-e2e-tests/$PROVIDER" && zip -r logs.zip logs
cd ./airflow-e2e-tests && zip -r logs.zip logs
- name: "Upload logs"
if: ${{ always() && !startsWith(inputs.airflow-version, '2.') }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: "${{ inputs.provider }}-e2e-test-logs-${{ inputs.airflow-version || 'prod' }}"
path: './providers-e2e-tests/${{ inputs.provider }}/logs.zip'
name: "openlineage-e2e-compat-logs-${{ inputs.airflow-version }}"
path: './airflow-e2e-tests/logs.zip'
retention-days: '7'
if-no-files-found: 'error'
1 change: 0 additions & 1 deletion .rat-excludes
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ venv
clients/*
files/*
dags/*
providers-e2e-tests/openlineage/dags/dag_doc.md
generated/*
.gitmodules
prod_image_installed_providers.txt
Expand Down
83 changes: 83 additions & 0 deletions airflow-e2e-tests/docker/openlineage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

# Docker Compose override for the openlineage E2E test mode.
#
# The OpenLineage system-test DAGs are sourced into the dags folder by
# openlineage_tests/prepare_dags.py (conftest._setup_openlineage_integration). They emit through the
# test VariableTransport (events land in Airflow Variables) and each DAG's terminal
# OpenLineageTestOperator validates them, so a run ending "success" means the lineage matched.
---
# yamllint disable rule:line-length
x-ol-common-env: &ol-common-env
# These DAGs are the only ones the harness drives; keep the stock example DAGs out of the way.
AIRFLOW__CORE__LOAD_EXAMPLES: 'false'
# The OpenLineage system-test DAGs (and the custom VariableTransport / OpenLineageTestOperator
# they import) live under <dags>/system/openlineage; the dags folder is added to PYTHONPATH so
# both DAG parsing and the dotted-path transport class resolve in every component.
PYTHONPATH: '/opt/airflow/dags'
# Route OpenLineage events to the test VariableTransport (stores events in Airflow Variables,
# read back and validated by the in-DAG OpenLineageTestOperator). The transport type is the
# fully-qualified class path; the OpenLineage client imports it directly.
AIRFLOW__OPENLINEAGE__TRANSPORT: '{"type": "system.openlineage.transport.variable.VariableTransport"}'
AIRFLOW__OPENLINEAGE__DISABLED: 'false'
# Deployment-wide policy required by the two conf-driven policy DAGs. Each rule is scoped by
# dag_id, so both rules coexist and do not affect any other DAG.
AIRFLOW__OPENLINEAGE__EMISSION_POLICY: '[{"scope": {"dag_id": "openlineage_policy_conf_locked_dag"}, "locked": true, "controls": {"include_source_code": false}}, {"scope": {"dag_id": "openlineage_policy_conf_source_code_dag"}, "controls": {"include_source_code": false}}]'
AIRFLOW__OPENLINEAGE__EXECUTION_TIMEOUT: '60'
# Both control how quickly the dag-processor picks up dags: REFRESH_INTERVAL is how often it
# re-scans the folder for new/removed files, MIN_FILE_PROCESS_INTERVAL is how often it re-parses
# a file it already knows about. Tightened from the 300s/30s defaults so the e2e run doesn't wait
# on the dag-processor's own cadence on top of everything else.
AIRFLOW__DAG_PROCESSOR__REFRESH_INTERVAL: '3'
AIRFLOW__DAG_PROCESSOR__MIN_FILE_PROCESS_INTERVAL: '3'
# openlineage_defer_simple_dag's TimeDeltaSensor defaults to a 180s delta so it reliably defers at
# least once even before a worker picks up the task; tighten it here since we do not need to wait.
SYSTEM_TESTS_OL_TIMEDELTA: '60'
# Every DAG's dagrun_timeout (system.openlineage.constants.DEFAULT_DAGRUN_TIMEOUT) defaults to 10
# minutes; tighten it here since we do not need to wait for worker.
SYSTEM_TESTS_OL_DAGRUN_TIMEOUT_MINUTES: '5'
AIRFLOW__CORE__TASK_SUCCESS_OVERTIME: '70'
# Serve the dags folder through a versioned bundle so example_openlineage_versioned_dag gets a
# non-null dag_bundle_version. MockVersionedLocalDagBundle still serves from /opt/airflow/dags
# (keeping PYTHONPATH valid); it is copied into the dags folder by prepare_dags.py.
AIRFLOW__DAG_PROCESSOR__DAG_BUNDLE_CONFIG_LIST: '[{"name": "dags-folder", "classpath": "e2e_versioned_bundle.MockVersionedLocalDagBundle", "kwargs": {}}]'
# The breeze PROD image installs every provider from source, including informatica, whose listener
# fires on every task and errors without its connection — suppressing OL events. A curated
# deployment (e.g. Astro) does not ship it; disable its listener to match.
AIRFLOW__INFORMATICA__LISTENER_DISABLED: 'true'

x-ol-common: &ol-common
environment:
<<: *ol-common-env
volumes:
# example_openlineage_docs_file_dag sets doc_md="dag_doc.md", which Airflow resolves with a
# CWD-relative open(); components run with CWD=/opt/airflow. prepare_dags.py generates a clean,
# license-free dag_doc.md (matching the expected event) in the dags folder; expose it here.
- ./dags/dag_doc.md:/opt/airflow/dag_doc.md

services:
airflow-apiserver:
<<: *ol-common
airflow-scheduler:
<<: *ol-common
airflow-dag-processor:
<<: *ol-common
airflow-triggerer:
<<: *ol-common
airflow-worker:
<<: *ol-common
Loading
Loading