kubectl-mcp-server 1.19.0__py3-none-any.whl → 1.19.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,14 +6,214 @@ Supports multi-cluster operations with context targeting.
6
6
 
7
7
  This module provides context-aware client creation for multi-cluster support.
8
8
  All get_*_client() functions accept an optional 'context' parameter.
9
+
10
+ Environment Variables:
11
+ MCP_K8S_PROVIDER: Provider type (kubeconfig, in-cluster, single)
12
+ MCP_K8S_KUBECONFIG: Path to kubeconfig file
13
+ MCP_K8S_CONTEXT: Default context for single provider
14
+ MCP_K8S_QPS: API rate limit (default: 100)
15
+ MCP_K8S_BURST: API burst limit (default: 200)
16
+ MCP_K8S_TIMEOUT: Request timeout in seconds (default: 30)
17
+ MCP_STATELESS_MODE: If "true", don't cache API clients (reload config each request)
18
+ MCP_KUBECONFIG_WATCH: If "true", auto-detect kubeconfig file changes
9
19
  """
10
20
 
11
21
  import os
12
22
  import logging
13
- from typing import Optional, Any
23
+ import threading
24
+ import time
25
+ from typing import Optional, Any, List, Dict, Callable
14
26
 
15
27
  logger = logging.getLogger("mcp-server")
16
28
 
29
+ _stateless_mode = os.environ.get("MCP_STATELESS_MODE", "").lower() in ("true", "1", "yes")
30
+ _kubeconfig_watcher: Optional["KubeconfigWatcher"] = None
31
+ _kubeconfig_last_mtime: Dict[str, float] = {}
32
+ _config_change_callbacks: List[Callable[[], None]] = []
33
+
34
+
35
+ class KubeconfigWatcher:
36
+ """Watch kubeconfig files for changes and trigger reloads.
37
+
38
+ This class monitors kubeconfig files and automatically invalidates
39
+ cached configurations when changes are detected.
40
+ """
41
+
42
+ def __init__(self, check_interval: float = 5.0):
43
+ """Initialize the kubeconfig watcher.
44
+
45
+ Args:
46
+ check_interval: How often to check for changes (seconds)
47
+ """
48
+ self._check_interval = check_interval
49
+ self._running = False
50
+ self._thread: Optional[threading.Thread] = None
51
+ self._watched_files: Dict[str, float] = {}
52
+ self._lock = threading.Lock()
53
+
54
+ def start(self):
55
+ """Start watching for kubeconfig changes."""
56
+ if self._running:
57
+ return
58
+
59
+ self._running = True
60
+ self._thread = threading.Thread(target=self._watch_loop, daemon=True)
61
+ self._thread.start()
62
+ logger.info("Kubeconfig watcher started")
63
+
64
+ def stop(self):
65
+ """Stop watching for kubeconfig changes."""
66
+ self._running = False
67
+ if self._thread:
68
+ self._thread.join(timeout=2.0)
69
+ self._thread = None
70
+ logger.info("Kubeconfig watcher stopped")
71
+
72
+ def add_file(self, filepath: str):
73
+ """Add a file to watch.
74
+
75
+ Args:
76
+ filepath: Path to kubeconfig file
77
+ """
78
+ filepath = os.path.expanduser(filepath)
79
+ if os.path.exists(filepath):
80
+ with self._lock:
81
+ self._watched_files[filepath] = os.path.getmtime(filepath)
82
+
83
+ def _watch_loop(self):
84
+ """Main watch loop - check for file changes periodically."""
85
+ while self._running:
86
+ try:
87
+ self._check_files()
88
+ except Exception as e:
89
+ logger.debug(f"Kubeconfig watch error: {e}")
90
+ time.sleep(self._check_interval)
91
+
92
+ def _check_files(self):
93
+ """Check all watched files for changes."""
94
+ with self._lock:
95
+ for filepath, last_mtime in list(self._watched_files.items()):
96
+ try:
97
+ if not os.path.exists(filepath):
98
+ continue
99
+
100
+ current_mtime = os.path.getmtime(filepath)
101
+ if current_mtime != last_mtime:
102
+ self._watched_files[filepath] = current_mtime
103
+ logger.info(f"Kubeconfig changed: {filepath}")
104
+ self._on_config_changed()
105
+ except Exception as e:
106
+ logger.debug(f"Error checking {filepath}: {e}")
107
+
108
+ def _on_config_changed(self):
109
+ """Handle kubeconfig file change - invalidate caches."""
110
+ global _config_loaded
111
+ _config_loaded = False
112
+
113
+ if _HAS_PROVIDER:
114
+ try:
115
+ provider = get_provider()
116
+ if hasattr(provider, 'invalidate_cache'):
117
+ provider.invalidate_cache()
118
+ except Exception:
119
+ pass
120
+
121
+ for callback in _config_change_callbacks:
122
+ try:
123
+ callback()
124
+ except Exception as e:
125
+ logger.debug(f"Config change callback error: {e}")
126
+
127
+
128
+ def enable_kubeconfig_watch(check_interval: float = 5.0):
129
+ """Enable automatic kubeconfig file watching.
130
+
131
+ When enabled, the server will automatically detect changes to kubeconfig
132
+ files and reload the configuration. This is useful when:
133
+ - Cloud provider CLIs update credentials (aws, gcloud, az)
134
+ - Users switch contexts using external tools
135
+ - Kubeconfig files are mounted dynamically (e.g., in Kubernetes)
136
+
137
+ Args:
138
+ check_interval: How often to check for changes (seconds)
139
+
140
+ Example:
141
+ enable_kubeconfig_watch(check_interval=10.0)
142
+ """
143
+ global _kubeconfig_watcher
144
+
145
+ if _kubeconfig_watcher is not None:
146
+ return
147
+
148
+ _kubeconfig_watcher = KubeconfigWatcher(check_interval=check_interval)
149
+ kubeconfig_env = os.environ.get('KUBECONFIG', '~/.kube/config')
150
+ for path in kubeconfig_env.split(os.pathsep):
151
+ _kubeconfig_watcher.add_file(path)
152
+ _kubeconfig_watcher.start()
153
+
154
+
155
+ def disable_kubeconfig_watch():
156
+ """Disable kubeconfig file watching."""
157
+ global _kubeconfig_watcher
158
+
159
+ if _kubeconfig_watcher is not None:
160
+ _kubeconfig_watcher.stop()
161
+ _kubeconfig_watcher = None
162
+
163
+
164
+ def on_config_change(callback: Callable[[], None]):
165
+ """Register a callback to be called when kubeconfig changes.
166
+
167
+ Args:
168
+ callback: Function to call when config changes
169
+ """
170
+ _config_change_callbacks.append(callback)
171
+
172
+
173
+ def is_stateless_mode() -> bool:
174
+ """Check if stateless mode is enabled.
175
+
176
+ In stateless mode, API clients are not cached and configuration
177
+ is reloaded on each request. This is useful for:
178
+ - Serverless environments (Lambda, Cloud Functions)
179
+ - Environments where credentials may change frequently
180
+ - Security-conscious deployments
181
+
182
+ Returns:
183
+ True if stateless mode is enabled
184
+ """
185
+ return _stateless_mode
186
+
187
+
188
+ def set_stateless_mode(enabled: bool):
189
+ """Enable or disable stateless mode.
190
+
191
+ Args:
192
+ enabled: True to enable stateless mode
193
+ """
194
+ global _stateless_mode
195
+ _stateless_mode = enabled
196
+ if enabled:
197
+ logger.info("Stateless mode enabled - API clients will not be cached")
198
+ else:
199
+ logger.info("Stateless mode disabled - API clients will be cached")
200
+
201
+ try:
202
+ from .providers import (
203
+ KubernetesProvider,
204
+ ProviderConfig,
205
+ ProviderType,
206
+ UnknownContextError,
207
+ get_provider,
208
+ get_context_names,
209
+ get_current_context as provider_get_current_context,
210
+ validate_context,
211
+ )
212
+ _HAS_PROVIDER = True
213
+ except ImportError:
214
+ _HAS_PROVIDER = False
215
+ logger.debug("Provider module not available, using basic config")
216
+
17
217
  _config_loaded = False
18
218
  _original_load_kube_config = None
19
219
 
@@ -35,7 +235,6 @@ def load_kubernetes_config(context: str = ""):
35
235
  from kubernetes import config
36
236
  from kubernetes.config.config_exception import ConfigException
37
237
 
38
- # Try in-cluster config first (for pods running in Kubernetes)
39
238
  try:
40
239
  config.load_incluster_config()
41
240
  logger.info("Loaded in-cluster Kubernetes configuration")
@@ -44,7 +243,6 @@ def load_kubernetes_config(context: str = ""):
44
243
  except ConfigException:
45
244
  logger.debug("Not running in-cluster, trying kubeconfig...")
46
245
 
47
- # Fall back to kubeconfig file
48
246
  try:
49
247
  kubeconfig_path = os.environ.get('KUBECONFIG', '~/.kube/config')
50
248
  kubeconfig_path = os.path.expanduser(kubeconfig_path)
@@ -72,7 +270,6 @@ def _patched_load_kube_config(*args, **kwargs):
72
270
 
73
271
  from kubernetes.config.config_exception import ConfigException
74
272
 
75
- # Try in-cluster config first
76
273
  try:
77
274
  from kubernetes import config
78
275
  config.load_incluster_config()
@@ -82,7 +279,6 @@ def _patched_load_kube_config(*args, **kwargs):
82
279
  except ConfigException:
83
280
  pass
84
281
 
85
- # Fall back to original behavior
86
282
  if _original_load_kube_config:
87
283
  _original_load_kube_config(*args, **kwargs)
88
284
  _config_loaded = True
@@ -106,7 +302,6 @@ def patch_kubernetes_config():
106
302
  logger.debug("kubernetes package not available for patching")
107
303
 
108
304
 
109
- # Auto-patch when this module is imported
110
305
  patch_kubernetes_config()
111
306
 
112
307
 
@@ -114,23 +309,37 @@ def _load_config_for_context(context: str = "") -> Any:
114
309
  """
