kubectl-mcp-server 1.18.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.
@@ -14,15 +14,190 @@ Environment Variables:
14
14
  MCP_K8S_QPS: API rate limit (default: 100)
15
15
  MCP_K8S_BURST: API burst limit (default: 200)
16
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
17
19
  """
18
20
 
19
21
  import os
20
22
  import logging
21
- from typing import Optional, Any, List, Dict
23
+ import threading
24
+ import time
25
+ from typing import Optional, Any, List, Dict, Callable
22
26
 
23
27
  logger = logging.getLogger("mcp-server")
24
28
 
25
- # Try to import provider module for enhanced features
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
+
26
201
  try:
27
202
  from .providers import (
28
203
  KubernetesProvider,
@@ -60,7 +235,6 @@ def load_kubernetes_config(context: str = ""):
60
235
  from kubernetes import config
61
236
  from kubernetes.config.config_exception import ConfigException
62
237
 
63
- # Try in-cluster config first (for pods running in Kubernetes)
64
238
  try:
65
239
  config.load_incluster_config()
66
240
  logger.info("Loaded in-cluster Kubernetes configuration")
@@ -69,7 +243,6 @@ def load_kubernetes_config(context: str = ""):
69
243
  except ConfigException:
70
244
  logger.debug("Not running in-cluster, trying kubeconfig...")
71
245
 
72
- # Fall back to kubeconfig file
73
246
  try:
74
247
  kubeconfig_path = os.environ.get('KUBECONFIG', '~/.kube/config')
75
248
  kubeconfig_path = os.path.expanduser(kubeconfig_path)
@@ -97,7 +270,6 @@ def _patched_load_kube_config(*args, **kwargs):
97
270
 
98
271
  from kubernetes.config.config_exception import ConfigException
99
272
 
100
- # Try in-cluster config first
101
273
  try:
102
274
  from kubernetes import config
103
275
  config.load_incluster_config()
@@ -107,7 +279,6 @@ def _patched_load_kube_config(*args, **kwargs):
107
279
  except ConfigException:
108
280
  pass
109
281
 
110
- # Fall back to original behavior
111
282
  if _original_load_kube_config:
112
283
  _original_load_kube_config(*args, **kwargs)
113
284
  _config_loaded = True
@@ -131,7 +302,6 @@ def patch_kubernetes_config():
131
302
  logger.debug("kubernetes package not available for patching")
132
303
 
133
304
 
134
- # Auto-patch when this module is imported
135
305
  patch_kubernetes_config()
136
306
 
137
307
 
@@ -139,7 +309,8 @@ def _load_config_for_context(context: str = "") -> Any:
139
309
  """
140
310
  Load kubernetes config for a specific context and return ApiClient.
141
311
 
142
- Uses the provider module for caching when available.
312
+ Uses the provider module for caching when available, unless stateless
313
+ mode is enabled.
143
314
 
144
315
  Args:
145
316
  context: Context name (empty for default)
@@ -151,8 +322,7 @@ def _load_config_for_context(context: str = "") -> Any:
151
322
  UnknownContextError: If context is not found (when provider available)
152
323
  RuntimeError: If config cannot be loaded
