11package kubernetes
22
33import (
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-
6533var Scheme = scheme .Scheme
6634var 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-
21836func (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