feat: watch for configuration changes

Watch kube config files for changes.
Automatically reload kubernetes client and list of tools.

Useful for logins or context changes after an MCP session has started.
This commit is contained in:
Marc Nuri
2025-03-21 18:05:41 +01:00
parent c9def7dd46
commit a98e69102c
7 changed files with 97 additions and 6 deletions

View File

@@ -1,6 +1,7 @@
package kubernetes
import (
"github.com/fsnotify/fsnotify"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/client-go/discovery"
"k8s.io/client-go/discovery/cached/memory"
@@ -17,8 +18,12 @@ import (
// Exposed for testing
var InClusterConfig = rest.InClusterConfig
type CloseWatchKubeConfig func() error
type Kubernetes struct {
cfg *rest.Config
kubeConfigFiles []string
CloseWatchKubeConfig CloseWatchKubeConfig
clientSet *kubernetes.Clientset
discoveryClient *discovery.DiscoveryClient
deferredDiscoveryRESTMapper *restmapper.DeferredDiscoveryRESTMapper
@@ -44,6 +49,7 @@ func NewKubernetes() (*Kubernetes, error) {
}
return &Kubernetes{
cfg: cfg,
kubeConfigFiles: resolveConfig().ConfigAccess().GetLoadingPrecedence(),
clientSet: clientSet,
discoveryClient: discoveryClient,
deferredDiscoveryRESTMapper: restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(discoveryClient)),
@@ -51,6 +57,44 @@ func NewKubernetes() (*Kubernetes, error) {
}, nil
}
func (k *Kubernetes) WatchKubeConfig(onKubeConfigChange func() error) {
if len(k.kubeConfigFiles) == 0 {
return
}
watcher, err := fsnotify.NewWatcher()
if err != nil {
return
}
for _, file := range k.kubeConfigFiles {
_ = watcher.Add(file)
}
go func() {
for {
select {
case _, ok := <-watcher.Events:
if !ok {
return
}
_ = onKubeConfigChange()
case _, ok := <-watcher.Errors:
if !ok {
return
}
}
}
}()
if k.CloseWatchKubeConfig != nil {
_ = k.CloseWatchKubeConfig()
}
k.CloseWatchKubeConfig = watcher.Close
}
func (k *Kubernetes) Close() {
if k.CloseWatchKubeConfig != nil {
_ = k.CloseWatchKubeConfig()
}
}
func marshal(v any) (string, error) {
switch t := v.(type) {
case []unstructured.Unstructured: