fix: avoid global k8s Configuration singleton race in read_config_map#764
Open
fix: avoid global k8s Configuration singleton race in read_config_map#764
Conversation
Each call to read_config_map was mutating the kubernetes_asyncio global Configuration._default via load_incluster_config(). Concurrent async requests could race each other, leaving the global transiently empty so that CoreV1Api() would construct an ApiClient with no host/token → 401. Fix by passing an explicit client_configuration object to load_incluster_config/load_kube_config and binding ApiClient to it directly, so no shared global state is touched. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
dmchoiboi
approved these changes
Feb 24, 2026
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
configmap.py::read_config_mapcalledkube_config.load_incluster_config()on every request, mutating thekubernetes_asyncioglobalConfiguration._defaultsingletonCoreV1Api()to construct anApiClientfrom a transiently empty global config (no host, no token) → 401 Unauthorizedclient.Configuration()per call, pass it toload_incluster_config/load_kube_config, and bindApiClientto it directly — the global singleton is never touchedTest plan
inference_framework_image_tagis unset (triggers_get_latest_tag→read_config_map)🤖 Generated with Claude Code
Greptile Summary
This PR fixes a concurrency race condition in
read_config_mapwhere callingkube_config.load_incluster_config()on every request mutated the globalConfiguration._defaultsingleton. Under concurrent async requests,CoreV1Api()could construct anApiClientfrom a transiently empty global config (no host, no token), resulting in 401 Unauthorized errors.Key changes:
client.Configuration()instance per call instead of relying on the mutable global singletonclient_configuration=configurationto bothload_incluster_configandload_kube_configasync with client.ApiClient(configuration)to scope the API client lifecycle and ensure proper cleanupCoreV1Apito the explicitapi_clientrather than the global defaultNote: The same global-singleton pattern still exists in
k8s_endpoint_resource_delegate.py:maybe_load_kube_config(),core/docker/remote_build.py,k8s_startup_watcher/watcher.py, andentrypoints/k8s_cache.py. These may be less susceptible since some use a load-once guard (_kube_config_loadedflag), but the pattern could be worth fixing in those locations too for consistency.Confidence Score: 5/5
Important Files Changed
async withfor ApiClient cleanup.Sequence Diagram
sequenceDiagram participant Caller as Caller (e.g. _get_latest_tag) participant RCM as read_config_map() participant Cfg as client.Configuration() participant KubeConfig as kube_config participant ApiClient as client.ApiClient participant CoreV1 as CoreV1Api participant K8sAPI as K8s API Server Caller->>RCM: read_config_map(name, namespace) RCM->>Cfg: new Configuration() (per-call instance) RCM->>KubeConfig: load_incluster_config(client_configuration=cfg) KubeConfig-->>Cfg: populates host, token, certs RCM->>ApiClient: async with ApiClient(cfg) ApiClient->>CoreV1: CoreV1Api(api_client) CoreV1->>K8sAPI: read_namespaced_config_map(name, namespace) K8sAPI-->>CoreV1: config_map response CoreV1-->>RCM: config_map.data RCM-->>Caller: Dict[str, str] Note over RCM,ApiClient: ApiClient cleaned up via async context managerLast reviewed commit: 0ae4260