Repository navigation
feat: update to support airflow 3 #36
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
Open
botanical
wants to merge
2
commits into
main
Choose a base branch
from
feat/update-to-support-airflow3
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| # README | ||
|
|
||
| The workflows read config from two GitHub Environments (under Settings -> Environments): `staging` and `production`. A job only sees the secrets and vars of the environment it declares. The same secret name, therefore, can hold a different value in each stage. | ||
|
|
||
| ## Which workflow uses which environment | ||
|
|
||
| - `staging` is used by [pr.yml](../.github/workflows/pr.yml). It runs when a PR is opened or updated and publishes to staging Airflow. | ||
| - `production` is used by [promote.yml](../.github/workflows/promote.yml) which [promotion-checker.yml](../.github/workflows/promotion-checker.yml) triggers after the PR is approved. It publishes to production Airflow. | ||
|
|
||
| For the GitHub `staging` environment, the following config should be set: | ||
|
|
||
| - Secrets, for a staging instance using Airflow 2, `STAGING_SM2A_ADMIN_USERNAME` and `STAGING_SM2A_ADMIN_PASSWORD`. | ||
| - Secrets, for a staging instance using Airflow 3, `AIRFLOW_JWT_SECRET` and `AIRFLOW_JWT_SUB`. | ||
| - Secret for the MDX PR step: `APP_PEM`. | ||
| - Vars: `STAGING_SM2A_API_URL`, `STAGING_AIRFLOW_API_VERSION`, `DATASET_DAG_NAME`, and `ARTIFACT_RETENTION_DAYS`. | ||
| - Vars for the MDX step: `SKIP_MDX_PR_STEP`, `VEDA_CONFIG_REPO_ORG`, `VEDA_CONFIG_REPO_NAME`, `VEDA_CONFIG_APP_ID`, and `GH_ACTOR_EMAIL`. | ||
|
|
||
| For the GitHub `production` environment, the following config should be set: | ||
|
|
||
| - Secrets, for a production instance using Airflow 2, `SM2A_ADMIN_USERNAME` and `SM2A_ADMIN_PASSWORD`. | ||
| - Secrets, for a production instance using Airflow 3, `AIRFLOW_JWT_SECRET` and `AIRFLOW_JWT_SUB` *which should be different values from what is set in staging*. | ||
| - Vars: `SM2A_API_URL`, `PRODUCTION_AIRFLOW_API_VERSION` and `PROMOTION_DAG_NAME`. | ||
|
|
||
| > Note: `*_AIRFLOW_API_VERSION` is the Airflow major version (can be either 2 or 3) which defaults to 2 if unset. The JWT secrets **are only needed for Airflow 3**. | ||
|
|
||
| ### Repo-level secrets | ||
|
|
||
| Set repo level secrets under `Settings` -> `Secrets and variables` -> `Actions`. | ||
|
|
||
| - `WORKFLOW_TRIGGER_TOKEN` which is a repo-level secret that isn't environment scoped. It is required for `promote.yml`. | ||
|
|
||
| ## Environment Variables Reference for Collection and Dataset Promotion | ||
|
|
||
| > See the [terminology note](../README.md#step-4-promote-to-production) in the root README for what "promotion" implies. | ||
| > | ||
|
|
||
| | Variable | Set in | Used by | Default | Notes | | ||
| | -- | -- | -- | -- | -- | | ||
| | STAGING_AIRFLOW_API_VERSION | Staging GitHub Env | Staging Publish/Promotion | 2 | The Airflow Version for the Staging Environment. For Airflow 2 = API v1 + Basic auth, Airflow 3 = API v2 + JWT | | ||
| | PRODUCTION_AIRFLOW_API_VERSION | Production GitHub Env | Production Promotion | 2 | Same as above | | ||
| | AIRFLOW_JWT_SECRET | Both (optional) | Any Publish/Promotion step that uses Airflow 3 | none | Conditionally required when the resolved API version is 3. Must match the secret that the Airflow 3 deployment signs with. Can be found in Secrets Manager | | ||
| | AIRFLOW_JWT_SUB | Both (optional) | Airflow 3 only | none | Conditionally required when the resolved API version is 3. The subject claim for the token (the Integer ID from Airflow's user table) | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| """ | ||
| Supports Airflow 2 and 3 (API v1 / v2) | ||
| """ | ||
|
|
||
| import os | ||
| import uuid | ||
| import json | ||
| import http.client | ||
|
|
||
| from typing import Dict, Any | ||
| from base64 import b64encode | ||
|
|
||
|
|
||
| class AirflowAPIError(Exception): | ||
| def __init__(self, msg, status_code=None): | ||
| super().__init__(msg) | ||
| self.status_code = status_code | ||
|
|
||
| pass | ||
|
|
||
|
|
||
| def _build_request_body( | ||
| conf: Dict[str, Any], dag_id: str, note: str = "", api_version: str = "2" | ||
| ) -> Dict[str, Any]: | ||
| """Build the request body for the DagRun""" | ||
| run_id = os.getenv("GITHUB_RUN_ID") | ||
| if run_id: | ||
| run_url = ( | ||
| f"{os.getenv('GITHUB_SERVER_URL')}/{os.getenv('GITHUB_REPOSITORY')}" | ||
| f"/actions/runs/{run_id}" | ||
| ) | ||
| default_note = f"Run from GitHub Actions: {run_url}" | ||
| else: | ||
| default_note = "Run from GitHub Actions veda-data workflow" | ||
|
|
||
| body = { | ||
| "conf": conf, | ||
| "dag_run_id": f"{dag_id}-{uuid.uuid4()}", | ||
| "note": note or default_note, | ||
| } | ||
| if api_version != "2": | ||
| body["logical_date"] = None | ||
| return body | ||
|
|
||
|
|
||
| def _generate_jwt_token(secret: str, sub: str, expiration_time: int = 3600) -> str: | ||
| """Generate a HS512 JWT Token for Airflow API authentication""" | ||
| import jwt | ||
| import time | ||
|
|
||
| payload = { | ||
| "iss": "airflow", | ||
| "sub": sub, | ||
| "aud": "apache-airflow", | ||
| "nbf": int(time.time()), | ||
| "iat": int(time.time()), | ||
| "exp": int(time.time()) + expiration_time, | ||
| } | ||
| return jwt.encode(payload, secret, algorithm="HS512") | ||
|
|
||
|
|
||
| def trigger_dag_run( | ||
| base_api_url: str, | ||
| dag_id: str, | ||
| conf: Dict[str, Any], | ||
| username: str, | ||
| password: str, | ||
| api_version: str = None, | ||
| ) -> Dict[str, Any]: | ||
| """ | ||
| Trigger a DAG run with version-aware API handling | ||
|
|
||
| Raises: AirflowAPIError if it fails | ||
| """ | ||
|
|
||
| if api_version is None: | ||
| api_version = os.getenv("AIRFLOW_API_VERSION") or "2" | ||
|
|
||
| api_path = f"/api/v{'1' if api_version == '2' else '2'}/dags/{dag_id}/dagRuns" | ||
| request_body = _build_request_body(conf, dag_id, api_version=api_version) | ||
|
|
||
| if api_version == "2": | ||
| api_token = b64encode(f"{username}:{password}".encode()).decode() | ||
| headers = { | ||
| "Content-Type": "application/json", | ||
| "Authorization": "Basic " + api_token, | ||
| } | ||
| else: | ||
| jwt_secret = os.getenv("AIRFLOW_JWT_SECRET") | ||
| if not jwt_secret: | ||
| raise AirflowAPIError("AIRFLOW_JWT_SECRET environment variable not set") | ||
| jwt_sub = os.getenv("AIRFLOW_JWT_SUB") | ||
| if not jwt_sub: | ||
| raise AirflowAPIError("AIRFLOW_JWT_SUB environment variable not set") | ||
| access_token = _generate_jwt_token(jwt_secret, jwt_sub) | ||
| headers = { | ||
| "Content-Type": "application/json", | ||
| "Authorization": f"Bearer {access_token}", | ||
| } | ||
|
|
||
| try: | ||
| http_conn = http.client.HTTPSConnection(base_api_url) | ||
| http_conn.request("POST", api_path, json.dumps(request_body), headers) | ||
| response = http_conn.getresponse() | ||
| response_data = response.read() | ||
| http_conn.close() | ||
|
|
||
| if response.status >= 400: | ||
| raise AirflowAPIError( | ||
| f"Airflow API v{api_version} returns " | ||
| f"{response.status}: {response_data.decode()}", | ||
| status_code=response.status, | ||
| ) | ||
|
|
||
| return { | ||
| "statusCode": response.status, | ||
| "body": response_data.decode(), | ||
| } | ||
|
|
||
| except AirflowAPIError: | ||
| raise | ||
| except Exception as e: | ||
| raise AirflowAPIError(f"Failed to trigger the DAG run: {str(e)}") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
docs comments posted on veda-data pull request