153
324
  """
154
- # Use provider module if available (provides caching and validation)
155
- if _HAS_PROVIDER:
325
+ if not _stateless_mode and _HAS_PROVIDER:
156
326
  try:
157
327
  provider = get_provider()
158
328
  return provider.get_api_client(context)
@@ -161,18 +331,15 @@ def _load_config_for_context(context: str = "") -> Any:
161
331
  except Exception as e:
162
332
  logger.warning(f"Provider failed, falling back to basic config: {e}")
163
333
 
164
- # Fallback to basic config loading
165
334
  from kubernetes import client, config
166
335
  from kubernetes.config.config_exception import ConfigException
167
336
 
168
- # Try in-cluster first
169
337
  try:
170
338
  config.load_incluster_config()
171
339
  return client.ApiClient()
172
340
  except ConfigException:
173
341
  pass
174
342
 
175
- # Load kubeconfig with optional context
176
343
  kubeconfig_path = os.environ.get('KUBECONFIG', '~/.kube/config')
177
344
  kubeconfig_path = os.path.expanduser(kubeconfig_path)
178
345
 
@@ -193,310 +360,102 @@ def _load_config_for_context(context: str = "") -> Any:
193
360
  return client.ApiClient(configuration=api_config)
194
361
 
195
362
 
196
- def get_k8s_client(context: str = ""):
197
- """Get a configured Kubernetes Core API client.
198
-
199
- Args:
200
- context: Optional context name for multi-cluster support
201
-
202
- Returns:
203
- kubernetes.client.CoreV1Api: Configured Kubernetes client
204
-
205
- Raises:
206
- RuntimeError: If Kubernetes config cannot be loaded
207
- """
363
+ def _get_client(context: str, client_class):
364
+ """Helper to create a configured Kubernetes API client."""
208
365
  from kubernetes import client
209
-
210
366
  try:
211
367
  api_client = _load_config_for_context(context)
212
- return client.CoreV1Api(api_client=api_client)
368
+ return client_class(api_client=api_client)
213
369
  except Exception as e:
214
370
  raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
215
371
 
216
372
 
217
- def get_apps_client(context: str = ""):
218
- """Get a configured Kubernetes Apps API client.
219
-
220
- Args:
221
- 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)
222
377
 
223
- Returns:
224
- kubernetes.client.AppsV1Api: Configured Apps API client
225
378
 