115
310
  Load kubernetes config for a specific context and return ApiClient.
116
311
 
312
+ Uses the provider module for caching when available, unless stateless
313
+ mode is enabled.
314
+
117
315
  Args:
118
316
  context: Context name (empty for default)
119
317
 
120
318
  Returns:
121
319
  kubernetes.client.ApiClient configured for the context
320
+
321
+ Raises:
322
+ UnknownContextError: If context is not found (when provider available)
323
+ RuntimeError: If config cannot be loaded
122
324
  """
325
+ if not _stateless_mode and _HAS_PROVIDER:
326
+ try:
327
+ provider = get_provider()
328
+ return provider.get_api_client(context)
329
+ except UnknownContextError:
330
+ raise
331
+ except Exception as e:
332
+ logger.warning(f"Provider failed, falling back to basic config: {e}")
333
+
123
334
  from kubernetes import client, config
124
335
  from kubernetes.config.config_exception import ConfigException
125
336
 
126
- # Try in-cluster first
127
337
  try:
128
338
  config.load_incluster_config()
129
339
  return client.ApiClient()
130
340
  except ConfigException:
131
341
  pass
132
342
 
133
- # Load kubeconfig with optional context
134
343
  kubeconfig_path = os.environ.get('KUBECONFIG', '~/.kube/config')
135
344
  kubeconfig_path = os.path.expanduser(kubeconfig_path)
136
345
 
@@ -151,309 +360,119 @@ def _load_config_for_context(context: str = "") -> Any:
151
360
  return client.ApiClient(configuration=api_config)
152
361
 
153
362
 
154
- def get_k8s_client(context: str = ""):
155
- """Get a configured Kubernetes Core API client.
156
-
157
- Args:
158
- context: Optional context name for multi-cluster support
159
-
160
- Returns:
161
- kubernetes.client.CoreV1Api: Configured Kubernetes client
162
-
163
- Raises:
164
- RuntimeError: If Kubernetes config cannot be loaded
165
- """
363
+ def _get_client(context: str, client_class):
364
+ """Helper to create a configured Kubernetes API client."""
166
365
  from kubernetes import client
167
-
168
366
  try:
169
367
  api_client = _load_config_for_context(context)
170
- return client.CoreV1Api(api_client=api_client)
368
+ return client_class(api_client=api_client)
171
369
  except Exception as e:
172
370
  raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
173
371
 
174
372
 
175
- def get_apps_client(context: str = ""):
176
- """Get a configured Kubernetes Apps API client.
177
-
178
- Args:
179
- context: Optional context name for multi-cluster support
373
+ def get_k8s_client(context: str = ""):
374
+ """Get a configured Kubernetes Core API client."""
375
+ from kubernetes import client
376
+ return _get_client(context, client.CoreV1Api)
180
377
 
