Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,29 @@ import (
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/restmapper"
"k8s.io/client-go/tools/clientcmd"
metricsv1beta1 "k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1beta1"
)

// AccessControlClientset is a limited clientset delegating interface to the standard kubernetes.Clientset
// Only a limited set of functions are implemented with a single point of access to the kubernetes API where
// apiVersion and kinds are checked for allowed access
type AccessControlClientset struct {
cfg *rest.Config
kubernetes.Interface
staticConfig *config.StaticConfig
clientCmdConfig clientcmd.ClientConfig
cfg *rest.Config
restMapper meta.ResettableRESTMapper
discoveryClient discovery.CachedDiscoveryInterface
dynamicClient dynamic.Interface
metricsV1beta1 *metricsv1beta1.MetricsV1beta1Client
}

func NewAccessControlClientset(staticConfig *config.StaticConfig, restConfig *rest.Config) (*AccessControlClientset, error) {
rest.CopyConfig(restConfig)
func NewAccessControlClientset(staticConfig *config.StaticConfig, clientCmdConfig clientcmd.ClientConfig, restConfig *rest.Config) (*AccessControlClientset, error) {
acc := &AccessControlClientset{
cfg: rest.CopyConfig(restConfig),
staticConfig: staticConfig,
clientCmdConfig: clientCmdConfig,
cfg: rest.CopyConfig(restConfig),
}
if acc.cfg.UserAgent == "" {
acc.cfg.UserAgent = rest.DefaultKubernetesUserAgent()
Expand Down Expand Up @@ -111,3 +115,8 @@ func (a *AccessControlClientset) SelfSubjectAccessReviews() (authorizationv1.Sel
func (a *AccessControlClientset) TokenReview() (authenticationv1.TokenReviewInterface, error) {
return a.AuthenticationV1().TokenReviews(), nil
}

// ToRawKubeConfigLoader returns the clientcmd.ClientConfig object (genericclioptions.RESTClientGetter)
func (a *AccessControlClientset) ToRawKubeConfigLoader() clientcmd.ClientConfig {
return a.clientCmdConfig
}
11 changes: 7 additions & 4 deletions pkg/kubernetes/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,16 @@ func IsInCluster(cfg *config.StaticConfig) bool {
}

func (k *Kubernetes) NamespaceOrDefault(namespace string) string {
return k.manager.NamespaceOrDefault(namespace)
if namespace == "" {
return k.configuredNamespace()
}
return namespace
}

// ConfigurationContextsDefault returns the current context name
// TODO: Should be moved to the Provider level ?
func (k *Kubernetes) ConfigurationContextsDefault() (string, error) {
cfg, err := k.manager.clientCmdConfig.RawConfig()
cfg, err := k.ToRawKubeConfigLoader().RawConfig()
if err != nil {
return "", err
}
Expand All @@ -48,7 +51,7 @@ func (k *Kubernetes) ConfigurationContextsDefault() (string, error) {
// ConfigurationContextsList returns the list of available context names
// TODO: Should be moved to the Provider level ?
func (k *Kubernetes) ConfigurationContextsList() (map[string]string, error) {
cfg, err := k.manager.clientCmdConfig.RawConfig()
cfg, err := k.ToRawKubeConfigLoader().RawConfig()
if err != nil {
return nil, err
}
Expand All @@ -71,7 +74,7 @@ func (k *Kubernetes) ConfigurationContextsList() (map[string]string, error) {
func (k *Kubernetes) ConfigurationView(minify bool) (runtime.Object, error) {
var cfg clientcmdapi.Config
var err error
if cfg, err = k.manager.clientCmdConfig.RawConfig(); err != nil {
if cfg, err = k.ToRawKubeConfigLoader().RawConfig(); err != nil {
return nil, err
}
if minify {
Expand Down
44 changes: 37 additions & 7 deletions pkg/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package kubernetes

import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/discovery"
"k8s.io/client-go/kubernetes/scheme"
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"

"github.com/containers/kubernetes-mcp-server/pkg/helm"
"github.com/containers/kubernetes-mcp-server/pkg/kiali"
Expand All @@ -20,26 +24,52 @@ const (

type CloseWatchKubeConfig func() error

var Scheme = scheme.Scheme
var ParameterCodec = runtime.NewParameterCodec(Scheme)

type Kubernetes struct {
manager *Manager
accessControlClientSet *AccessControlClientset
}

var _ helm.Kubernetes = (*Kubernetes)(nil)

// AccessControlClientset returns the access-controlled clientset
// This ensures that any denied resources configured in the system are properly enforced
func (k *Kubernetes) AccessControlClientset() *AccessControlClientset {
return k.manager.accessControlClientSet
return k.accessControlClientSet
}

var Scheme = scheme.Scheme
var ParameterCodec = runtime.NewParameterCodec(Scheme)

func (k *Kubernetes) NewHelm() *helm.Helm {
// This is a derived Kubernetes, so it already has the Helm initialized
return helm.NewHelm(k.manager)
return helm.NewHelm(k)
}

// NewKiali returns a Kiali client initialized with the same StaticConfig and bearer token
// as the underlying derived Kubernetes manager.
func (k *Kubernetes) NewKiali() *kiali.Kiali {
return kiali.NewKiali(k.manager.staticConfig, k.AccessControlClientset().cfg)
return kiali.NewKiali(k.AccessControlClientset().staticConfig, k.AccessControlClientset().cfg)
}

func (k *Kubernetes) configuredNamespace() string {
if ns, _, nsErr := k.AccessControlClientset().ToRawKubeConfigLoader().Namespace(); nsErr == nil {
return ns
}
return ""
}

func (k *Kubernetes) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
return k.AccessControlClientset().DiscoveryClient(), nil
}

func (k *Kubernetes) ToRESTMapper() (meta.RESTMapper, error) {
return k.AccessControlClientset().RESTMapper(), nil
}

// ToRESTConfig returns the rest.Config object (genericclioptions.RESTClientGetter)
func (k *Kubernetes) ToRESTConfig() (*rest.Config, error) {
return k.AccessControlClientset().cfg, nil
}
Comment on lines +60 to +71
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@manusa do we want to keep the error in the return types here? I understand changing that would be a breaking change downstream, but I think long term it could simplify our code somewhat if we don't anticipate changes re-introducing the error value here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These methods are the implementation of the genericclioptions.RESTClientGetter (note they basically duplicate the ones already available through the AccessControlClientset).

These were before at the Manager level (here I'm trying to trim off any access to Kubernetes)

It's my understanding that other clients, besides helm, might end up using this interface. That's the main reasoning to having them here.


func (k *Kubernetes) ToRawKubeConfigLoader() clientcmd.ClientConfig {
return k.AccessControlClientset().ToRawKubeConfigLoader()
}
Loading