Repository navigation
Add AwsEksExecutor to the Amazon provider - #74399
Conversation
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.
bb6fed2 to
86701fb
Compare
… 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.
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.
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.
|
As of today 60/60 system tests are successfully running on the AWS system test dashboard using the EksExecutor |
|
^ Nevermind, question answered. I'm already following the approach Niko recommend (and you agreed with) here |
|
@eladkal Any thoughts? Now that it's working e2e, I'm happy to merge this |
o-nikolas
left a comment
There was a problem hiding this comment.
This is a big one! Submitting my comments for the docs now. Will have to circle back later for the code and tests.
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.
|
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.
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.
Rendered from 35686a9; the Requirements section now includes the botocore note.
o-nikolas
left a comment
There was a problem hiding this comment.
Nice, work! Just a few more nits
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.
Adds
AwsEksExecutor, which runs each task as a pod on an Amazon EKS cluster. You select it withexecutor = 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 (unlesscheck_health_on_startupis 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_factoryandasync_client_factorysettings from #73014 itself, so users don't set those. If the installedcncf.kubernetesdoesn't have that option, the executor raises anAirflowOptionalProviderFeatureExceptionon import, naming the version it needs.Because those settings are shared, a plain
KubernetesExecutorin 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 globalKubernetesExecutornext to a teamAwsEksExecutorworks 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_clusternow takes an optionalsessionargument. The executor passes in the session from itsEksHook. The argument defaults toNone, which keeps today's behavior, so existing callers are unaffected. It no longer setsAWS_STS_REGIONAL_ENDPOINTSin-process, since botocore has defaulted to the regional endpoint since 1.40; only theeks_get_tokencommand-line entry point still sets it, in its own short-lived process.MIN_CNCF_KUBERNETES_VERSIONis set to10.24.0, the expected nextcncf.kubernetesminor carrying #73014.Testing
Unit tests in Breeze:
executors/eks/,utils/test_eks_get_token.pyandEksHook.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:After, with
AwsEksExecutorandasync_pod_creation = True. The second run comes from the same scheduler process, after the first token has expired: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:After, the scheduler refuses to start:
And
executor = KubernetesExecutor;team_a=AwsEksExecutorstill starts, with the Kubernetes executor on its own kubeconfig:Docs
EKS executor docs page
Multi-team deployments, including the limit on running it next to a Kubernetes executor in the same scope
EKS executor config options
Amazon executors page (the "Choosing an executor" table is gone)
Was generative AI tooling used to co-author this PR?
Generated-by: Claude Code (claude-fable-5) following the guidelines