mirror of
https://github.com/containers/kubernetes-mcp-server.git
synced 2025-10-23 01:22:57 +03:00
refactor(kubernetes): force usage of Derived kubernetes (125)
refactor(kubernetes): force usage of Derived kubernetes Prevents consumers of the kubernetes package the usage of public methods on a non-derived config instance. --- review(kubernetes): force usage of Derived kubernetes Addresses comment by ardaguclu
This commit is contained in:
@@ -21,7 +21,7 @@ var InClusterConfig = func() (*rest.Config, error) {
|
||||
}
|
||||
|
||||
// resolveKubernetesConfigurations resolves the required kubernetes configurations and sets them in the Kubernetes struct
|
||||
func resolveKubernetesConfigurations(kubernetes *Kubernetes) error {
|
||||
func resolveKubernetesConfigurations(kubernetes *Manager) error {
|
||||
// Always set clientCmdConfig
|
||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||
if kubernetes.Kubeconfig != "" {
|
||||
@@ -45,56 +45,60 @@ func resolveKubernetesConfigurations(kubernetes *Kubernetes) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (k *Kubernetes) IsInCluster() bool {
|
||||
if k.Kubeconfig != "" {
|
||||
func (m *Manager) IsInCluster() bool {
|
||||
if m.Kubeconfig != "" {
|
||||
return false
|
||||
}
|
||||
cfg, err := InClusterConfig()
|
||||
return err == nil && cfg != nil
|
||||
}
|
||||
|
||||
func (k *Kubernetes) configuredNamespace() string {
|
||||
if ns, _, nsErr := k.clientCmdConfig.Namespace(); nsErr == nil {
|
||||
func (m *Manager) configuredNamespace() string {
|
||||
if ns, _, nsErr := m.clientCmdConfig.Namespace(); nsErr == nil {
|
||||
return ns
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (k *Kubernetes) NamespaceOrDefault(namespace string) string {
|
||||
func (m *Manager) NamespaceOrDefault(namespace string) string {
|
||||
if namespace == "" {
|
||||
return k.configuredNamespace()
|
||||
return m.configuredNamespace()
|
||||
}
|
||||
return namespace
|
||||
}
|
||||
|
||||
func (k *Kubernetes) NamespaceOrDefault(namespace string) string {
|
||||
return k.manager.NamespaceOrDefault(namespace)
|
||||
}
|
||||
|
||||
// ToRESTConfig returns the rest.Config object (genericclioptions.RESTClientGetter)
|
||||
func (k *Kubernetes) ToRESTConfig() (*rest.Config, error) {
|
||||
return k.cfg, nil
|
||||
func (m *Manager) ToRESTConfig() (*rest.Config, error) {
|
||||
return m.cfg, nil
|
||||
}
|
||||
|
||||
// ToRawKubeConfigLoader returns the clientcmd.ClientConfig object (genericclioptions.RESTClientGetter)
|
||||
func (k *Kubernetes) ToRawKubeConfigLoader() clientcmd.ClientConfig {
|
||||
return k.clientCmdConfig
|
||||
func (m *Manager) ToRawKubeConfigLoader() clientcmd.ClientConfig {
|
||||
return m.clientCmdConfig
|
||||
}
|
||||
|
||||
func (k *Kubernetes) ConfigurationView(minify bool) (runtime.Object, error) {
|
||||
func (m *Manager) ConfigurationView(minify bool) (runtime.Object, error) {
|
||||
var cfg clientcmdapi.Config
|
||||
var err error
|
||||
if k.IsInCluster() {
|
||||
if m.IsInCluster() {
|
||||
cfg = *clientcmdapi.NewConfig()
|
||||
cfg.Clusters["cluster"] = &clientcmdapi.Cluster{
|
||||
Server: k.cfg.Host,
|
||||
InsecureSkipTLSVerify: k.cfg.Insecure,
|
||||
Server: m.cfg.Host,
|
||||
InsecureSkipTLSVerify: m.cfg.Insecure,
|
||||
}
|
||||
cfg.AuthInfos["user"] = &clientcmdapi.AuthInfo{
|
||||
Token: k.cfg.BearerToken,
|
||||
Token: m.cfg.BearerToken,
|
||||
}
|
||||
cfg.Contexts["context"] = &clientcmdapi.Context{
|
||||
Cluster: "cluster",
|
||||
AuthInfo: "user",
|
||||
}
|
||||
cfg.CurrentContext = "context"
|
||||
} else if cfg, err = k.clientCmdConfig.RawConfig(); err != nil {
|
||||
} else if cfg, err = m.clientCmdConfig.RawConfig(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if minify {
|
||||
|
||||
@@ -12,10 +12,10 @@ import (
|
||||
|
||||
func TestKubernetes_IsInCluster(t *testing.T) {
|
||||
t.Run("with explicit kubeconfig", func(t *testing.T) {
|
||||
k := Kubernetes{
|
||||
m := Manager{
|
||||
Kubeconfig: "kubeconfig",
|
||||
}
|
||||
if k.IsInCluster() {
|
||||
if m.IsInCluster() {
|
||||
t.Errorf("expected not in cluster, got in cluster")
|
||||
}
|
||||
})
|
||||
@@ -27,10 +27,10 @@ func TestKubernetes_IsInCluster(t *testing.T) {
|
||||
defer func() {
|
||||
InClusterConfig = originalFunction
|
||||
}()
|
||||
k := Kubernetes{
|
||||
m := Manager{
|
||||
Kubeconfig: "",
|
||||
}
|
||||
if !k.IsInCluster() {
|
||||
if !m.IsInCluster() {
|
||||
t.Errorf("expected in cluster, got not in cluster")
|
||||
}
|
||||
})
|
||||
@@ -42,10 +42,10 @@ func TestKubernetes_IsInCluster(t *testing.T) {
|
||||
defer func() {
|
||||
InClusterConfig = originalFunction
|
||||
}()
|
||||
k := Kubernetes{
|
||||
m := Manager{
|
||||
Kubeconfig: "",
|
||||
}
|
||||
if k.IsInCluster() {
|
||||
if m.IsInCluster() {
|
||||
t.Errorf("expected not in cluster, got in cluster")
|
||||
}
|
||||
})
|
||||
@@ -57,10 +57,10 @@ func TestKubernetes_IsInCluster(t *testing.T) {
|
||||
defer func() {
|
||||
InClusterConfig = originalFunction
|
||||
}()
|
||||
k := Kubernetes{
|
||||
m := Manager{
|
||||
Kubeconfig: "",
|
||||
}
|
||||
if k.IsInCluster() {
|
||||
if m.IsInCluster() {
|
||||
t.Errorf("expected not in cluster, got in cluster")
|
||||
}
|
||||
})
|
||||
@@ -72,8 +72,8 @@ func TestKubernetes_ResolveKubernetesConfigurations_Explicit(t *testing.T) {
|
||||
t.Skip("Skipping test on non-linux platforms")
|
||||
}
|
||||
tempDir := t.TempDir()
|
||||
k := Kubernetes{Kubeconfig: path.Join(tempDir, "config")}
|
||||
err := resolveKubernetesConfigurations(&k)
|
||||
m := Manager{Kubeconfig: path.Join(tempDir, "config")}
|
||||
err := resolveKubernetesConfigurations(&m)
|
||||
if err == nil {
|
||||
t.Errorf("expected error, got nil")
|
||||
}
|
||||
@@ -90,8 +90,8 @@ func TestKubernetes_ResolveKubernetesConfigurations_Explicit(t *testing.T) {
|
||||
if err := os.WriteFile(kubeconfigPath, []byte(""), 0644); err != nil {
|
||||
t.Fatalf("failed to create kubeconfig file: %v", err)
|
||||
}
|
||||
k := Kubernetes{Kubeconfig: kubeconfigPath}
|
||||
err := resolveKubernetesConfigurations(&k)
|
||||
m := Manager{Kubeconfig: kubeconfigPath}
|
||||
err := resolveKubernetesConfigurations(&m)
|
||||
if err == nil {
|
||||
t.Errorf("expected error, got nil")
|
||||
}
|
||||
@@ -123,16 +123,16 @@ users:
|
||||
if err := os.WriteFile(kubeconfigPath, []byte(kubeconfigContent), 0644); err != nil {
|
||||
t.Fatalf("failed to create kubeconfig file: %v", err)
|
||||
}
|
||||
k := Kubernetes{Kubeconfig: kubeconfigPath}
|
||||
err := resolveKubernetesConfigurations(&k)
|
||||
m := Manager{Kubeconfig: kubeconfigPath}
|
||||
err := resolveKubernetesConfigurations(&m)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
if k.cfg == nil {
|
||||
if m.cfg == nil {
|
||||
t.Errorf("expected non-nil config, got nil")
|
||||
}
|
||||
if k.cfg.Host != "https://example.com" {
|
||||
t.Errorf("expected host https://example.com, got %s", k.cfg.Host)
|
||||
if m.cfg.Host != "https://example.com" {
|
||||
t.Errorf("expected host https://example.com, got %s", m.cfg.Host)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@ const (
|
||||
type CloseWatchKubeConfig func() error
|
||||
|
||||
type Kubernetes struct {
|
||||
manager *Manager
|
||||
}
|
||||
|
||||
type Manager struct {
|
||||
// Kubeconfig path override
|
||||
Kubeconfig string
|
||||
cfg *rest.Config
|
||||
@@ -38,11 +42,10 @@ type Kubernetes struct {
|
||||
discoveryClient discovery.CachedDiscoveryInterface
|
||||
deferredDiscoveryRESTMapper *restmapper.DeferredDiscoveryRESTMapper
|
||||
dynamicClient *dynamic.DynamicClient
|
||||
Helm *helm.Helm
|
||||
}
|
||||
|
||||
func NewKubernetes(kubeconfig string) (*Kubernetes, error) {
|
||||
k8s := &Kubernetes{
|
||||
func NewManager(kubeconfig string) (*Manager, error) {
|
||||
k8s := &Manager{
|
||||
Kubeconfig: kubeconfig,
|
||||
}
|
||||
if err := resolveKubernetesConfigurations(k8s); err != nil {
|
||||
@@ -68,15 +71,14 @@ func NewKubernetes(kubeconfig string) (*Kubernetes, error) {
|
||||
return nil, err
|
||||
}
|
||||
k8s.parameterCodec = runtime.NewParameterCodec(k8s.scheme)
|
||||
k8s.Helm = helm.NewHelm(k8s)
|
||||
return k8s, nil
|
||||
}
|
||||
|
||||
func (k *Kubernetes) WatchKubeConfig(onKubeConfigChange func() error) {
|
||||
if k.clientCmdConfig == nil {
|
||||
func (m *Manager) WatchKubeConfig(onKubeConfigChange func() error) {
|
||||
if m.clientCmdConfig == nil {
|
||||
return
|
||||
}
|
||||
kubeConfigFiles := k.clientCmdConfig.ConfigAccess().GetLoadingPrecedence()
|
||||
kubeConfigFiles := m.clientCmdConfig.ConfigAccess().GetLoadingPrecedence()
|
||||
if len(kubeConfigFiles) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -102,33 +104,33 @@ func (k *Kubernetes) WatchKubeConfig(onKubeConfigChange func() error) {
|
||||
}
|
||||
}
|
||||
}()
|
||||
if k.CloseWatchKubeConfig != nil {
|
||||
_ = k.CloseWatchKubeConfig()
|
||||
if m.CloseWatchKubeConfig != nil {
|
||||
_ = m.CloseWatchKubeConfig()
|
||||
}
|
||||
k.CloseWatchKubeConfig = watcher.Close
|
||||
m.CloseWatchKubeConfig = watcher.Close
|
||||
}
|
||||
|
||||
func (k *Kubernetes) Close() {
|
||||
if k.CloseWatchKubeConfig != nil {
|
||||
_ = k.CloseWatchKubeConfig()
|
||||
func (m *Manager) Close() {
|
||||
if m.CloseWatchKubeConfig != nil {
|
||||
_ = m.CloseWatchKubeConfig()
|
||||
}
|
||||
}
|
||||
|
||||
func (k *Kubernetes) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
|
||||
return k.discoveryClient, nil
|
||||
func (m *Manager) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
|
||||
return m.discoveryClient, nil
|
||||
}
|
||||
|
||||
func (k *Kubernetes) ToRESTMapper() (meta.RESTMapper, error) {
|
||||
return k.deferredDiscoveryRESTMapper, nil
|
||||
func (m *Manager) ToRESTMapper() (meta.RESTMapper, error) {
|
||||
return m.deferredDiscoveryRESTMapper, nil
|
||||
}
|
||||
|
||||
func (k *Kubernetes) Derived(ctx context.Context) *Kubernetes {
|
||||
func (m *Manager) Derived(ctx context.Context) *Kubernetes {
|
||||
authorization, ok := ctx.Value(AuthorizationHeader).(string)
|
||||
if !ok || !strings.HasPrefix(authorization, "Bearer ") {
|
||||
return k
|
||||
return &Kubernetes{manager: m}
|
||||
}
|
||||
klog.V(5).Infof("%s header found (Bearer), using provided bearer token", AuthorizationHeader)
|
||||
derivedCfg := rest.CopyConfig(k.cfg)
|
||||
derivedCfg := rest.CopyConfig(m.cfg)
|
||||
derivedCfg.BearerToken = strings.TrimPrefix(authorization, "Bearer ")
|
||||
derivedCfg.BearerTokenFile = ""
|
||||
derivedCfg.Username = ""
|
||||
@@ -137,28 +139,42 @@ func (k *Kubernetes) Derived(ctx context.Context) *Kubernetes {
|
||||
derivedCfg.AuthConfigPersister = nil
|
||||
derivedCfg.ExecProvider = nil
|
||||
derivedCfg.Impersonate = rest.ImpersonationConfig{}
|
||||
clientCmdApiConfig, err := k.clientCmdConfig.RawConfig()
|
||||
clientCmdApiConfig, err := m.clientCmdConfig.RawConfig()
|
||||
if err != nil {
|
||||
return k
|
||||
return &Kubernetes{manager: m}
|
||||
}
|
||||
clientCmdApiConfig.AuthInfos = make(map[string]*clientcmdapi.AuthInfo)
|
||||
derived := &Kubernetes{
|
||||
Kubeconfig: k.Kubeconfig,
|
||||
derived := &Kubernetes{manager: &Manager{
|
||||
Kubeconfig: m.Kubeconfig,
|
||||
clientCmdConfig: clientcmd.NewDefaultClientConfig(clientCmdApiConfig, nil),
|
||||
cfg: derivedCfg,
|
||||
scheme: k.scheme,
|
||||
parameterCodec: k.parameterCodec,
|
||||
}
|
||||
derived.clientSet, err = kubernetes.NewForConfig(derived.cfg)
|
||||
scheme: m.scheme,
|
||||
parameterCodec: m.parameterCodec,
|
||||
}}
|
||||
derived.manager.clientSet, err = kubernetes.NewForConfig(derived.manager.cfg)
|
||||
if err != nil {
|
||||
return k
|
||||
return &Kubernetes{manager: m}
|
||||
}
|
||||
derived.discoveryClient = memory.NewMemCacheClient(discovery.NewDiscoveryClient(derived.clientSet.CoreV1().RESTClient()))
|
||||
derived.deferredDiscoveryRESTMapper = restmapper.NewDeferredDiscoveryRESTMapper(derived.discoveryClient)
|
||||
derived.dynamicClient, err = dynamic.NewForConfig(derived.cfg)
|
||||
derived.manager.discoveryClient = memory.NewMemCacheClient(discovery.NewDiscoveryClient(derived.manager.clientSet.CoreV1().RESTClient()))
|
||||
derived.manager.deferredDiscoveryRESTMapper = restmapper.NewDeferredDiscoveryRESTMapper(derived.manager.discoveryClient)
|
||||
derived.manager.dynamicClient, err = dynamic.NewForConfig(derived.manager.cfg)
|
||||
if err != nil {
|
||||
return k
|
||||
return &Kubernetes{manager: m}
|
||||
}
|
||||
derived.Helm = helm.NewHelm(derived)
|
||||
return derived
|
||||
}
|
||||
|
||||
// TODO: check test to see why cache isn't getting invalidated automatically https://github.com/manusa/kubernetes-mcp-server/pull/125#discussion_r2152194784
|
||||
func (k *Kubernetes) CacheInvalidate() {
|
||||
if k.manager.discoveryClient != nil {
|
||||
k.manager.discoveryClient.Invalidate()
|
||||
}
|
||||
if k.manager.deferredDiscoveryRESTMapper != nil {
|
||||
k.manager.deferredDiscoveryRESTMapper.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func (k *Kubernetes) NewHelm() *helm.Helm {
|
||||
// This is a derived Kubernetes, so it already has the Helm initialized
|
||||
return helm.NewHelm(k.manager)
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func (k *Kubernetes) IsOpenShift(ctx context.Context) bool {
|
||||
func (m *Manager) IsOpenShift(ctx context.Context) bool {
|
||||
// This method should be fast and not block (it's called at startup)
|
||||
timeoutSeconds := int64(5)
|
||||
if _, err := k.dynamicClient.Resource(schema.GroupVersionResource{
|
||||
if _, err := m.dynamicClient.Resource(schema.GroupVersionResource{
|
||||
Group: "project.openshift.io",
|
||||
Version: "v1",
|
||||
Resource: "projects",
|
||||
|
||||
@@ -49,7 +49,7 @@ func (k *Kubernetes) PodsGet(ctx context.Context, namespace, name string) (*unst
|
||||
|
||||
func (k *Kubernetes) PodsDelete(ctx context.Context, namespace, name string) (string, error) {
|
||||
namespace = k.NamespaceOrDefault(namespace)
|
||||
pod, err := k.clientSet.CoreV1().Pods(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
pod, err := k.manager.clientSet.CoreV1().Pods(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -62,18 +62,18 @@ func (k *Kubernetes) PodsDelete(ctx context.Context, namespace, name string) (st
|
||||
|
||||
// Delete managed service
|
||||
if isManaged {
|
||||
if sl, _ := k.clientSet.CoreV1().Services(namespace).List(ctx, metav1.ListOptions{
|
||||
if sl, _ := k.manager.clientSet.CoreV1().Services(namespace).List(ctx, metav1.ListOptions{
|
||||
LabelSelector: managedLabelSelector.String(),
|
||||
}); sl != nil {
|
||||
for _, svc := range sl.Items {
|
||||
_ = k.clientSet.CoreV1().Services(namespace).Delete(ctx, svc.Name, metav1.DeleteOptions{})
|
||||
_ = k.manager.clientSet.CoreV1().Services(namespace).Delete(ctx, svc.Name, metav1.DeleteOptions{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete managed Route
|
||||
if isManaged && k.supportsGroupVersion("route.openshift.io/v1") {
|
||||
routeResources := k.dynamicClient.
|
||||
routeResources := k.manager.dynamicClient.
|
||||
Resource(schema.GroupVersionResource{Group: "route.openshift.io", Version: "v1", Resource: "routes"}).
|
||||
Namespace(namespace)
|
||||
if rl, _ := routeResources.List(ctx, metav1.ListOptions{
|
||||
@@ -86,12 +86,12 @@ func (k *Kubernetes) PodsDelete(ctx context.Context, namespace, name string) (st
|
||||
|
||||
}
|
||||
return "Pod deleted successfully",
|
||||
k.clientSet.CoreV1().Pods(namespace).Delete(ctx, name, metav1.DeleteOptions{})
|
||||
k.manager.clientSet.CoreV1().Pods(namespace).Delete(ctx, name, metav1.DeleteOptions{})
|
||||
}
|
||||
|
||||
func (k *Kubernetes) PodsLog(ctx context.Context, namespace, name, container string) (string, error) {
|
||||
tailLines := int64(256)
|
||||
req := k.clientSet.CoreV1().Pods(k.NamespaceOrDefault(namespace)).GetLogs(name, &v1.PodLogOptions{
|
||||
req := k.manager.clientSet.CoreV1().Pods(k.NamespaceOrDefault(namespace)).GetLogs(name, &v1.PodLogOptions{
|
||||
TailLines: &tailLines,
|
||||
Container: container,
|
||||
})
|
||||
@@ -197,7 +197,7 @@ func (k *Kubernetes) PodsTop(ctx context.Context, options PodsTopOptions) (*metr
|
||||
} else {
|
||||
namespace = k.NamespaceOrDefault(namespace)
|
||||
}
|
||||
metricsClient, err := metricsclientset.NewForConfig(k.cfg)
|
||||
metricsClient, err := metricsclientset.NewForConfig(k.manager.cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create metrics client: %w", err)
|
||||
}
|
||||
@@ -220,7 +220,7 @@ func (k *Kubernetes) PodsTop(ctx context.Context, options PodsTopOptions) (*metr
|
||||
|
||||
func (k *Kubernetes) PodsExec(ctx context.Context, namespace, name, container string, command []string) (string, error) {
|
||||
namespace = k.NamespaceOrDefault(namespace)
|
||||
pod, err := k.clientSet.CoreV1().Pods(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
pod, err := k.manager.clientSet.CoreV1().Pods(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -260,18 +260,18 @@ func (k *Kubernetes) PodsExec(ctx context.Context, namespace, name, container st
|
||||
func (k *Kubernetes) createExecutor(namespace, name string, podExecOptions *v1.PodExecOptions) (remotecommand.Executor, error) {
|
||||
// Compute URL
|
||||
// https://github.com/kubernetes/kubectl/blob/5366de04e168bcbc11f5e340d131a9ca8b7d0df4/pkg/cmd/exec/exec.go#L382-L397
|
||||
req := k.clientSet.CoreV1().RESTClient().
|
||||
req := k.manager.clientSet.CoreV1().RESTClient().
|
||||
Post().
|
||||
Resource("pods").
|
||||
Namespace(namespace).
|
||||
Name(name).
|
||||
SubResource("exec")
|
||||
req.VersionedParams(podExecOptions, k.parameterCodec)
|
||||
spdyExec, err := remotecommand.NewSPDYExecutor(k.cfg, "POST", req.URL())
|
||||
req.VersionedParams(podExecOptions, k.manager.parameterCodec)
|
||||
spdyExec, err := remotecommand.NewSPDYExecutor(k.manager.cfg, "POST", req.URL())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
webSocketExec, err := remotecommand.NewWebSocketExecutor(k.cfg, "GET", req.URL().String())
|
||||
webSocketExec, err := remotecommand.NewWebSocketExecutor(k.manager.cfg, "GET", req.URL().String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -36,12 +36,12 @@ func (k *Kubernetes) ResourcesList(ctx context.Context, gvk *schema.GroupVersion
|
||||
// Check if operation is allowed for all namespaces (applicable for namespaced resources)
|
||||
isNamespaced, _ := k.isNamespaced(gvk)
|
||||
if isNamespaced && !k.canIUse(ctx, gvr, namespace, "list") && namespace == "" {
|
||||
namespace = k.configuredNamespace()
|
||||
namespace = k.manager.configuredNamespace()
|
||||
}
|
||||
if options.AsTable {
|
||||
return k.resourcesListAsTable(ctx, gvk, gvr, namespace, options)
|
||||
}
|
||||
return k.dynamicClient.Resource(*gvr).Namespace(namespace).List(ctx, options.ListOptions)
|
||||
return k.manager.dynamicClient.Resource(*gvr).Namespace(namespace).List(ctx, options.ListOptions)
|
||||
}
|
||||
|
||||
func (k *Kubernetes) ResourcesGet(ctx context.Context, gvk *schema.GroupVersionKind, namespace, name string) (*unstructured.Unstructured, error) {
|
||||
@@ -53,7 +53,7 @@ func (k *Kubernetes) ResourcesGet(ctx context.Context, gvk *schema.GroupVersionK
|
||||
if namespaced, nsErr := k.isNamespaced(gvk); nsErr == nil && namespaced {
|
||||
namespace = k.NamespaceOrDefault(namespace)
|
||||
}
|
||||
return k.dynamicClient.Resource(*gvr).Namespace(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
return k.manager.dynamicClient.Resource(*gvr).Namespace(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
func (k *Kubernetes) ResourcesCreateOrUpdate(ctx context.Context, resource string) ([]*unstructured.Unstructured, error) {
|
||||
@@ -79,7 +79,7 @@ func (k *Kubernetes) ResourcesDelete(ctx context.Context, gvk *schema.GroupVersi
|
||||
if namespaced, nsErr := k.isNamespaced(gvk); nsErr == nil && namespaced {
|
||||
namespace = k.NamespaceOrDefault(namespace)
|
||||
}
|
||||
return k.dynamicClient.Resource(*gvr).Namespace(namespace).Delete(ctx, name, metav1.DeleteOptions{})
|
||||
return k.manager.dynamicClient.Resource(*gvr).Namespace(namespace).Delete(ctx, name, metav1.DeleteOptions{})
|
||||
}
|
||||
|
||||
// resourcesListAsTable retrieves a list of resources in a table format.
|
||||
@@ -98,7 +98,7 @@ func (k *Kubernetes) resourcesListAsTable(ctx context.Context, gvk *schema.Group
|
||||
}
|
||||
url = append(url, gvr.Resource)
|
||||
var table metav1.Table
|
||||
err := k.clientSet.CoreV1().RESTClient().
|
||||
err := k.manager.clientSet.CoreV1().RESTClient().
|
||||
Get().
|
||||
SetHeader("Accept", strings.Join([]string{
|
||||
fmt.Sprintf("application/json;as=Table;v=%s;g=%s", metav1.SchemeGroupVersion.Version, metav1.GroupName),
|
||||
@@ -106,7 +106,7 @@ func (k *Kubernetes) resourcesListAsTable(ctx context.Context, gvk *schema.Group
|
||||
"application/json",
|
||||
}, ",")).
|
||||
AbsPath(url...).
|
||||
SpecificallyVersionedParams(&options.ListOptions, k.parameterCodec, schema.GroupVersion{Version: "v1"}).
|
||||
SpecificallyVersionedParams(&options.ListOptions, k.manager.parameterCodec, schema.GroupVersion{Version: "v1"}).
|
||||
Do(ctx).Into(&table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -141,7 +141,7 @@ func (k *Kubernetes) resourcesCreateOrUpdate(ctx context.Context, resources []*u
|
||||
if namespaced, nsErr := k.isNamespaced(&gvk); nsErr == nil && namespaced {
|
||||
namespace = k.NamespaceOrDefault(namespace)
|
||||
}
|
||||
resources[i], rErr = k.dynamicClient.Resource(*gvr).Namespace(namespace).Apply(ctx, obj.GetName(), obj, metav1.ApplyOptions{
|
||||
resources[i], rErr = k.manager.dynamicClient.Resource(*gvr).Namespace(namespace).Apply(ctx, obj.GetName(), obj, metav1.ApplyOptions{
|
||||
FieldManager: version.BinaryName,
|
||||
})
|
||||
if rErr != nil {
|
||||
@@ -149,14 +149,14 @@ func (k *Kubernetes) resourcesCreateOrUpdate(ctx context.Context, resources []*u
|
||||
}
|
||||
// Clear the cache to ensure the next operation is performed on the latest exposed APIs (will change after the CRD creation)
|
||||
if gvk.Kind == "CustomResourceDefinition" {
|
||||
k.deferredDiscoveryRESTMapper.Reset()
|
||||
k.manager.deferredDiscoveryRESTMapper.Reset()
|
||||
}
|
||||
}
|
||||
return resources, nil
|
||||
}
|
||||
|
||||
func (k *Kubernetes) resourceFor(gvk *schema.GroupVersionKind) (*schema.GroupVersionResource, error) {
|
||||
m, err := k.deferredDiscoveryRESTMapper.RESTMapping(schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, gvk.Version)
|
||||
m, err := k.manager.deferredDiscoveryRESTMapper.RESTMapping(schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, gvk.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -164,7 +164,7 @@ func (k *Kubernetes) resourceFor(gvk *schema.GroupVersionKind) (*schema.GroupVer
|
||||
}
|
||||
|
||||
func (k *Kubernetes) isNamespaced(gvk *schema.GroupVersionKind) (bool, error) {
|
||||
apiResourceList, err := k.discoveryClient.ServerResourcesForGroupVersion(gvk.GroupVersion().String())
|
||||
apiResourceList, err := k.manager.discoveryClient.ServerResourcesForGroupVersion(gvk.GroupVersion().String())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -177,14 +177,14 @@ func (k *Kubernetes) isNamespaced(gvk *schema.GroupVersionKind) (bool, error) {
|
||||
}
|
||||
|
||||
func (k *Kubernetes) supportsGroupVersion(groupVersion string) bool {
|
||||
if _, err := k.discoveryClient.ServerResourcesForGroupVersion(groupVersion); err != nil {
|
||||
if _, err := k.manager.discoveryClient.ServerResourcesForGroupVersion(groupVersion); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (k *Kubernetes) canIUse(ctx context.Context, gvr *schema.GroupVersionResource, namespace, verb string) bool {
|
||||
response, err := k.clientSet.AuthorizationV1().SelfSubjectAccessReviews().Create(ctx, &authv1.SelfSubjectAccessReview{
|
||||
response, err := k.manager.clientSet.AuthorizationV1().SelfSubjectAccessReviews().Create(ctx, &authv1.SelfSubjectAccessReview{
|
||||
Spec: authv1.SelfSubjectAccessReviewSpec{ResourceAttributes: &authv1.ResourceAttributes{
|
||||
Namespace: namespace,
|
||||
Verb: verb,
|
||||
|
||||
@@ -65,7 +65,7 @@ func (s *Server) helmInstall(ctx context.Context, ctr mcp.CallToolRequest) (*mcp
|
||||
if v, ok := ctr.GetArguments()["namespace"].(string); ok {
|
||||
namespace = v
|
||||
}
|
||||
ret, err := s.k.Derived(ctx).Helm.Install(ctx, chart, values, name, namespace)
|
||||
ret, err := s.k.Derived(ctx).NewHelm().Install(ctx, chart, values, name, namespace)
|
||||
if err != nil {
|
||||
return NewTextResult("", fmt.Errorf("failed to install helm chart '%s': %w", chart, err)), nil
|
||||
}
|
||||
@@ -81,7 +81,7 @@ func (s *Server) helmList(ctx context.Context, ctr mcp.CallToolRequest) (*mcp.Ca
|
||||
if v, ok := ctr.GetArguments()["namespace"].(string); ok {
|
||||
namespace = v
|
||||
}
|
||||
ret, err := s.k.Derived(ctx).Helm.List(namespace, allNamespaces)
|
||||
ret, err := s.k.Derived(ctx).NewHelm().List(namespace, allNamespaces)
|
||||
if err != nil {
|
||||
return NewTextResult("", fmt.Errorf("failed to list helm releases in namespace '%s': %w", namespace, err)), nil
|
||||
}
|
||||
@@ -98,7 +98,7 @@ func (s *Server) helmUninstall(ctx context.Context, ctr mcp.CallToolRequest) (*m
|
||||
if v, ok := ctr.GetArguments()["namespace"].(string); ok {
|
||||
namespace = v
|
||||
}
|
||||
ret, err := s.k.Derived(ctx).Helm.Uninstall(name, namespace)
|
||||
ret, err := s.k.Derived(ctx).NewHelm().Uninstall(name, namespace)
|
||||
if err != nil {
|
||||
return NewTextResult("", fmt.Errorf("failed to uninstall helm chart '%s': %w", name, err)), nil
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ type Configuration struct {
|
||||
type Server struct {
|
||||
configuration *Configuration
|
||||
server *server.MCPServer
|
||||
k *kubernetes.Kubernetes
|
||||
k *kubernetes.Manager
|
||||
}
|
||||
|
||||
func NewSever(configuration Configuration) (*Server, error) {
|
||||
@@ -49,7 +49,7 @@ func NewSever(configuration Configuration) (*Server, error) {
|
||||
}
|
||||
|
||||
func (s *Server) reloadKubernetesClient() error {
|
||||
k, err := kubernetes.NewKubernetes(s.configuration.Kubeconfig)
|
||||
k, err := kubernetes.NewManager(s.configuration.Kubeconfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -87,8 +87,7 @@ func TestPodsTop(t *testing.T) {
|
||||
})
|
||||
// Enable metrics API addon
|
||||
metricsApiAvailable = true
|
||||
d, _ := c.mcpServer.k.ToDiscoveryClient()
|
||||
d.Invalidate() // Force discovery client to refresh
|
||||
c.mcpServer.k.Derived(t.Context()).CacheInvalidate() // Force discovery client to refresh
|
||||
podsTopDefaults, err := c.callTool("pods_top", map[string]interface{}{})
|
||||
t.Run("pods_top defaults returns pod metrics from all namespaces", func(t *testing.T) {
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user