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
15 changes: 15 additions & 0 deletions .github/workflows/additional-prod-image-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ on: # yamllint disable-line rule:truthy
description: "Whether to run Java SDK e2e tests (true/false)"
required: true
type: string
run-go-sdk-e2e-tests:
description: "Whether to run Go SDK e2e tests (true/false)"
required: true
type: string
constraints-branch:
description: "Branch used to construct constraints URL from."
required: true
Expand Down Expand Up @@ -305,6 +309,17 @@ jobs:
use-uv: ${{ inputs.use-uv }}
e2e_test_mode: "java_sdk"
if: inputs.canary-run == 'true' || inputs.run-java-sdk-e2e-tests == 'true'
test-e2e-integration-tests-go-sdk:
name: "Go SDK e2e tests with PROD image"
uses: ./.github/workflows/airflow-e2e-tests.yml
with:
workflow-name: "Go SDK e2e test"
runners: ${{ inputs.runners }}
platform: ${{ inputs.platform }}
default-python-version: "${{ inputs.default-python-version }}"
use-uv: ${{ inputs.use-uv }}
e2e_test_mode: "go_sdk"
if: inputs.canary-run == 'true' || inputs.run-go-sdk-e2e-tests == 'true'

test-ui-e2e-chromium:
name: "Chromium UI e2e tests with PROD image"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/airflow-e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ on: # yamllint disable-line rule:truthy
type: string
required: true
e2e_test_mode:
description: "Test mode - basic, remote_log, remote_log_elasticsearch, remote_log_opensearch, xcom_object_storage, event_driven, or java_sdk" # yamllint disable-line rule:line-length
description: "Test mode - basic, remote_log, remote_log_elasticsearch, remote_log_opensearch, xcom_object_storage, event_driven, java_sdk, or go_sdk" # yamllint disable-line rule:line-length
type: string
default: "basic"

Expand Down Expand Up @@ -80,7 +80,7 @@ on: # yamllint disable-line rule:truthy
type: string
default: ""
e2e_test_mode:
description: "Test mode - basic, remote_log, remote_log_elasticsearch, remote_log_opensearch, xcom_object_storage, event_driven, or java_sdk" # yamllint disable-line rule:line-length
description: "Test mode - basic, remote_log, remote_log_elasticsearch, remote_log_opensearch, xcom_object_storage, event_driven, java_sdk, or go_sdk" # yamllint disable-line rule:line-length
type: string
default: "basic"

Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ci-amd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ jobs:
run-remote-logging-s3-e2e-tests: ${{ steps.selective-checks.outputs.run-remote-logging-s3-e2e-tests }}
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-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 @@ -880,6 +881,7 @@ jobs:
run-remote-logging-s3-e2e-tests: ${{ needs.build-info.outputs.run-remote-logging-s3-e2e-tests }}
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 }}
use-uv: ${{ needs.build-info.outputs.use-uv }}
run-ui-e2e-tests: ${{ needs.build-info.outputs.run-ui-e2e-tests }}
run-airflow-ctl-integration-tests: ${{ needs.build-info.outputs.run-airflow-ctl-integration-tests }}
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ci-arm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ jobs:
run-remote-logging-s3-e2e-tests: ${{ steps.selective-checks.outputs.run-remote-logging-s3-e2e-tests }}
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-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 @@ -870,6 +871,7 @@ jobs:
run-remote-logging-s3-e2e-tests: ${{ needs.build-info.outputs.run-remote-logging-s3-e2e-tests }}
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 }}
use-uv: ${{ needs.build-info.outputs.use-uv }}
run-ui-e2e-tests: ${{ needs.build-info.outputs.run-ui-e2e-tests }}
run-airflow-ctl-integration-tests: ${{ needs.build-info.outputs.run-airflow-ctl-integration-tests }}
Expand Down
37 changes: 37 additions & 0 deletions airflow-e2e-tests/docker/go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# 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 go_sdk E2E test mode.
#
# The Go bundle compiled by airflow-go-pack (conftest._setup_go_sdk_integration)
# is a self-contained, statically linked executable, so the stock Airflow worker
# image can exec it directly -- no extra runtime needs to be installed. We only
# bind-mount the bundle into the directory the ExecutableCoordinator scans and
# point the worker at the "golang" queue where @task.stub tasks are routed.
---
services:
airflow-worker:
# The bundle is built with CGO_ENABLED=0, so the SDK's user.Current() call at
# init falls back to Go's pure-Go resolver, which reads $USER / $HOME and
# panics if either is empty. The supervisor launches the bundle with the
# worker's environment, so set both here.
environment:
USER: airflow
HOME: /home/airflow
volumes:
- ./go-bundles:/opt/airflow/go-bundles:ro
command: celery worker -q golang,default
109 changes: 109 additions & 0 deletions airflow-e2e-tests/tests/airflow_e2e_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
E2E_DAGS_FOLDER,
E2E_TEST_MODE,
ELASTICSEARCH_PATH,
GO_BUILDER_IMAGE,
GO_COMPOSE_PATH,
GO_SDK_BIN_PATH,
GO_SDK_BUNDLE_NAME,
GO_SDK_DAGS_PATH,
GO_SDK_EXAMPLE_BUNDLE_PKG,
JAVA_COMPOSE_PATH,
JAVA_DOCKERFILE_PATH,
JAVA_SDK_DAGS_PATH,
Expand Down Expand Up @@ -333,6 +339,106 @@ def _setup_java_sdk_integration(dot_env_file, tmp_dir):
os.environ["ENV_FILE_PATH"] = str(dot_env_file)


def _setup_go_sdk_integration(dot_env_file, tmp_dir):
"""Set up the go_sdk E2E test mode.

Compiles the Go SDK example bundle into a self-contained executable bundle
via the ``airflow-go-pack`` tooling, drops it into the directory the
``ExecutableCoordinator`` scans, copies the Python stub Dag, and writes the
coordinator configuration.

The packed bundle is a statically linked native executable (built with
``CGO_ENABLED=0``), so the stock Airflow worker image can exec it directly
without a Go toolchain or any extra runtime installed -- see ``go.yml``.
"""
# Build + pack the example bundle inside an ephemeral Go container so the
# host does not need Go installed.
#
# --user keeps build outputs owned by the current user (not root).
# HOME points at a writable, gitignored dir under go-sdk/bin so the Go build
# and module caches persist between runs (first run downloads modules once;
# subsequent runs skip straight to compilation).
# CGO_ENABLED=0 yields a fully static binary that runs on the stock worker.
# USER/HOME must be set because the SDK calls user.Current() at init; with
# cgo disabled Go's pure-Go resolver reads those env vars instead of libc,
# and panics if either is empty (the same vars are set on the worker in
# go.yml so the packed binary runs the same way at execution time).
# `go tool airflow-go-pack` builds the bundle package, reads its
# --airflow-metadata, and appends the source + airflow-metadata.yaml + the
# AFBNDL01 trailer, writing a single self-contained executable bundle.
go_cache_home = "/repo/go-sdk/bin/.home"
bundle_out = f"/repo/go-sdk/bin/{GO_SDK_BUNDLE_NAME}"
console.print(f"[yellow]Building Go SDK example bundle ({GO_BUILDER_IMAGE})...")
subprocess.run(
[
"docker",
"run",
"--rm",
"--user",
f"{os.getuid()}:{os.getgid()}",
"-e",
f"HOME={go_cache_home}",
"-e",
"USER=airflow",
"-e",
"CGO_ENABLED=0",
# Mount the repo so the whole go-sdk module (go.mod, tool directive,
# example sources) is visible to `go tool`.
"-v",
f"{AIRFLOW_ROOT_PATH}:/repo",
"-w",
"/repo/go-sdk",
GO_BUILDER_IMAGE,
"go",
"tool",
"airflow-go-pack",
"--output",
bundle_out,
GO_SDK_EXAMPLE_BUNDLE_PKG,
],
check=True,
)

# Copy the compose override into the temp directory.
copyfile(GO_COMPOSE_PATH, tmp_dir / "go.yml")

# Place the packed bundle where the compose bind-mount (./go-bundles) exposes
# it to the worker at /opt/airflow/go-bundles. The bundle scanner requires
# the file to be executable, so preserve the exec bit.
go_bundles_dir = tmp_dir / "go-bundles"
go_bundles_dir.mkdir()
packed_bundle = go_bundles_dir / GO_SDK_BUNDLE_NAME
copyfile(GO_SDK_BIN_PATH / GO_SDK_BUNDLE_NAME, packed_bundle)
os.chmod(packed_bundle, 0o755)