181
- Returns:
182
- kubernetes.client.AppsV1Api: Configured Apps API client
183
378
 
184
- Raises:
185
- RuntimeError: If Kubernetes config cannot be loaded
186
- """
379
+ def get_apps_client(context: str = ""):
380
+ """Get a configured Kubernetes Apps API client."""
187
381
  from kubernetes import client
188
-
189
- try:
190
- api_client = _load_config_for_context(context)
191
- return client.AppsV1Api(api_client=api_client)
192
- except Exception as e:
193
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
382
+ return _get_client(context, client.AppsV1Api)
194
383
 
195
384
 
196
385
  def get_rbac_client(context: str = ""):
197
- """Get a configured Kubernetes RBAC API client.
198
-
199
- Args:
200
- context: Optional context name for multi-cluster support
201
-
202
- Returns:
203
- kubernetes.client.RbacAuthorizationV1Api: Configured RBAC client
204
-
205
- Raises:
206
- RuntimeError: If Kubernetes config cannot be loaded
207
- """
386
+ """Get a configured Kubernetes RBAC API client."""
208
387
  from kubernetes import client
209
-
210
- try:
211
- api_client = _load_config_for_context(context)
212
- return client.RbacAuthorizationV1Api(api_client=api_client)
213
- except Exception as e:
214
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
388
+ return _get_client(context, client.RbacAuthorizationV1Api)
215
389
 
216
390
 
217
391
  def get_networking_client(context: str = ""):
218
- """Get a configured Kubernetes Networking API client.
219
-
220
- Args:
221
- context: Optional context name for multi-cluster support
222
-
223
- Returns:
224
- kubernetes.client.NetworkingV1Api: Configured Networking client
225
-
226
- Raises:
227
- RuntimeError: If Kubernetes config cannot be loaded
228
- """
392
+ """Get a configured Kubernetes Networking API client."""
229
393
  from kubernetes import client
230
-
231
- try:
232
- api_client = _load_config_for_context(context)
233
- return client.NetworkingV1Api(api_client=api_client)
234
- except Exception as e:
235
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
394
+ return _get_client(context, client.NetworkingV1Api)
236
395
 
237
396
 
238
397
  def get_storage_client(context: str = ""):
239
- """Get a configured Kubernetes Storage API client.
240
-
241
- Args:
242
- context: Optional context name for multi-cluster support
243
-
244
- Returns:
245
- kubernetes.client.StorageV1Api: Configured Storage client
246
-
247
- Raises:
248
- RuntimeError: If Kubernetes config cannot be loaded
249
- """
398
+ """Get a configured Kubernetes Storage API client."""
250
399
  from kubernetes import client
251
-
252
- try:
253
- api_client = _load_config_for_context(context)
254
- return client.StorageV1Api(api_client=api_client)
255
- except Exception as e:
256
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
400
+ return _get_client(context, client.StorageV1Api)
257
401
 
258
402
 
259
403
  def get_batch_client(context: str = ""):
260
- """Get a configured Kubernetes Batch API client.
261
-
262
- Args:
263
- context: Optional context name for multi-cluster support
264
-
265
- Returns:
266
- kubernetes.client.BatchV1Api: Configured Batch client
267
-
268
- Raises:
269
- RuntimeError: If Kubernetes config cannot be loaded
270
- """
404
+ """Get a configured Kubernetes Batch API client."""
271
405
  from kubernetes import client
272
-
273
- try:
274
- api_client = _load_config_for_context(context)
275
- return client.BatchV1Api(api_client=api_client)
276
- except Exception as e:
277
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
406
+ return _get_client(context, client.BatchV1Api)
278
407
 
279
408
 
280
409
  def get_autoscaling_client(context: str = ""):
281
- """Get a configured Kubernetes Autoscaling API client.
282
-
283
- Args:
284
- context: Optional context name for multi-cluster support
285
-
286
- Returns:
287
- kubernetes.client.AutoscalingV1Api: Configured Autoscaling client
288
-
289
- Raises:
290
- RuntimeError: If Kubernetes config cannot be loaded
291
- """
410
+ """Get a configured Kubernetes Autoscaling API client."""
292
411
  from kubernetes import client
293
-
294
- try:
295
- api_client = _load_config_for_context(context)
296
- return client.AutoscalingV1Api(api_client=api_client)
297
- except Exception as e:
298
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
412
+ return _get_client(context, client.AutoscalingV1Api)
299
413
 
300
414
 
301
415
  def get_policy_client(context: str = ""):
302
- """Get a configured Kubernetes Policy API client.
303
-
304
- Args:
305
- context: Optional context name for multi-cluster support
306
-
307
- Returns:
308
- kubernetes.client.PolicyV1Api: Configured Policy client
309
-
310
- Raises:
311
- RuntimeError: If Kubernetes config cannot be loaded
312
- """
416
+ """Get a configured Kubernetes Policy API client."""
313
417
  from kubernetes import client
314
-
315
- try:
316
- api_client = _load_config_for_context(context)
317
- return client.PolicyV1Api(api_client=api_client)
318
- except Exception as e:
319
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
418
+ return _get_client(context, client.PolicyV1Api)
320
419
 
321
420
 
322
421
  def get_custom_objects_client(context: str = ""):
323
- """Get a configured Kubernetes Custom Objects API client.
324
-
325
- Args:
326
- context: Optional context name for multi-cluster support
327
-
328
- Returns:
329
- kubernetes.client.CustomObjectsApi: Configured Custom Objects client
330
-
331
- Raises:
332
- RuntimeError: If Kubernetes config cannot be loaded
333
- """
422
+ """Get a configured Kubernetes Custom Objects API client."""
334
423
  from kubernetes import client
335
-
336
- try:
337
- api_client = _load_config_for_context(context)
338
- return client.CustomObjectsApi(api_client=api_client)
339
- except Exception as e:
340
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
424
+ return _get_client(context, client.CustomObjectsApi)
341
425
 
342
426
 
343
427
  def get_version_client(context: str = ""):
344
- """Get a configured Kubernetes Version API client.
345
-
346
- Args:
347
- context: Optional context name for multi-cluster support
348
-
349
- Returns:
350
- kubernetes.client.VersionApi: Configured Version client
351
-
352
- Raises:
353
- RuntimeError: If Kubernetes config cannot be loaded
354
- """
428
+ """Get a configured Kubernetes Version API client."""
355
429
  from kubernetes import client
356
-
357
- try:
358
- api_client = _load_config_for_context(context)
359
- return client.VersionApi(api_client=api_client)
360
- except Exception as e:
361
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
430
+ return _get_client(context, client.VersionApi)
362
431
 
363
432
 
364
433
  def get_admissionregistration_client(context: str = ""):
365
- """Get a configured Kubernetes Admission Registration API client.
366
-
367
- Args:
368
- context: Optional context name for multi-cluster support
369
-
370
- Returns:
371
- kubernetes.client.AdmissionregistrationV1Api: Configured Admission client
372
-
373
- Raises:
374
- RuntimeError: If Kubernetes config cannot be loaded
375
- """
434
+ """Get a configured Kubernetes Admission Registration API client."""
376
435
  from kubernetes import client
377
-
378
- try:
379
- api_client = _load_config_for_context(context)
380
- return client.AdmissionregistrationV1Api(api_client=api_client)
381
- except Exception as e:
382
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
436
+ return _get_client(context, client.AdmissionregistrationV1Api)
383
437
 
384
438
 
385
439
  def get_apiextensions_client(context: str = ""):
386
- """Get a configured Kubernetes API Extensions client.
387
-
388
- Args:
389
- context: Optional context name for multi-cluster support
390
-
391
- Returns:
392
- kubernetes.client.ApiextensionsV1Api: Configured API Extensions client
393
-
394
- Raises:
395
- RuntimeError: If Kubernetes config cannot be loaded
396
- """
440
+ """Get a configured Kubernetes API Extensions client."""
397
441
  from kubernetes import client
398
-
399
- try:
400
- api_client = _load_config_for_context(context)
401
- return client.ApiextensionsV1Api(api_client=api_client)
402
- except Exception as e:
403
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
442
+ return _get_client(context, client.ApiextensionsV1Api)
404
443
 
405
444
 
406
445
  def get_coordination_client(context: str = ""):
407
- """Get a configured Kubernetes Coordination API client.
408
-
409
- Args:
410
- context: Optional context name for multi-cluster support
411
-
412
- Returns:
413
- kubernetes.client.CoordinationV1Api: Configured Coordination client
414
-
415
- Raises:
416
- RuntimeError: If Kubernetes config cannot be loaded
417
- """
446
+ """Get a configured Kubernetes Coordination API client."""
418
447
  from kubernetes import client
419
-
420
- try:
421
- api_client = _load_config_for_context(context)
422
- return client.CoordinationV1Api(api_client=api_client)
423
- except Exception as e:
424
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
448
+ return _get_client(context, client.CoordinationV1Api)
425
449
 
426
450
 
427
451
  def get_events_client(context: str = ""):
428
- """Get a configured Kubernetes Events API client.
429
-
430
- Args:
431
- context: Optional context name for multi-cluster support
432
-
433
- Returns:
434
- kubernetes.client.EventsV1Api: Configured Events client
435
-
436
- Raises:
437
- RuntimeError: If Kubernetes config cannot be loaded
438
- """
452
+ """Get a configured Kubernetes Events API client."""
439
453
  from kubernetes import client
454
+ return _get_client(context, client.EventsV1Api)
440
455
 
441
- try:
442
- api_client = _load_config_for_context(context)
443
- return client.EventsV1Api(api_client=api_client)
444
- except Exception as e:
445
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
446
-
447
-
448
- # Utility functions for context management
449
456
 
450
457
  def list_contexts() -> list:
451
- """
452
- List all available kubeconfig contexts.
458
+ """List all available kubeconfig contexts."""
459
+ if _HAS_PROVIDER:
460
+ try:
461
+ provider = get_provider()
462
+ contexts = provider.list_contexts()
463
+ return [
464
+ {
465
+ "name": ctx.name,
466
+ "cluster": ctx.cluster,
467
+ "user": ctx.user,
468
+ "namespace": ctx.namespace,
469
+ "active": ctx.is_active
470
+ }
471
+ for ctx in contexts
472
+ ]
473
+ except Exception as e:
474
+ logger.warning(f"Provider list_contexts failed: {e}")
453
475
 
