Skip to content

Commit 42b4c73

Browse files
committed
refactor(kubernetes)!: consolidate AccessControlClientset into Kubernetes struct
The AccessControlClientset was an unnecessary layer of indirection between the KubernetesClient interface and the Kubernetes implementation. This change merges the AccessControlClientset directly into the Kubernetes struct, simplifying the architecture and reducing complexity. - Merge KubernetesClientSet interface into KubernetesClient - Move all AccessControlClientset methods to Kubernetes struct - Delete accesscontrol.go and accesscontrol_client_set.go - Rename NewAccessControlClientset to NewKubernetes - Update all callers to use Kubernetes directly BREAKING CHANGE: AccessControlClientset() method removed from KubernetesClient interface. Callers should use the KubernetesClient methods directly. Signed-off-by: Marc Nuri <[email protected]>
1 parent 3959fd5 commit 42b4c73

26 files changed

+243
-270
lines changed

pkg/api/kubernetes.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ type NodesTopOptions struct {
4343
Name string
4444
}
4545

46-
type KubernetesClientSet interface {
46+
// KubernetesClient defines the interface for Kubernetes operations that tool and prompt handlers need.
47+
// This interface abstracts the concrete Kubernetes implementation to allow controlled access to the underlying resource APIs,
48+
// better decoupling, and testability.
49+
type KubernetesClient interface {
4750
genericclioptions.RESTClientGetter
4851
kubernetes.Interface
4952
// NamespaceOrDefault returns the provided namespace or the default configured namespace if empty
@@ -53,18 +56,8 @@ type KubernetesClientSet interface {
5356
DiscoveryClient() discovery.CachedDiscoveryInterface
5457
DynamicClient() dynamic.Interface
5558
MetricsV1beta1Client() *metricsv1beta1.MetricsV1beta1Client
56-
}
57-
58-
// KubernetesClient defines the interface for Kubernetes operations that tool and prompt handlers need.
59-
// This interface abstracts the concrete Kubernetes implementation to allow for better decoupling
60-
// and testability. The pkg/kubernetes.Kubernetes type implements this interface.
61-
//
62-
// For toolsets that need direct access to the Kubernetes clientset (e.g., for DynamicClient),
63-
// they can type-assert to ClientsetProvider.
64-
type KubernetesClient interface {
65-
// AccessControlClientset provides access to the underlying Kubernetes clientset with access control enforced
66-
AccessControlClientset() KubernetesClientSet
6759

60+
// TODO: To be removed in next iteration
6861
// --- Resource Operations ---
6962

7063
// ResourcesList lists resources of the specified GroupVersionKind

pkg/kubernetes/accesscontrol.go

Lines changed: 0 additions & 1 deletion
This file was deleted.

pkg/kubernetes/accesscontrol_client_set.go

Lines changed: 0 additions & 122 deletions
This file was deleted.

pkg/kubernetes/configuration.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func IsInCluster(cfg api.ClusterProvider) bool {
3434
// ConfigurationContextsDefault returns the current context name
3535
// TODO: Should be moved to the Provider level ?
3636
func (k *Kubernetes) ConfigurationContextsDefault() (string, error) {
37-
cfg, err := k.AccessControlClientset().ToRawKubeConfigLoader().RawConfig()
37+
cfg, err := k.ToRawKubeConfigLoader().RawConfig()
3838
if err != nil {
3939
return "", err
4040
}
@@ -44,7 +44,7 @@ func (k *Kubernetes) ConfigurationContextsDefault() (string, error) {
4444
// ConfigurationContextsList returns the list of available context names
4545
// TODO: Should be moved to the Provider level ?
4646
func (k *Kubernetes) ConfigurationContextsList() (map[string]string, error) {
47-
cfg, err := k.AccessControlClientset().ToRawKubeConfigLoader().RawConfig()
47+
cfg, err := k.ToRawKubeConfigLoader().RawConfig()
4848
if err != nil {
4949
return nil, err
5050
}
@@ -67,7 +67,7 @@ func (k *Kubernetes) ConfigurationContextsList() (map[string]string, error) {
6767
func (k *Kubernetes) ConfigurationView(minify bool) (runtime.Object, error) {
6868
var cfg clientcmdapi.Config
6969
var err error
70-
if cfg, err = k.AccessControlClientset().ToRawKubeConfigLoader().RawConfig(); err != nil {
70+
if cfg, err = k.ToRawKubeConfigLoader().RawConfig(); err != nil {
7171
return nil, err
7272
}
7373
if minify {

pkg/kubernetes/kubernetes.go

Lines changed: 110 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
11
package kubernetes
22

33
import (
4+
"fmt"
5+
"net/http"
6+
47
"github.com/containers/kubernetes-mcp-server/pkg/api"
8+
"k8s.io/apimachinery/pkg/api/meta"
59
"k8s.io/apimachinery/pkg/runtime"
10+
"k8s.io/client-go/discovery"
11+
"k8s.io/client-go/discovery/cached/memory"
12+
"k8s.io/client-go/dynamic"
13+
"k8s.io/client-go/kubernetes"
614
"k8s.io/client-go/kubernetes/scheme"
715
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
16+
"k8s.io/client-go/rest"
17+
"k8s.io/client-go/restmapper"
18+
"k8s.io/client-go/tools/clientcmd"
19+
metricsv1beta1 "k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1beta1"
820
)
921

1022
type HeaderKey string
@@ -21,14 +33,107 @@ type CloseWatchKubeConfig func() error
2133
var Scheme = scheme.Scheme
2234
var ParameterCodec = runtime.NewParameterCodec(Scheme)
2335

36+
// Kubernetes is a limited Kubernetes Client delegating interface to the standard kubernetes.Clientset
37+
// Only a limited set of functions are implemented with a single point of access to the kubernetes API where
38+
// apiVersion and kinds are checked for allowed access
2439
type Kubernetes struct {
25-
accessControlClientSet *AccessControlClientset
40+
kubernetes.Interface
41+
config api.BaseConfig
42+
clientCmdConfig clientcmd.ClientConfig
43+
restConfig *rest.Config
44+
restMapper meta.ResettableRESTMapper
45+
discoveryClient discovery.CachedDiscoveryInterface
46+
dynamicClient dynamic.Interface
47+
metricsV1beta1 *metricsv1beta1.MetricsV1beta1Client
2648
}
2749

2850
var _ api.KubernetesClient = (*Kubernetes)(nil)
2951

30-
// AccessControlClientset returns the access-controlled clientset
31-
// This ensures that any denied resources configured in the system are properly enforced
32-
func (k *Kubernetes) AccessControlClientset() api.KubernetesClientSet {
33-
return k.accessControlClientSet
52+
func NewKubernetes(config api.BaseConfig, clientCmdConfig clientcmd.ClientConfig, restConfig *rest.Config) (*Kubernetes, error) {
53+
k := &Kubernetes{
54+
config: config,
55+
clientCmdConfig: clientCmdConfig,
56+
restConfig: rest.CopyConfig(restConfig),
57+
}
58+
if k.restConfig.UserAgent == "" {
59+
k.restConfig.UserAgent = rest.DefaultKubernetesUserAgent()
60+
}
61+
k.restConfig.Wrap(func(original http.RoundTripper) http.RoundTripper {
62+
return &AccessControlRoundTripper{
63+
delegate: original,
64+
deniedResourcesProvider: config,
65+
restMapper: k.restMapper,
66+
}
67+
})
68+
discoveryClient, err := discovery.NewDiscoveryClientForConfig(k.restConfig)
69+
if err != nil {
70+
return nil, fmt.Errorf("failed to create discovery client: %v", err)
71+
}
72+
k.discoveryClient = memory.NewMemCacheClient(discoveryClient)
73+
k.restMapper = restmapper.NewDeferredDiscoveryRESTMapper(k.discoveryClient)
74+
k.Interface, err = kubernetes.NewForConfig(k.restConfig)
75+
if err != nil {
76+
return nil, err
77+
}
78+
k.dynamicClient, err = dynamic.NewForConfig(k.restConfig)
79+
if err != nil {
80+
return nil, err
81+
}
82+
k.metricsV1beta1, err = metricsv1beta1.NewForConfig(k.restConfig)
83+
if err != nil {
84+
return nil, err
85+
}
86+
return k, nil
87+
}
88+
89+
func (k *Kubernetes) RESTConfig() *rest.Config {
90+
return k.restConfig
91+
}
92+
93+
func (k *Kubernetes) RESTMapper() meta.ResettableRESTMapper {
94+
return k.restMapper
95+
}
96+
97+
func (k *Kubernetes) DiscoveryClient() discovery.CachedDiscoveryInterface {
98+
return k.discoveryClient
99+
}
100+
101+
func (k *Kubernetes) DynamicClient() dynamic.Interface {
102+
return k.dynamicClient
103+
}
104+
105+
func (k *Kubernetes) MetricsV1beta1Client() *metricsv1beta1.MetricsV1beta1Client {
106+
return k.metricsV1beta1
107+
}
108+
109+
func (k *Kubernetes) configuredNamespace() string {
110+
if ns, _, nsErr := k.ToRawKubeConfigLoader().Namespace(); nsErr == nil {
111+
return ns
112+
}
113+
return ""
114+
}
115+
116+
func (k *Kubernetes) NamespaceOrDefault(namespace string) string {
117+
if namespace == "" {
118+
return k.configuredNamespace()
119+
}
120+
return namespace
121+
}
122+
123+
func (k *Kubernetes) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
124+
return k.DiscoveryClient(), nil
125+
}
126+
127+
func (k *Kubernetes) ToRESTMapper() (meta.RESTMapper, error) {
128+
return k.RESTMapper(), nil
129+
}
130+
131+
// ToRESTConfig returns the rest.Config object (genericclioptions.RESTClientGetter)
132+
func (k *Kubernetes) ToRESTConfig() (*rest.Config, error) {
133+
return k.RESTConfig(), nil
134+
}
135+
136+
// ToRawKubeConfigLoader returns the clientcmd.ClientConfig object (genericclioptions.RESTClientGetter)
137+
func (k *Kubernetes) ToRawKubeConfigLoader() clientcmd.ClientConfig {
138+
return k.clientCmdConfig
34139
}

0 commit comments

Comments
 (0)