# Copy the Go SDK example stub Dag so Airflow can discover and serialize it.
copyfile(GO_SDK_DAGS_PATH / "go_examples.py", tmp_dir / "dags" / "go_examples.py")

# Coordinator registry: maps the logical name "go-sdk" to ExecutableCoordinator,
# which scans executables_root for the packed bundle by dag_id.
# Queue mapping: routes tasks on the "golang" queue to "go-sdk".
coordinator_config = json.dumps(
{
"go-sdk": {
"classpath": "airflow.sdk.coordinators.executable.ExecutableCoordinator",
"kwargs": {"executables_root": ["/opt/airflow/go-bundles"]},
}
}
)
queue_to_coordinator = json.dumps({"golang": "go-sdk"})

dot_env_file.write_text(
f"AIRFLOW_UID={os.getuid()}\n"
# Single-quote the JSON values so Docker Compose reads them literally.
f"AIRFLOW__SDK__COORDINATORS='{coordinator_config}'\n"
f"AIRFLOW__SDK__QUEUE_TO_COORDINATOR='{queue_to_coordinator}'\n"
# Connection and variable read by the Go example bundle tasks.
"AIRFLOW_CONN_TEST_HTTP=http://test:test@example.com/\n"
"AIRFLOW_VAR_MY_VARIABLE=test_value\n"
)
os.environ["ENV_FILE_PATH"] = str(dot_env_file)


def spin_up_airflow_environment(tmp_path_factory: pytest.TempPathFactory):
tmp_dir = tmp_path_factory.mktemp("breeze-airflow-e2e-tests")

Expand Down Expand Up @@ -377,6 +483,9 @@ def spin_up_airflow_environment(tmp_path_factory: pytest.TempPathFactory):
elif E2E_TEST_MODE == "java_sdk":
compose_file_names.append("java.yml")
_setup_java_sdk_integration(dot_env_file, tmp_dir)
elif E2E_TEST_MODE == "go_sdk":
compose_file_names.append("go.yml")
_setup_go_sdk_integration(dot_env_file, tmp_dir)

#
# Please Do not use this Fernet key in any deployments! Please generate your own key.
Expand Down
17 changes: 17 additions & 0 deletions airflow-e2e-tests/tests/airflow_e2e_tests/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,23 @@
JAVA_COMPOSE_PATH = AIRFLOW_ROOT_PATH / "airflow-e2e-tests" / "docker" / "java.yml"
JAVA_DOCKERFILE_PATH = AIRFLOW_ROOT_PATH / "airflow-e2e-tests" / "docker" / "Dockerfile.java"

# Go SDK E2E test paths
GO_SDK_ROOT_PATH = AIRFLOW_ROOT_PATH / "go-sdk"
GO_SDK_DAGS_PATH = GO_SDK_ROOT_PATH / "dags"
# Package directory holding func main() for the example bundle; airflow-go-pack
# builds and packs this into a self-contained executable bundle.
GO_SDK_EXAMPLE_BUNDLE_PKG = "./example/bundle"
# Name of the packed bundle binary (matches the example bundle's package dir name).
GO_SDK_BUNDLE_NAME = "example_dags"
# Where airflow-go-pack writes the packed bundle inside the repo (go-sdk/bin is gitignored).
GO_SDK_BIN_PATH = GO_SDK_ROOT_PATH / "bin"
GO_COMPOSE_PATH = AIRFLOW_ROOT_PATH / "airflow-e2e-tests" / "docker" / "go.yml"
# Go toolchain image used to build the bundle; must satisfy go-sdk/go.mod's toolchain.
# The Alpine variant is ~7x smaller than the Debian one and is safe here because the
# bundle is built with CGO_ENABLED=0 (a fully static binary, independent of musl/glibc)
# and module fetches go through the HTTPS proxy (no git/gcc needed).
GO_BUILDER_IMAGE = os.environ.get("GO_BUILDER_IMAGE", "golang:1.24-alpine")

# Local provider sources are mounted into the airflow containers under this directory so
# ``_PIP_ADDITIONAL_REQUIREMENTS`` can install the in-tree (latest, possibly unreleased)
# provider rather than the published one from PyPI.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# 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.
Loading
Loading