226
- Raises:
227
- RuntimeError: If Kubernetes config cannot be loaded
228
- """
379
+ def get_apps_client(context: str = ""):
380
+ """Get a configured Kubernetes Apps API client."""
229
381
  from kubernetes import client
230
-
231
- try:
232
- api_client = _load_config_for_context(context)
233
- return client.AppsV1Api(api_client=api_client)
234
- except Exception as e:
235
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
382
+ return _get_client(context, client.AppsV1Api)
236
383
 
237
384
 
238
385
  def get_rbac_client(context: str = ""):
239
- """Get a configured Kubernetes RBAC API client.
240
-
241
- Args:
242
- context: Optional context name for multi-cluster support
243
-
244
- Returns:
245
- kubernetes.client.RbacAuthorizationV1Api: Configured RBAC client
246
-
247
- Raises:
248
- RuntimeError: If Kubernetes config cannot be loaded
249
- """
386
+ """Get a configured Kubernetes RBAC API client."""
250
387
  from kubernetes import client
251
-
252
- try:
253
- api_client = _load_config_for_context(context)
254
- return client.RbacAuthorizationV1Api(api_client=api_client)
255
- except Exception as e:
256
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
388
+ return _get_client(context, client.RbacAuthorizationV1Api)
257
389
 
258
390
 
259
391
  def get_networking_client(context: str = ""):
260
- """Get a configured Kubernetes Networking API client.
261
-
262
- Args:
263
- context: Optional context name for multi-cluster support
264
-
265
- Returns:
266
- kubernetes.client.NetworkingV1Api: Configured Networking client
267
-
268
- Raises:
269
- RuntimeError: If Kubernetes config cannot be loaded
270
- """
392
+ """Get a configured Kubernetes Networking API client."""
271
393
  from kubernetes import client
272
-
273
- try:
274
- api_client = _load_config_for_context(context)
275
- return client.NetworkingV1Api(api_client=api_client)
276
- except Exception as e:
277
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
394
+ return _get_client(context, client.NetworkingV1Api)
278
395
 
279
396
 
280
397
  def get_storage_client(context: str = ""):
281
- """Get a configured Kubernetes Storage API client.
282
-
283
- Args:
284
- context: Optional context name for multi-cluster support
285
-
286
- Returns:
287
- kubernetes.client.StorageV1Api: Configured Storage client
288
-
289
- Raises:
290
- RuntimeError: If Kubernetes config cannot be loaded
291
- """
398
+ """Get a configured Kubernetes Storage API client."""
292
399
  from kubernetes import client
293
-
294
- try:
295
- api_client = _load_config_for_context(context)
296
- return client.StorageV1Api(api_client=api_client)
297
- except Exception as e:
298
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
400
+ return _get_client(context, client.StorageV1Api)
299
401
 
300
402
 
301
403
  def get_batch_client(context: str = ""):
302
- """Get a configured Kubernetes Batch API client.
303
-
304
- Args:
305
- context: Optional context name for multi-cluster support
306
-
307
- Returns:
308
- kubernetes.client.BatchV1Api: Configured Batch client
309
-
310
- Raises:
311
- RuntimeError: If Kubernetes config cannot be loaded
312
- """
404
+ """Get a configured Kubernetes Batch API client."""
313
405
  from kubernetes import client
314
-
315
- try:
316
- api_client = _load_config_for_context(context)
317
- return client.BatchV1Api(api_client=api_client)
318
- except Exception as e:
319
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
406
+ return _get_client(context, client.BatchV1Api)
320
407
 
321
408
 
322
409
  def get_autoscaling_client(context: str = ""):
323
- """Get a configured Kubernetes Autoscaling API client.
324
-
325
- Args:
326
- context: Optional context name for multi-cluster support
327
-
328
- Returns:
329
- kubernetes.client.AutoscalingV1Api: Configured Autoscaling client
330
-
331
- Raises:
332
- RuntimeError: If Kubernetes config cannot be loaded
333
- """
410
+ """Get a configured Kubernetes Autoscaling API client."""
334
411
  from kubernetes import client
335
-
336
- try:
337
- api_client = _load_config_for_context(context)
338
- return client.AutoscalingV1Api(api_client=api_client)
339
- except Exception as e:
340
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
412
+ return _get_client(context, client.AutoscalingV1Api)
341
413
 
342
414
 
343
415
  def get_policy_client(context: str = ""):
344
- """Get a configured Kubernetes Policy API client.
345
-
346
- Args:
347
- context: Optional context name for multi-cluster support
348
-
349
- Returns:
350
- kubernetes.client.PolicyV1Api: Configured Policy client
351
-
352
- Raises:
353
- RuntimeError: If Kubernetes config cannot be loaded
354
- """
416
+ """Get a configured Kubernetes Policy API client."""
355
417
  from kubernetes import client
356
-
357
- try:
358
- api_client = _load_config_for_context(context)
359
- return client.PolicyV1Api(api_client=api_client)
360
- except Exception as e:
361
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
418
+ return _get_client(context, client.PolicyV1Api)
362
419
 
363
420
 
364
421
  def get_custom_objects_client(context: str = ""):
365
- """Get a configured Kubernetes Custom Objects API client.
366
-
367
- Args:
368
- context: Optional context name for multi-cluster support
369
-
370
- Returns:
371
- kubernetes.client.CustomObjectsApi: Configured Custom Objects client
372
-
373
- Raises:
374
- RuntimeError: If Kubernetes config cannot be loaded
375
- """
422
+ """Get a configured Kubernetes Custom Objects API client."""
376
423
  from kubernetes import client
377
-
378
- try:
379
- api_client = _load_config_for_context(context)
380
- return client.CustomObjectsApi(api_client=api_client)
381
- except Exception as e:
382
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
424
+ return _get_client(context, client.CustomObjectsApi)
383
425
 
384
426
 
385
427
  def get_version_client(context: str = ""):
386
- """Get a configured Kubernetes Version API client.
387
-
388
- Args:
389
- context: Optional context name for multi-cluster support
390
-
391
- Returns:
392
- kubernetes.client.VersionApi: Configured Version client
393
-
394
- Raises:
395
- RuntimeError: If Kubernetes config cannot be loaded
396
- """
428
+ """Get a configured Kubernetes Version API client."""
397
429
  from kubernetes import client
398
-
399
- try:
400
- api_client = _load_config_for_context(context)
401
- return client.VersionApi(api_client=api_client)
402
- except Exception as e:
403
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
430
+ return _get_client(context, client.VersionApi)
404
431
 
405
432
 
406
433
  def get_admissionregistration_client(context: str = ""):
407
- """Get a configured Kubernetes Admission Registration API client.
408
-
409
- Args:
410
- context: Optional context name for multi-cluster support
411
-
412
- Returns:
413
- kubernetes.client.AdmissionregistrationV1Api: Configured Admission client
414
-
415
- Raises:
416
- RuntimeError: If Kubernetes config cannot be loaded
417
- """
434
+ """Get a configured Kubernetes Admission Registration API client."""
418
435
  from kubernetes import client
419
-
420
- try:
421
- api_client = _load_config_for_context(context)
422
- return client.AdmissionregistrationV1Api(api_client=api_client)
423
- except Exception as e:
424
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
436
+ return _get_client(context, client.AdmissionregistrationV1Api)
425
437
 
426
438
 
427
439
  def get_apiextensions_client(context: str = ""):
428
- """Get a configured Kubernetes API Extensions client.
429
-
430
- Args:
431
- context: Optional context name for multi-cluster support
432
-
433
- Returns:
434
- kubernetes.client.ApiextensionsV1Api: Configured API Extensions client
435
-
436
- Raises:
437
- RuntimeError: If Kubernetes config cannot be loaded
438
- """
440
+ """Get a configured Kubernetes API Extensions client."""
439
441
  from kubernetes import client
440
-
441
- try:
442
- api_client = _load_config_for_context(context)
443
- return client.ApiextensionsV1Api(api_client=api_client)
444
- except Exception as e:
445
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
442
+ return _get_client(context, client.ApiextensionsV1Api)
446
443
 
447
444
 
448
445
  def get_coordination_client(context: str = ""):
449
- """Get a configured Kubernetes Coordination API client.
450
-
451
- Args:
452
- context: Optional context name for multi-cluster support
453
-
454
- Returns:
455
- kubernetes.client.CoordinationV1Api: Configured Coordination client
456
-
457
- Raises:
458
- RuntimeError: If Kubernetes config cannot be loaded
459
- """
446
+ """Get a configured Kubernetes Coordination API client."""
460
447
  from kubernetes import client
461
-
462
- try:
463
- api_client = _load_config_for_context(context)
464
- return client.CoordinationV1Api(api_client=api_client)
465
- except Exception as e:
466
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
448
+ return _get_client(context, client.CoordinationV1Api)
467
449
 
468
450
 
469
451
  def get_events_client(context: str = ""):
470
- """Get a configured Kubernetes Events API client.
471
-
472
- Args:
473
- context: Optional context name for multi-cluster support
474
-
475
- Returns:
476
- kubernetes.client.EventsV1Api: Configured Events client
477
-
478
- Raises:
479
- RuntimeError: If Kubernetes config cannot be loaded
480
- """
452
+ """Get a configured Kubernetes Events API client."""
481
453
  from kubernetes import client
454
+ return _get_client(context, client.EventsV1Api)
482
455
 
483
- try:
484
- api_client = _load_config_for_context(context)
485
- return client.EventsV1Api(api_client=api_client)
486
- except Exception as e:
487
- raise RuntimeError(f"Invalid kube-config. Context: {context or 'default'}. Error: {e}")
488
-
489
-
490
- # Utility functions for context management
491
456
 
492
457
  def list_contexts() -> list:
493
- """
494
- List all available kubeconfig contexts.
495
-
496
- Returns:
497
- List of context dictionaries with name, cluster, user, namespace
498
- """
499
- # Use provider if available
458
+ """List all available kubeconfig contexts."""
500
459
  if _HAS_PROVIDER:
501
460
  try:
502
461
  provider = get_provider()
@@ -514,7 +473,6 @@ def list_contexts() -> list:
514
473
  except Exception as e:
515
474
  logger.warning(f"Provider list_contexts failed: {e}")
516
475
 
517
- # Fallback to direct kubeconfig reading
518
476
  from kubernetes import config
519
477
 
520
478
  try:
@@ -539,22 +497,14 @@ def list_contexts() -> list:
539
497
 
540
498
 
541
499
  def get_active_context() -> Optional[str]:
542
- """
543
- Get the current active context name.
544
-
545
- Returns:
546
- Active context name or None
547
- """
548
- # Use provider if available
500
+ """Get the current active context name."""
549
501
  if _HAS_PROVIDER:
550
502
  try:
551
503
  return provider_get_current_context()
552
504
  except Exception as e:
553
505
  logger.warning(f"Provider get_current_context failed: {e}")
554
506
 
555
- # Fallback to direct kubeconfig reading
556
507
  from kubernetes import config
557
-
558
508
  try:
559
509
  kubeconfig_path = os.environ.get('KUBECONFIG', '~/.kube/config')
560
510
  kubeconfig_path = os.path.expanduser(kubeconfig_path)
@@ -567,90 +517,33 @@ def get_active_context() -> Optional[str]:
567
517
 
568
518
 
569
519
  def context_exists(context: str) -> bool:
570
- """
571
- Check if a context exists in kubeconfig.
572
-
573
- Args:
574
- context: Context name to check
575
-
576
- Returns:
577
- True if context exists
578
- """
520
+ """Check if a context exists in kubeconfig."""
579
521
  contexts = list_contexts()
580
522
  return any(ctx["name"] == context for ctx in contexts)
581
523
 
582
524
 
583
525
  def _get_kubectl_context_args(context: str = "") -> list:
584
- """
585
- Get kubectl command arguments for specifying a context.
586
-
587
- This utility function returns the appropriate --context flag arguments
588
- for kubectl commands when targeting a specific cluster.
589
-
590
- Args:
591
- context: Context name (empty string for default context)
592
-
593
- Returns:
594
- List of command arguments, e.g., ["--context", "my-cluster"]
595
- or empty list if no context specified
596
- """
526
+ """Get kubectl command arguments for specifying a context."""
597
527
  if context and context.strip():
598
528
  return ["--context", context.strip()]
599
529
  return []
600
530
 
601
531
 
602
- # Re-export provider types for convenience
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
+
603
543
  if _HAS_PROVIDER:
604
- __all__ = [
605
- # Client functions
606
- "get_k8s_client",
607
- "get_apps_client",
608
- "get_rbac_client",
609
- "get_networking_client",
610
- "get_storage_client",
611
- "get_batch_client",
612
- "get_autoscaling_client",
613
- "get_policy_client",
614
- "get_custom_objects_client",
615
- "get_version_client",
616
- "get_admissionregistration_client",
617
- "get_apiextensions_client",
618
- "get_coordination_client",
619
- "get_events_client",
620
- # Config functions
621
- "load_kubernetes_config",
622
- "patch_kubernetes_config",
623
- # Context functions
624
- "list_contexts",
625
- "get_active_context",
626
- "context_exists",
627
- # Provider types (when available)
628
- "KubernetesProvider",
629
- "ProviderConfig",
630
- "ProviderType",
631
- "UnknownContextError",
632
- "get_provider",
633
- "validate_context",
544
+ __all__ = _BASE_EXPORTS + [
545
+ "KubernetesProvider", "ProviderConfig", "ProviderType",
546
+ "UnknownContextError", "get_provider", "validate_context",
634
547
  ]
635
548
  else:
636
- __all__ = [
637
- "get_k8s_client",
638
- "get_apps_client",
639
- "get_rbac_client",
640
- "get_networking_client",
641
- "get_storage_client",
642
- "get_batch_client",
643
- "get_autoscaling_client",
644
- "get_policy_client",
645
- "get_custom_objects_client",
646
- "get_version_client",
647
- "get_admissionregistration_client",
648
- "get_apiextensions_client",
649
- "get_coordination_client",
650
- "get_events_client",
651
- "load_kubernetes_config",
652
- "patch_kubernetes_config",
653
- "list_contexts",
654
- "get_active_context",
655
- "context_exists",
656
- ]
549
+ __all__ = _BASE_EXPORTS