Skip to content

Commit 4a0914e

Browse files
authored
refactor(api)!: extract operational methods from KubernetesClient to Core struct (#589)
Move all operational methods (pods, nodes, namespaces, events, resources, and configuration) from the KubernetesClient interface to a separate Core struct in pkg/kubernetes. This slims down the KubernetesClient interface to only expose core client accessors while providing the operational methods through a composable wrapper. Consumers now access operational methods via kubernetes.NewCore(client) instead of calling them directly on the KubernetesClient interface. Signed-off-by: Marc Nuri <[email protected]>
1 parent 0452558 commit 4a0914e

File tree

15 files changed

+142
-187
lines changed

15 files changed

+142
-187
lines changed

pkg/api/kubernetes.go

Lines changed: 5 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,11 @@ import (
55

66
"k8s.io/apimachinery/pkg/api/meta"
77
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8-
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
9-
"k8s.io/apimachinery/pkg/runtime"
10-
"k8s.io/apimachinery/pkg/runtime/schema"
118
"k8s.io/cli-runtime/pkg/genericclioptions"
129
"k8s.io/client-go/discovery"
1310
"k8s.io/client-go/dynamic"
1411
"k8s.io/client-go/kubernetes"
1512
"k8s.io/client-go/rest"
16-
"k8s.io/metrics/pkg/apis/metrics"
1713
metricsv1beta1 "k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1beta1"
1814
)
1915

@@ -51,72 +47,14 @@ type KubernetesClient interface {
5147
kubernetes.Interface
5248
// NamespaceOrDefault returns the provided namespace or the default configured namespace if empty
5349
NamespaceOrDefault(namespace string) string
50+
// RESTConfig returns the REST config used to create clients
5451
RESTConfig() *rest.Config
52+
// RESTMapper returns the REST mapper used to map GVK to GVR
5553
RESTMapper() meta.ResettableRESTMapper
54+
// DiscoveryClient returns the cached discovery client
5655
DiscoveryClient() discovery.CachedDiscoveryInterface
56+
// DynamicClient returns the dynamic client
5757
DynamicClient() dynamic.Interface
58+
// MetricsV1beta1Client returns the metrics v1beta1 client
5859
MetricsV1beta1Client() *metricsv1beta1.MetricsV1beta1Client
59-
60-
// TODO: To be removed in next iteration
61-
// --- Resource Operations ---
62-
63-
// ResourcesList lists resources of the specified GroupVersionKind
64-
ResourcesList(ctx context.Context, gvk *schema.GroupVersionKind, namespace string, options ListOptions) (runtime.Unstructured, error)
65-
// ResourcesGet retrieves a single resource by name
66-
ResourcesGet(ctx context.Context, gvk *schema.GroupVersionKind, namespace, name string) (*unstructured.Unstructured, error)
67-
// ResourcesCreateOrUpdate creates or updates resources from a YAML/JSON string
68-
ResourcesCreateOrUpdate(ctx context.Context, resource string) ([]*unstructured.Unstructured, error)
69-
// ResourcesDelete deletes a resource by name
70-
ResourcesDelete(ctx context.Context, gvk *schema.GroupVersionKind, namespace, name string) error
71-
// ResourcesScale gets or sets the scale of a resource
72-
ResourcesScale(ctx context.Context, gvk *schema.GroupVersionKind, namespace, name string, desiredScale int64, shouldScale bool) (*unstructured.Unstructured, error)
73-
74-
// --- Namespace Operations ---
75-
76-
// NamespacesList lists all namespaces
77-
NamespacesList(ctx context.Context, options ListOptions) (runtime.Unstructured, error)
78-
// ProjectsList lists all OpenShift projects
79-
ProjectsList(ctx context.Context, options ListOptions) (runtime.Unstructured, error)
80-
81-
// --- Pod Operations ---
82-
83-
// PodsListInAllNamespaces lists pods across all namespaces
84-
PodsListInAllNamespaces(ctx context.Context, options ListOptions) (runtime.Unstructured, error)
85-
// PodsListInNamespace lists pods in a specific namespace
86-
PodsListInNamespace(ctx context.Context, namespace string, options ListOptions) (runtime.Unstructured, error)
87-
// PodsGet retrieves a single pod by name
88-
PodsGet(ctx context.Context, namespace, name string) (*unstructured.Unstructured, error)
89-
// PodsDelete deletes a pod and its managed resources
90-
PodsDelete(ctx context.Context, namespace, name string) (string, error)
91-
// PodsLog retrieves logs from a pod container
92-
PodsLog(ctx context.Context, namespace, name, container string, previous bool, tail int64) (string, error)
93-
// PodsRun creates and runs a new pod with optional service and route
94-
PodsRun(ctx context.Context, namespace, name, image string, port int32) ([]*unstructured.Unstructured, error)
95-
// PodsTop retrieves pod metrics
96-
PodsTop(ctx context.Context, options PodsTopOptions) (*metrics.PodMetricsList, error)
97-
// PodsExec executes a command in a pod container
98-
PodsExec(ctx context.Context, namespace, name, container string, command []string) (string, error)
99-
100-
// --- Node Operations ---
101-
102-
// NodesLog retrieves logs from a node
103-
NodesLog(ctx context.Context, name string, query string, tailLines int64) (string, error)
104-
// NodesStatsSummary retrieves stats summary from a node
105-
NodesStatsSummary(ctx context.Context, name string) (string, error)
106-
// NodesTop retrieves node metrics
107-
NodesTop(ctx context.Context, options NodesTopOptions) (*metrics.NodeMetricsList, error)
108-
109-
// --- Event Operations ---
110-
111-
// EventsList lists events in a namespace
112-
EventsList(ctx context.Context, namespace string) ([]map[string]any, error)
113-
114-
// --- Configuration Operations ---
115-
116-
// ConfigurationContextsList returns the list of available context names
117-
ConfigurationContextsList() (map[string]string, error)
118-
// ConfigurationContextsDefault returns the current context name
119-
ConfigurationContextsDefault() (string, error)
120-
// ConfigurationView returns the kubeconfig content
121-
ConfigurationView(minify bool) (runtime.Object, error)
12260
}

pkg/kubernetes/configuration.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ func IsInCluster(cfg api.ClusterProvider) bool {
3333

3434
// ConfigurationContextsDefault returns the current context name
3535
// TODO: Should be moved to the Provider level ?
36-
func (k *Kubernetes) ConfigurationContextsDefault() (string, error) {
37-
cfg, err := k.ToRawKubeConfigLoader().RawConfig()
36+
func (c *Core) ConfigurationContextsDefault() (string, error) {
37+
cfg, err := c.ToRawKubeConfigLoader().RawConfig()
3838
if err != nil {
3939
return "", err
4040
}
@@ -43,8 +43,8 @@ func (k *Kubernetes) ConfigurationContextsDefault() (string, error) {
4343

4444
// ConfigurationContextsList returns the list of available context names
4545
// TODO: Should be moved to the Provider level ?
46-
func (k *Kubernetes) ConfigurationContextsList() (map[string]string, error) {
47-
cfg, err := k.ToRawKubeConfigLoader().RawConfig()
46+
func (c *Core) ConfigurationContextsList() (map[string]string, error) {
47+
cfg, err := c.ToRawKubeConfigLoader().RawConfig()
4848
if err != nil {
4949
return nil, err
5050
}
@@ -64,10 +64,10 @@ func (k *Kubernetes) ConfigurationContextsList() (map[string]string, error) {
6464
// If minify is true, keeps only the current-context and the relevant pieces of the configuration for that context.
6565
// If minify is false, all contexts, clusters, auth-infos, and users are returned in the configuration.
6666
// TODO: Should be moved to the Provider level ?
67-
func (k *Kubernetes) ConfigurationView(minify bool) (runtime.Object, error) {
67+
func (c *Core) ConfigurationView(minify bool) (runtime.Object, error) {
6868
var cfg clientcmdapi.Config
6969
var err error
70-
if cfg, err = k.ToRawKubeConfigLoader().RawConfig(); err != nil {
70+
if cfg, err = c.ToRawKubeConfigLoader().RawConfig(); err != nil {
7171
return nil, err
7272
}
7373
if minify {

pkg/kubernetes/core.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package kubernetes
2+
3+
import "github.com/containers/kubernetes-mcp-server/pkg/api"
4+
5+
type Core struct {
6+
api.KubernetesClient
7+
}
8+
9+
func NewCore(client api.KubernetesClient) *Core {
10+
return &Core{
11+
KubernetesClient: client,
12+
}
13+
}

pkg/kubernetes/events.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import (
1111
"k8s.io/apimachinery/pkg/runtime/schema"
1212
)
1313

14-
func (k *Kubernetes) EventsList(ctx context.Context, namespace string) ([]map[string]any, error) {
14+
func (c *Core) EventsList(ctx context.Context, namespace string) ([]map[string]any, error) {
1515
var eventMap []map[string]any
16-
raw, err := k.ResourcesList(ctx, &schema.GroupVersionKind{
16+
raw, err := c.ResourcesList(ctx, &schema.GroupVersionKind{
1717
Group: "", Version: "v1", Kind: "Event",
1818
}, namespace, api.ListOptions{})
1919
if err != nil {

pkg/kubernetes/namespaces.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ import (
88
"k8s.io/apimachinery/pkg/runtime/schema"
99
)
1010

11-
func (k *Kubernetes) NamespacesList(ctx context.Context, options api.ListOptions) (runtime.Unstructured, error) {
12-
return k.ResourcesList(ctx, &schema.GroupVersionKind{
11+
func (c *Core) NamespacesList(ctx context.Context, options api.ListOptions) (runtime.Unstructured, error) {
12+
return c.ResourcesList(ctx, &schema.GroupVersionKind{
1313
Group: "", Version: "v1", Kind: "Namespace",
1414
}, "", options)
1515
}
1616

17-
func (k *Kubernetes) ProjectsList(ctx context.Context, options api.ListOptions) (runtime.Unstructured, error) {
18-
return k.ResourcesList(ctx, &schema.GroupVersionKind{
17+
func (c *Core) ProjectsList(ctx context.Context, options api.ListOptions) (runtime.Unstructured, error) {
18+
return c.ResourcesList(ctx, &schema.GroupVersionKind{
1919
Group: "project.openshift.io", Version: "v1", Kind: "Project",
2020
}, "", options)
2121
}

pkg/kubernetes/nodes.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,19 @@ import (
1111
metricsv1beta1api "k8s.io/metrics/pkg/apis/metrics/v1beta1"
1212
)
1313

14-
func (k *Kubernetes) NodesLog(ctx context.Context, name string, query string, tailLines int64) (string, error) {
14+
func (c *Core) NodesLog(ctx context.Context, name string, query string, tailLines int64) (string, error) {
1515
// Use the node proxy API to access logs from the kubelet
1616
// https://kubernetes.io/docs/concepts/cluster-administration/system-logs/#log-query
1717
// Common log paths:
1818
// - /var/log/kubelet.log - kubelet logs
1919
// - /var/log/kube-proxy.log - kube-proxy logs
2020
// - /var/log/containers/ - container logs
2121

22-
if _, err := k.CoreV1().Nodes().Get(ctx, name, metav1.GetOptions{}); err != nil {
22+
if _, err := c.CoreV1().Nodes().Get(ctx, name, metav1.GetOptions{}); err != nil {
2323
return "", fmt.Errorf("failed to get node %s: %w", name, err)
2424
}
2525

26-
req := k.CoreV1().RESTClient().
26+
req := c.CoreV1().RESTClient().
2727
Get().
2828
AbsPath("api", "v1", "nodes", name, "proxy", "logs")
2929
req.Param("query", query)
@@ -45,16 +45,16 @@ func (k *Kubernetes) NodesLog(ctx context.Context, name string, query string, ta
4545
return string(rawData), nil
4646
}
4747

48-
func (k *Kubernetes) NodesStatsSummary(ctx context.Context, name string) (string, error) {
48+
func (c *Core) NodesStatsSummary(ctx context.Context, name string) (string, error) {
4949
// Use the node proxy API to access stats summary from the kubelet
5050
// https://kubernetes.io/docs/reference/instrumentation/understand-psi-metrics/
5151
// This endpoint provides CPU, memory, filesystem, and network statistics
5252

53-
if _, err := k.CoreV1().Nodes().Get(ctx, name, metav1.GetOptions{}); err != nil {
53+
if _, err := c.CoreV1().Nodes().Get(ctx, name, metav1.GetOptions{}); err != nil {
5454
return "", fmt.Errorf("failed to get node %s: %w", name, err)
5555
}
5656

57-
result := k.CoreV1().RESTClient().
57+
result := c.CoreV1().RESTClient().
5858
Get().
5959
AbsPath("api", "v1", "nodes", name, "proxy", "stats", "summary").
6060
Do(ctx)
@@ -70,21 +70,21 @@ func (k *Kubernetes) NodesStatsSummary(ctx context.Context, name string) (string
7070
return string(rawData), nil
7171
}
7272

73-
func (k *Kubernetes) NodesTop(ctx context.Context, options api.NodesTopOptions) (*metrics.NodeMetricsList, error) {
73+
func (c *Core) NodesTop(ctx context.Context, options api.NodesTopOptions) (*metrics.NodeMetricsList, error) {
7474
// TODO, maybe move to mcp Tools setup and omit in case metrics aren't available in the target cluster
75-
if !k.supportsGroupVersion(metrics.GroupName + "/" + metricsv1beta1api.SchemeGroupVersion.Version) {
75+
if !c.supportsGroupVersion(metrics.GroupName + "/" + metricsv1beta1api.SchemeGroupVersion.Version) {
7676
return nil, errors.New("metrics API is not available")
7777
}
7878
versionedMetrics := &metricsv1beta1api.NodeMetricsList{}
7979
var err error
8080
if options.Name != "" {
81-
m, err := k.MetricsV1beta1Client().NodeMetricses().Get(ctx, options.Name, metav1.GetOptions{})
81+
m, err := c.MetricsV1beta1Client().NodeMetricses().Get(ctx, options.Name, metav1.GetOptions{})
8282
if err != nil {
8383
return nil, fmt.Errorf("failed to get metrics for node %s: %w", options.Name, err)
8484
}
8585
versionedMetrics.Items = []metricsv1beta1api.NodeMetrics{*m}
8686
} else {
87-
versionedMetrics, err = k.MetricsV1beta1Client().NodeMetricses().List(ctx, options.ListOptions)
87+
versionedMetrics, err = c.MetricsV1beta1Client().NodeMetricses().List(ctx, options.ListOptions)
8888
if err != nil {
8989
return nil, fmt.Errorf("failed to list node metrics: %w", err)
9090
}

0 commit comments

Comments
 (0)