454
- Returns:
455
- List of context dictionaries with name, cluster, user, namespace
456
- """
457
476
  from kubernetes import config
458
477
 
459
478
  try:
@@ -478,14 +497,14 @@ def list_contexts() -> list:
478
497
 
479
498
 
480
499
  def get_active_context() -> Optional[str]:
481
- """
482
- Get the current active context name.
500
+ """Get the current active context name."""
501
+ if _HAS_PROVIDER:
502
+ try:
503
+ return provider_get_current_context()
504
+ except Exception as e:
505
+ logger.warning(f"Provider get_current_context failed: {e}")
483
506
 
484
- Returns:
485
- Active context name or None
486
- """
487
507
  from kubernetes import config
488
-
489
508
  try:
490
509
  kubeconfig_path = os.environ.get('KUBECONFIG', '~/.kube/config')
491
510
  kubeconfig_path = os.path.expanduser(kubeconfig_path)
@@ -498,33 +517,33 @@ def get_active_context() -> Optional[str]:
498
517
 
499
518
 
500
519
  def context_exists(context: str) -> bool:
501
- """
502
- Check if a context exists in kubeconfig.
503
-
504
- Args:
505
- context: Context name to check
506
-
507
- Returns:
508
- True if context exists
509
- """
520
+ """Check if a context exists in kubeconfig."""
510
521
  contexts = list_contexts()
511
522
  return any(ctx["name"] == context for ctx in contexts)
512
523
 
513
524
 
514
525
  def _get_kubectl_context_args(context: str = "") -> list:
515
- """
516
- Get kubectl command arguments for specifying a context.
517
-
518
- This utility function returns the appropriate --context flag arguments
519
- for kubectl commands when targeting a specific cluster.
520
-
521
- Args:
522
- context: Context name (empty string for default context)
523
-
524
- Returns:
525
- List of command arguments, e.g., ["--context", "my-cluster"]
526
- or empty list if no context specified
527
- """
526
+ """Get kubectl command arguments for specifying a context."""
528
527
  if context and context.strip():
529
528
  return ["--context", context.strip()]
530
529
  return []
530
+
531
+
532
+ _BASE_EXPORTS = [
533
+ "get_k8s_client", "get_apps_client", "get_rbac_client", "get_networking_client",
534
+ "get_storage_client", "get_batch_client", "get_autoscaling_client", "get_policy_client",
535
+ "get_custom_objects_client", "get_version_client", "get_admissionregistration_client",
536
+ "get_apiextensions_client", "get_coordination_client", "get_events_client",
537
+ "load_kubernetes_config", "patch_kubernetes_config",
538
+ "list_contexts", "get_active_context", "context_exists",
539
+ "enable_kubeconfig_watch", "disable_kubeconfig_watch", "on_config_change", "KubeconfigWatcher",
540
+ "is_stateless_mode", "set_stateless_mode",
541
+ ]
542
+
543
+ if _HAS_PROVIDER:
544
+ __all__ = _BASE_EXPORTS + [
545
+ "KubernetesProvider", "ProviderConfig", "ProviderType",
546
+ "UnknownContextError", "get_provider", "validate_context",
547
+ ]
548
+ else:
549
+ __all__ = _BASE_EXPORTS