Skip to content

Commit f3a4466

Browse files
manusaCali0707
andauthored
refactor(kubernetes): keep Provider as only external Kubernetes interface (#372)
* refactor(kubernetes): keep Provider as only external Kubernetes interface Initial phase to unify-merge the Provider interface with the Manager struct. - Renamed ManagerProvider to Provider (i.e. kubernets.Provider) - Moved Manager related logic to specific files - Exposed relevant method through Provider interface (GetDerivedKubernetes, IsOpenShift, VerifyToken) Signed-off-by: Marc Nuri <[email protected]> * Update pkg/kubernetes/provider_kubeconfig.go Co-authored-by: Calum Murray <[email protected]> Signed-off-by: Marc Nuri <[email protected]> --------- Signed-off-by: Marc Nuri <[email protected]> Co-authored-by: Calum Murray <[email protected]>
1 parent 1e154d7 commit f3a4466

File tree

14 files changed

+361
-336
lines changed

14 files changed

+361
-336
lines changed

pkg/http/authorization.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323

2424
type KubernetesApiTokenVerifier interface {
2525
// KubernetesApiVerifyToken TODO: clarify proper implementation
26-
KubernetesApiVerifyToken(ctx context.Context, token, audience, cluster string) (*authenticationapiv1.UserInfo, []string, error)
26+
KubernetesApiVerifyToken(ctx context.Context, cluster, token, audience string) (*authenticationapiv1.UserInfo, []string, error)
2727
// GetTargetParameterName returns the parameter name used for target identification in MCP requests
2828
GetTargetParameterName() string
2929
}
@@ -247,7 +247,7 @@ func (c *JWTClaims) ValidateWithProvider(ctx context.Context, audience string, p
247247

248248
func (c *JWTClaims) ValidateWithKubernetesApi(ctx context.Context, audience, cluster string, verifier KubernetesApiTokenVerifier) error {
249249
if verifier != nil {
250-
_, _, err := verifier.KubernetesApiVerifyToken(ctx, c.Token, audience, cluster)
250+
_, _, err := verifier.KubernetesApiVerifyToken(ctx, cluster, c.Token, audience)
251251
if err != nil {
252252
return fmt.Errorf("kubernetes API token validation error: %v", err)
253253
}

pkg/kubernetes/configuration.go

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -47,42 +47,10 @@ func resolveKubernetesConfigurations(kubernetes *Manager) error {
4747
return err
4848
}
4949

50-
func (m *Manager) IsInCluster() bool {
51-
if m.staticConfig.KubeConfig != "" {
52-
return false
53-
}
54-
cfg, err := InClusterConfig()
55-
return err == nil && cfg != nil
56-
}
57-
58-
func (m *Manager) configuredNamespace() string {
59-
if ns, _, nsErr := m.clientCmdConfig.Namespace(); nsErr == nil {
60-
return ns
61-
}
62-
return ""
63-
}
64-
65-
func (m *Manager) NamespaceOrDefault(namespace string) string {
66-
if namespace == "" {
67-
return m.configuredNamespace()
68-
}
69-
return namespace
70-
}
71-
7250
func (k *Kubernetes) NamespaceOrDefault(namespace string) string {
7351
return k.manager.NamespaceOrDefault(namespace)
7452
}
7553

76-
// ToRESTConfig returns the rest.Config object (genericclioptions.RESTClientGetter)
77-
func (m *Manager) ToRESTConfig() (*rest.Config, error) {
78-
return m.cfg, nil
79-
}
80-
81-
// ToRawKubeConfigLoader returns the clientcmd.ClientConfig object (genericclioptions.RESTClientGetter)
82-
func (m *Manager) ToRawKubeConfigLoader() clientcmd.ClientConfig {
83-
return m.clientCmdConfig
84-
}
85-
8654
// ConfigurationContextsDefault returns the current context name
8755
// TODO: Should be moved to the Provider level ?
8856
func (k *Kubernetes) ConfigurationContextsDefault() (string, error) {

pkg/kubernetes/kubernetes.go

Lines changed: 1 addition & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,10 @@
11
package kubernetes
22

33
import (
4-
"context"
5-
"errors"
6-
"strings"
7-
84
"k8s.io/apimachinery/pkg/runtime"
95

10-
"github.com/fsnotify/fsnotify"
11-
12-
"k8s.io/apimachinery/pkg/api/meta"
13-
"k8s.io/client-go/discovery"
14-
"k8s.io/client-go/discovery/cached/memory"
15-
"k8s.io/client-go/dynamic"
16-
"k8s.io/client-go/kubernetes/scheme"
17-
"k8s.io/client-go/rest"
18-
"k8s.io/client-go/restmapper"
19-
"k8s.io/client-go/tools/clientcmd"
20-
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
21-
"k8s.io/klog/v2"
22-
23-
"github.com/containers/kubernetes-mcp-server/pkg/config"
246
"github.com/containers/kubernetes-mcp-server/pkg/helm"
7+
"k8s.io/client-go/kubernetes/scheme"
258

269
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
2710
)
@@ -47,174 +30,9 @@ func (k *Kubernetes) AccessControlClientset() *AccessControlClientset {
4730
return k.manager.accessControlClientSet
4831
}
4932

50-
type Manager struct {
51-
cfg *rest.Config
52-
clientCmdConfig clientcmd.ClientConfig
53-
discoveryClient discovery.CachedDiscoveryInterface
54-
accessControlClientSet *AccessControlClientset
55-
accessControlRESTMapper *AccessControlRESTMapper
56-
dynamicClient *dynamic.DynamicClient
57-
58-
staticConfig *config.StaticConfig
59-
CloseWatchKubeConfig CloseWatchKubeConfig
60-
}
61-
62-
var _ helm.Kubernetes = (*Manager)(nil)
63-
var _ Openshift = (*Manager)(nil)
64-
6533
var Scheme = scheme.Scheme
6634
var ParameterCodec = runtime.NewParameterCodec(Scheme)
6735

68-
func NewManager(config *config.StaticConfig) (*Manager, error) {
69-
k8s := &Manager{
70-
staticConfig: config,
71-
}
72-
if err := resolveKubernetesConfigurations(k8s); err != nil {
73-
return nil, err
74-
}
75-
// TODO: Won't work because not all client-go clients use the shared context (e.g. discovery client uses context.TODO())
76-
//k8s.cfg.Wrap(func(original http.RoundTripper) http.RoundTripper {
77-
// return &impersonateRoundTripper{original}
78-
//})
79-
var err error
80-
k8s.accessControlClientSet, err = NewAccessControlClientset(k8s.cfg, k8s.staticConfig)
81-
if err != nil {
82-
return nil, err
83-
}
84-
k8s.discoveryClient = memory.NewMemCacheClient(k8s.accessControlClientSet.DiscoveryClient())
85-
k8s.accessControlRESTMapper = NewAccessControlRESTMapper(
86-
restmapper.NewDeferredDiscoveryRESTMapper(k8s.discoveryClient),
87-
k8s.staticConfig,
88-
)
89-
k8s.dynamicClient, err = dynamic.NewForConfig(k8s.cfg)
90-
if err != nil {
91-
return nil, err
92-
}
93-
return k8s, nil
94-
}
95-
96-
func (m *Manager) WatchKubeConfig(onKubeConfigChange func() error) {
97-
if m.clientCmdConfig == nil {
98-
return
99-
}
100-
kubeConfigFiles := m.clientCmdConfig.ConfigAccess().GetLoadingPrecedence()
101-
if len(kubeConfigFiles) == 0 {
102-
return
103-
}
104-
watcher, err := fsnotify.NewWatcher()
105-
if err != nil {
106-
return
107-
}
108-
for _, file := range kubeConfigFiles {
109-
_ = watcher.Add(file)
110-
}
111-
go func() {
112-
for {
113-
select {
114-
case _, ok := <-watcher.Events:
115-
if !ok {
116-
return
117-
}
118-
_ = onKubeConfigChange()
119-
case _, ok := <-watcher.Errors:
120-
if !ok {
121-
return
122-
}
123-
}
124-
}
125-
}()
126-
if m.CloseWatchKubeConfig != nil {
127-
_ = m.CloseWatchKubeConfig()
128-
}
129-
m.CloseWatchKubeConfig = watcher.Close
130-
}
131-
132-
func (m *Manager) Close() {
133-
if m.CloseWatchKubeConfig != nil {
134-
_ = m.CloseWatchKubeConfig()
135-
}
136-
}
137-
138-
func (m *Manager) GetAPIServerHost() string {
139-
if m.cfg == nil {
140-
return ""
141-
}
142-
return m.cfg.Host
143-
}
144-
145-
func (m *Manager) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
146-
return m.discoveryClient, nil
147-
}
148-
149-
func (m *Manager) ToRESTMapper() (meta.RESTMapper, error) {
150-
return m.accessControlRESTMapper, nil
151-
}
152-
153-
func (m *Manager) Derived(ctx context.Context) (*Kubernetes, error) {
154-
authorization, ok := ctx.Value(OAuthAuthorizationHeader).(string)
155-
if !ok || !strings.HasPrefix(authorization, "Bearer ") {
156-
if m.staticConfig.RequireOAuth {
157-
return nil, errors.New("oauth token required")
158-
}
159-
return &Kubernetes{manager: m}, nil
160-
}
161-
klog.V(5).Infof("%s header found (Bearer), using provided bearer token", OAuthAuthorizationHeader)
162-
derivedCfg := &rest.Config{
163-
Host: m.cfg.Host,
164-
APIPath: m.cfg.APIPath,
165-
// Copy only server verification TLS settings (CA bundle and server name)
166-
TLSClientConfig: rest.TLSClientConfig{
167-
Insecure: m.cfg.Insecure,
168-
ServerName: m.cfg.ServerName,
169-
CAFile: m.cfg.CAFile,
170-
CAData: m.cfg.CAData,
171-
},
172-
BearerToken: strings.TrimPrefix(authorization, "Bearer "),
173-
// pass custom UserAgent to identify the client
174-
UserAgent: CustomUserAgent,
175-
QPS: m.cfg.QPS,
176-
Burst: m.cfg.Burst,
177-
Timeout: m.cfg.Timeout,
178-
Impersonate: rest.ImpersonationConfig{},
179-
}
180-
clientCmdApiConfig, err := m.clientCmdConfig.RawConfig()
181-
if err != nil {
182-
if m.staticConfig.RequireOAuth {
183-
klog.Errorf("failed to get kubeconfig: %v", err)
184-
return nil, errors.New("failed to get kubeconfig")
185-
}
186-
return &Kubernetes{manager: m}, nil
187-
}
188-
clientCmdApiConfig.AuthInfos = make(map[string]*clientcmdapi.AuthInfo)
189-
derived := &Kubernetes{manager: &Manager{
190-
clientCmdConfig: clientcmd.NewDefaultClientConfig(clientCmdApiConfig, nil),
191-
cfg: derivedCfg,
192-
staticConfig: m.staticConfig,
193-
}}
194-
derived.manager.accessControlClientSet, err = NewAccessControlClientset(derived.manager.cfg, derived.manager.staticConfig)
195-
if err != nil {
196-
if m.staticConfig.RequireOAuth {
197-
klog.Errorf("failed to get kubeconfig: %v", err)
198-
return nil, errors.New("failed to get kubeconfig")
199-
}
200-
return &Kubernetes{manager: m}, nil
201-
}
202-
derived.manager.discoveryClient = memory.NewMemCacheClient(derived.manager.accessControlClientSet.DiscoveryClient())
203-
derived.manager.accessControlRESTMapper = NewAccessControlRESTMapper(
204-
restmapper.NewDeferredDiscoveryRESTMapper(derived.manager.discoveryClient),
205-
derived.manager.staticConfig,
206-
)
207-
derived.manager.dynamicClient, err = dynamic.NewForConfig(derived.manager.cfg)
208-
if err != nil {
209-
if m.staticConfig.RequireOAuth {
210-
klog.Errorf("failed to initialize dynamic client: %v", err)
211-
return nil, errors.New("failed to initialize dynamic client")
212-
}
213-
return &Kubernetes{manager: m}, nil
214-
}
215-
return derived, nil
216-
}
217-
21836
func (k *Kubernetes) NewHelm() *helm.Helm {
21937
// This is a derived Kubernetes, so it already has the Helm initialized
22038
return helm.NewHelm(k.manager)

0 commit comments

Comments
 (0)