Skip to content

Add AwsEksExecutor to the Amazon provider - #74399

Merged
o-nikolas merged 37 commits into
apache:mainfrom
aws-mwaa:amazon/eks-get-token-session
Oct 20, 2026
Merged

o-nikolas merged 37 commits into
apache:mainfrom
aws-mwaa:amazon/eks-get-token-session

Conversation

@seanghaeli

@seanghaeli seanghaeli commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Adds AwsEksExecutor, which runs each task as a pod on an Amazon EKS cluster. You select it with executor = airflow.providers.amazon.aws.executors.eks.AwsEksExecutor. It subclasses the Kubernetes executor and only replaces authentication. The Kubernetes client is built from an Airflow AWS connection with no kubeconfig, and the short-lived EKS token is re-minted in-process shortly before it expires. That avoids the subprocess-per-refresh path that has produced repeated credential-refresh bugs (#55195, #58743, #65212, #65335, #74306). On startup the executor checks that the cluster is usable and (unless check_health_on_startup is off) that it can list pods, so a missing access entry fails right away instead of showing up later as a watcher error loop. Settings live in a new [aws_eks_executor] section, and there's a new docs page.

The executor plugs its client builders into the client_factory and async_client_factory settings from #73014 itself, so users don't set those. If the installed cncf.kubernetes doesn't have that option, the executor raises an AirflowOptionalProviderFeatureException on import, naming the version it needs.

Because those settings are shared, a plain KubernetesExecutor in the same scope (both global, or the same team) would pick up the EKS client and send its pods to the EKS cluster. So the executor refuses to start in that setup and says how to fix the config. A global KubernetesExecutor next to a team AwsEksExecutor works fine. Letting a subclass bring its own client without going through the shared setting is tracked in #74844.

To mint the token with the connection's credentials, fetch_access_token_for_cluster now takes an optional session argument. The executor passes in the session from its EksHook. The argument defaults to None, which keeps today's behavior, so existing callers are unaffected. It no longer sets AWS_STS_REGIONAL_ENDPOINTS in-process, since botocore has defaulted to the regional endpoint since 1.40; only the eks_get_token command-line entry point still sets it, in its own short-lived process.

MIN_CNCF_KUBERNETES_VERSION is set to 10.24.0, the expected next cncf.kubernetes minor carrying #73014.

Testing

Unit tests in Breeze: executors/eks/, utils/test_eks_get_token.py and EksHook.generate_config_file: 58 passed.

System tests: On the system test dashboard, using this PR's branch, 60/60 system tests succeed using worker pods on a real EKS cluster.

Before, with the stock KubernetesExecutor:

kubernetes.config.config_exception.ConfigException: Service host/port is not set.
RUN e2e_first state=scheduler_exited

After, with AwsEksExecutor and async_pod_creation = True. The second run comes from the same scheduler process, after the first token has expired:

EKS Executor health check has succeeded.
RUN e2e_first state=success elapsed_since_scheduler_start=40s
scheduler pid 89 still alive at 1090s, triggering e2e_after_token_expiry
RUN e2e_after_token_expiry state=success elapsed_since_scheduler_start=1111s

With executor = AwsEksExecutor,KubernetesExecutor, where the Kubernetes executor has its own kubeconfig. Before the guard, the Kubernetes executor never used its kubeconfig and silently ran on the EKS client:

KE exec plugin calls: 0

After, the scheduler refuses to start:

ValueError: AwsEksExecutor can't run alongside KubernetesExecutor in the global scope. AwsEksExecutor sets the [kubernetes_executor] client factory for the global scope, so KubernetesExecutor would silently send its pods to the EKS cluster. Configure the two executors for different teams, or don't use them together. See https://github.com/apache/airflow/issues/74844

And executor = KubernetesExecutor;team_a=AwsEksExecutor still starts, with the Kubernetes executor on its own kubeconfig:

EKS Executor health check has succeeded.
RUN e2e_first state=success elapsed_since_scheduler_start=40s
KE exec plugin calls: 2

Docs

EKS executor docs
EKS executor docs page

EKS executor multi-team deployments
Multi-team deployments, including the limit on running it next to a Kubernetes executor in the same scope

EKS executor config options
EKS executor config options

Amazon executors page
Amazon executors page (the "Choosing an executor" table is gone)


Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

Generated-by: Claude Code (claude-fable-5) following the guidelines

@boring-cyborg boring-cyborg Bot added area:providers provider:amazon AWS/Amazon - related issues labels Oct 7, 2026
@seanghaeli
seanghaeli marked this pull request as ready for review October 7, 2026 17:18
@seanghaeli
seanghaeli requested a review from o-nikolas as a code owner October 7, 2026 17:18
Callers that already hold a boto3 session (with its credentials and region) can pass it in instead of having the helper build a new one from the environment.
A KubernetesExecutor subclass that runs tasks as pods on an Amazon EKS cluster. It supplies its own Kubernetes client through the cncf.kubernetes client_factory and async_client_factory settings, built from an Airflow AWS connection with the EKS token re-minted in-process, and checks cluster access at startup. The factories live in a private _client_factory module.
@seanghaeli
seanghaeli force-pushed the amazon/eks-get-token-session branch from bb6fed2 to 86701fb Compare October 7, 2026 21:57
@seanghaeli seanghaeli changed the title Let fetch_access_token_for_cluster reuse a caller's boto3 session Add AwsEksExecutor to the Amazon provider Oct 7, 2026
… clients

kubernetes 35 has no alias kwarg on get_api_key_with_prefix and
kubernetes_asyncio 32 has a synchronous auth_settings(), so assert through
each client's own auth_settings() instead.
Add an eks/utils.py with CONFIG_GROUP_NAME, CONFIG_DEFAULTS and
AllEksConfigKeys like ecs/batch/lambda, add check_health_on_startup and
a public check_health() with the same startup flow and messages, order
the config options like the other sections, and use the shared logging
include and option-list style in the docs.
Checks that AllEksConfigKeys and CONFIG_DEFAULTS stay in step with the [aws_eks_executor] section in provider.yaml.
Comment thread providers/amazon/docs/executors/eks-executor.rst
Comment thread providers/amazon/docs/executors/eks-executor.rst Outdated
Comment thread providers/amazon/docs/executors/eks-executor.rst Outdated
Comment thread providers/amazon/docs/executors/eks-executor.rst Outdated
Comment thread providers/amazon/docs/executors/eks-executor.rst Outdated
Comment thread providers/amazon/src/airflow/providers/amazon/aws/executors/eks/eks_executor.py Outdated
Comment thread providers/amazon/src/airflow/providers/amazon/aws/executors/eks/eks_executor.py Outdated
Raise AirflowOptionalProviderFeatureException naming the minimum
cncf.kubernetes version at import time, for both a missing and a too-old
provider, instead of checking it in the executor constructor, and drop
the docs paragraph that described the check.
The executor accepts ACTIVE and UPDATING clusters, but the error for any
other status only named ACTIVE.
kubernetes 36 reads the bearer token from the BearerToken api_key, older
releases from authorization, and kubernetes_asyncio has always used
BearerToken. Set the one key each client reads instead of both, so the
branch can go once the minimum kubernetes version is 36.
Use Sphinx references for the config option, the set-config page and
the multi-team page, link the boto3 credentials guide and the EKS update
docs instead of describing them, and say what the startup check catches.
Use one message, naming only the minimum cncf.kubernetes version, for both
the missing and the too-old provider, and inline the version since nothing
else reads MIN_CNCF_KUBERNETES_VERSION.
Name the verbs on pods and pods/log, matching the Helm chart's pod-launcher
role, in both the health check error and the 'Grant access to the cluster'
docs step, and drop the docs sentence on AWS credential refresh, which works
the same as for every Amazon hook.
Set the team-scoped [kubernetes_executor] client_factory and
async_client_factory for a team executor, since cncf.kubernetes looks a
team's factory up in the team's own config only. Teams share the cluster
from [aws_eks_executor], as KubernetesExecutor teams share its default
client, and get their own team-scoped pod settings.
The installed kubernetes version cannot change at runtime, so compute the
api_key name for the bearer token at module level instead of on every
client build, and test the version check directly.
Tell the user where to set cluster_name when it is missing, and what to do
when the cluster is not in a usable state. Also correct the comment on
refused cluster statuses, which left out PENDING.
The Batch executor refuses EKS jobs, so drop EKS from its compute options.
An unset EKS region_name falls back to the AWS connection's region before
boto3's. Worker pods are only deleted when their task succeeds, since
failed pods are kept by default.
Link the [kubernetes_executor] and [aws_eks_executor] options, pod_override
and pod templates to their reference docs, link the EKS access entry and
aws-auth ConfigMap guides, and point the shared logging section at the
logging docs with :doc: rather than hard-coded stable URLs.
seanghaeli and others added 5 commits October 9, 2026 00:27
The refresh hook runs before every Kubernetes API request. Like the
kubernetes client's in-cluster config, only get a new token when the
cached one is about to expire, a minute before the 15 minute lifetime
(TOKEN_EXPIRATION_MINUTES), instead of signing a new one each time.
@seanghaeli

Copy link
Copy Markdown
Contributor Author

As of today 60/60 system tests are successfully running on the AWS system test dashboard using the EksExecutor

@seanghaeli

seanghaeli commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor Author

Using the Eks Executor depends on cncf.kubernetes >= 10.24.0 but I haven't bumped the version floor in this PR, and instead just throw an error if the user is on an older version of cncf. @vincbeck do you agree with this, or prefer that I bump the version floor?

^ Nevermind, question answered. I'm already following the approach Niko recommend (and you agreed with) here

#72542 (review)

@seanghaeli

Copy link
Copy Markdown
Contributor Author

@eladkal Any thoughts? Now that it's working e2e, I'm happy to merge this

@o-nikolas o-nikolas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a big one! Submitting my comments for the docs now. Will have to circle back later for the code and tests.

Comment thread providers/amazon/docs/executors/eks-executor.rst
Comment thread providers/amazon/docs/executors/eks-executor.rst Outdated
Comment thread providers/amazon/docs/executors/eks-executor.rst Outdated
Comment thread providers/amazon/docs/executors/eks-executor.rst Outdated
Comment thread providers/amazon/docs/executors/index.rst Outdated
Comment thread providers/amazon/docs/executors/index.rst Outdated
With no pod template, no pod_override image and no
worker_container_repository/tag, the worker pod has no image and the
API server rejects it with a bare 422. Check the built pod before it is
sent and fail the task with an error naming the three ways to set one.
@seanghaeli

Copy link
Copy Markdown
Contributor Author

Thanks @o-nikolas , addressed your docs comments!

The client factory deleted the variable after resolving the STS endpoint,
dropping any value the user had set, and every token fetch then set it to
"regional" for good. Both now set it only while they need it and put the
previous value back.
…same scope

AwsEksExecutor installs its client factory through the [kubernetes_executor]
client_factory env vars, so a plain KubernetesExecutor in the same scope (both
global, or the same team) picks it up and silently sends its pods to the EKS
cluster. Fail at construction instead, with a message that says how to fix the
config. A global KubernetesExecutor next to a team AwsEksExecutor is unaffected.

The proper fix is tracked in apache#74844.
seanghaeli added a commit to aws-mwaa/upstream-to-airflow that referenced this pull request Oct 16, 2026
The executor reads [aws_eks_executor] globally, so a team's own cluster_name,
region_name or conn_id would be silently ignored and its pods sent to the
global cluster. Fail at construction until per-team clusters land (apache#74844).
Every API server log read builds a new client, and each one left another
CA temp file behind. Reuse the file written for a CA, and remove it when
the process exits, like the kubernetes kubeconfig loader does.
botocore has defaulted sts_regional_endpoints to "regional" since 1.40.0,
and the amazon provider requires botocore 1.42.79 or newer, so setting
AWS_STS_REGIONAL_ENDPOINTS while building the client is not needed. It
was also unsafe: the env var is shared by the whole process, so threads
building clients at the same time, as in the API server, could race on
it. The eks_get_token CLI still sets it, since it runs in its own
short-lived process.
seanghaeli added a commit to aws-mwaa/upstream-to-airflow that referenced this pull request Oct 19, 2026
Rendered from 35686a9; the Requirements section now includes the botocore note.

@o-nikolas o-nikolas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, work! Just a few more nits

Comment thread providers/amazon/src/airflow/providers/amazon/get_provider_info.py Outdated
Comment thread providers/amazon/src/airflow/providers/amazon/aws/executors/eks/eks_executor.py Outdated
Comment thread providers/amazon/src/airflow/providers/amazon/aws/executors/eks/eks_executor.py Outdated
Reverts bca7914. The check isn't specific to EKS, and overriding cncf's
private _build_pod_request is brittle, so it moves to cncf instead. A pod with
no image now fails with the API server's own error.
@o-nikolas
o-nikolas merged commit 3134ac2 into apache:main Oct 20, 2026
72 checks passed
@o-nikolas
o-nikolas deleted the amazon/eks-get-token-session branch October 20, 2026 01:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providers provider:amazon AWS/Amazon - related issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants