mirror of
https://github.com/containers/kubernetes-mcp-server.git
synced 2025-10-23 01:22:57 +03:00
test(config): add new test case to increase the test coverage of Derived Config (167)
Add new unit tests to check the values in Derived config --- Rely on kubeconfig in staticConfig instead of a separate but equal one
This commit is contained in:
@@ -24,8 +24,8 @@ var InClusterConfig = func() (*rest.Config, error) {
|
||||
func resolveKubernetesConfigurations(kubernetes *Manager) error {
|
||||
// Always set clientCmdConfig
|
||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||
if kubernetes.Kubeconfig != "" {
|
||||
pathOptions.LoadingRules.ExplicitPath = kubernetes.Kubeconfig
|
||||
if kubernetes.staticConfig.KubeConfig != "" {
|
||||
pathOptions.LoadingRules.ExplicitPath = kubernetes.staticConfig.KubeConfig
|
||||
}
|
||||
kubernetes.clientCmdConfig = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
|
||||
pathOptions.LoadingRules,
|
||||
@@ -46,7 +46,7 @@ func resolveKubernetesConfigurations(kubernetes *Manager) error {
|
||||
}
|
||||
|
||||
func (m *Manager) IsInCluster() bool {
|
||||
if m.Kubeconfig != "" {
|
||||
if m.staticConfig.KubeConfig != "" {
|
||||
return false
|
||||
}
|
||||
cfg, err := InClusterConfig()
|
||||
|
||||
@@ -2,18 +2,23 @@ package kubernetes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"k8s.io/client-go/rest"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"github.com/manusa/kubernetes-mcp-server/pkg/config"
|
||||
)
|
||||
|
||||
func TestKubernetes_IsInCluster(t *testing.T) {
|
||||
t.Run("with explicit kubeconfig", func(t *testing.T) {
|
||||
m := Manager{
|
||||
Kubeconfig: "kubeconfig",
|
||||
staticConfig: &config.StaticConfig{
|
||||
KubeConfig: "kubeconfig",
|
||||
},
|
||||
}
|
||||
if m.IsInCluster() {
|
||||
t.Errorf("expected not in cluster, got in cluster")
|
||||
@@ -28,7 +33,9 @@ func TestKubernetes_IsInCluster(t *testing.T) {
|
||||
InClusterConfig = originalFunction
|
||||
}()
|
||||
m := Manager{
|
||||
Kubeconfig: "",
|
||||
staticConfig: &config.StaticConfig{
|
||||
KubeConfig: "",
|
||||
},
|
||||
}
|
||||
if !m.IsInCluster() {
|
||||
t.Errorf("expected in cluster, got not in cluster")
|
||||
@@ -43,7 +50,9 @@ func TestKubernetes_IsInCluster(t *testing.T) {
|
||||
InClusterConfig = originalFunction
|
||||
}()
|
||||
m := Manager{
|
||||
Kubeconfig: "",
|
||||
staticConfig: &config.StaticConfig{
|
||||
KubeConfig: "",
|
||||
},
|
||||
}
|
||||
if m.IsInCluster() {
|
||||
t.Errorf("expected not in cluster, got in cluster")
|
||||
@@ -58,7 +67,9 @@ func TestKubernetes_IsInCluster(t *testing.T) {
|
||||
InClusterConfig = originalFunction
|
||||
}()
|
||||
m := Manager{
|
||||
Kubeconfig: "",
|
||||
staticConfig: &config.StaticConfig{
|
||||
KubeConfig: "",
|
||||
},
|
||||
}
|
||||
if m.IsInCluster() {
|
||||
t.Errorf("expected not in cluster, got in cluster")
|
||||
@@ -72,7 +83,9 @@ func TestKubernetes_ResolveKubernetesConfigurations_Explicit(t *testing.T) {
|
||||
t.Skip("Skipping test on non-linux platforms")
|
||||
}
|
||||
tempDir := t.TempDir()
|
||||
m := Manager{Kubeconfig: path.Join(tempDir, "config")}
|
||||
m := Manager{staticConfig: &config.StaticConfig{
|
||||
KubeConfig: path.Join(tempDir, "config"),
|
||||
}}
|
||||
err := resolveKubernetesConfigurations(&m)
|
||||
if err == nil {
|
||||
t.Errorf("expected error, got nil")
|
||||
@@ -90,7 +103,9 @@ func TestKubernetes_ResolveKubernetesConfigurations_Explicit(t *testing.T) {
|
||||
if err := os.WriteFile(kubeconfigPath, []byte(""), 0644); err != nil {
|
||||
t.Fatalf("failed to create kubeconfig file: %v", err)
|
||||
}
|
||||
m := Manager{Kubeconfig: kubeconfigPath}
|
||||
m := Manager{staticConfig: &config.StaticConfig{
|
||||
KubeConfig: kubeconfigPath,
|
||||
}}
|
||||
err := resolveKubernetesConfigurations(&m)
|
||||
if err == nil {
|
||||
t.Errorf("expected error, got nil")
|
||||
@@ -123,7 +138,9 @@ users:
|
||||
if err := os.WriteFile(kubeconfigPath, []byte(kubeconfigContent), 0644); err != nil {
|
||||
t.Fatalf("failed to create kubeconfig file: %v", err)
|
||||
}
|
||||
m := Manager{Kubeconfig: kubeconfigPath}
|
||||
m := Manager{staticConfig: &config.StaticConfig{
|
||||
KubeConfig: kubeconfigPath,
|
||||
}}
|
||||
err := resolveKubernetesConfigurations(&m)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
|
||||
@@ -39,8 +39,6 @@ type Kubernetes struct {
|
||||
}
|
||||
|
||||
type Manager struct {
|
||||
// Kubeconfig path override
|
||||
Kubeconfig string
|
||||
cfg *rest.Config
|
||||
clientCmdConfig clientcmd.ClientConfig
|
||||
discoveryClient discovery.CachedDiscoveryInterface
|
||||
@@ -57,9 +55,8 @@ var ParameterCodec = runtime.NewParameterCodec(Scheme)
|
||||
|
||||
var _ helm.Kubernetes = &Manager{}
|
||||
|
||||
func NewManager(kubeconfig string, config *config.StaticConfig) (*Manager, error) {
|
||||
func NewManager(config *config.StaticConfig) (*Manager, error) {
|
||||
k8s := &Manager{
|
||||
Kubeconfig: kubeconfig,
|
||||
staticConfig: config,
|
||||
}
|
||||
if err := resolveKubernetesConfigurations(k8s); err != nil {
|
||||
@@ -166,7 +163,6 @@ func (m *Manager) Derived(ctx context.Context) *Kubernetes {
|
||||
}
|
||||
clientCmdApiConfig.AuthInfos = make(map[string]*clientcmdapi.AuthInfo)
|
||||
derived := &Kubernetes{manager: &Manager{
|
||||
Kubeconfig: m.Kubeconfig,
|
||||
clientCmdConfig: clientcmd.NewDefaultClientConfig(clientCmdApiConfig, nil),
|
||||
cfg: derivedCfg,
|
||||
staticConfig: m.staticConfig,
|
||||
|
||||
211
pkg/kubernetes/kubernetes_test.go
Normal file
211
pkg/kubernetes/kubernetes_test.go
Normal file
@@ -0,0 +1,211 @@
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/manusa/kubernetes-mcp-server/pkg/config"
|
||||
)
|
||||
|
||||
func TestManager_Derived(t *testing.T) {
|
||||
// Create a temporary kubeconfig file for testing
|
||||
tempDir := t.TempDir()
|
||||
kubeconfigPath := path.Join(tempDir, "config")
|
||||
kubeconfigContent := `
|
||||
apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- cluster:
|
||||
server: https://test-cluster.example.com
|
||||
name: test-cluster
|
||||
contexts:
|
||||
- context:
|
||||
cluster: test-cluster
|
||||
user: test-user
|
||||
name: test-context
|
||||
current-context: test-context
|
||||
users:
|
||||
- name: test-user
|
||||
user:
|
||||
username: test-username
|
||||
password: test-password
|
||||
`
|
||||
if err := os.WriteFile(kubeconfigPath, []byte(kubeconfigContent), 0644); err != nil {
|
||||
t.Fatalf("failed to create kubeconfig file: %v", err)
|
||||
}
|
||||
|
||||
t.Run("without authorization header returns original manager", func(t *testing.T) {
|
||||
testStaticConfig := &config.StaticConfig{
|
||||
KubeConfig: kubeconfigPath,
|
||||
DisabledTools: []string{"configuration_view"},
|
||||
DeniedResources: []config.GroupVersionKind{
|
||||
{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
},
|
||||
}
|
||||
|
||||
testManager, err := NewManager(testStaticConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create manager: %v", err)
|
||||
}
|
||||
defer testManager.Close()
|
||||
ctx := context.Background()
|
||||
derived := testManager.Derived(ctx)
|
||||
|
||||
if derived.manager != testManager {
|
||||
t.Errorf("expected original manager, got different manager")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with invalid authorization header returns original manager", func(t *testing.T) {
|
||||
testStaticConfig := &config.StaticConfig{
|
||||
KubeConfig: kubeconfigPath,
|
||||
DisabledTools: []string{"configuration_view"},
|
||||
DeniedResources: []config.GroupVersionKind{
|
||||
{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
},
|
||||
}
|
||||
|
||||
testManager, err := NewManager(testStaticConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create manager: %v", err)
|
||||
}
|
||||
defer testManager.Close()
|
||||
ctx := context.WithValue(context.Background(), OAuthAuthorizationHeader, "invalid-token")
|
||||
derived := testManager.Derived(ctx)
|
||||
|
||||
if derived.manager != testManager {
|
||||
t.Errorf("expected original manager, got different manager")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with valid bearer token creates derived manager with correct configuration", func(t *testing.T) {
|
||||
testStaticConfig := &config.StaticConfig{
|
||||
KubeConfig: kubeconfigPath,
|
||||
DisabledTools: []string{"configuration_view"},
|
||||
DeniedResources: []config.GroupVersionKind{
|
||||
{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
},
|
||||
}
|
||||
|
||||
testManager, err := NewManager(testStaticConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create manager: %v", err)
|
||||
}
|
||||
defer testManager.Close()
|
||||
testBearerToken := "test-bearer-token-123"
|
||||
ctx := context.WithValue(context.Background(), OAuthAuthorizationHeader, "Bearer "+testBearerToken)
|
||||
derived := testManager.Derived(ctx)
|
||||
|
||||
if derived.manager == testManager {
|
||||
t.Errorf("expected new derived manager, got original manager")
|
||||
}
|
||||
|
||||
if derived.manager.staticConfig != testStaticConfig {
|
||||
t.Errorf("staticConfig not properly wired to derived manager")
|
||||
}
|
||||
|
||||
derivedCfg := derived.manager.cfg
|
||||
if derivedCfg == nil {
|
||||
t.Fatalf("derived config is nil")
|
||||
}
|
||||
|
||||
originalCfg := testManager.cfg
|
||||
if derivedCfg.Host != originalCfg.Host {
|
||||
t.Errorf("expected Host %s, got %s", originalCfg.Host, derivedCfg.Host)
|
||||
}
|
||||
if derivedCfg.APIPath != originalCfg.APIPath {
|
||||
t.Errorf("expected APIPath %s, got %s", originalCfg.APIPath, derivedCfg.APIPath)
|
||||
}
|
||||
if derivedCfg.QPS != originalCfg.QPS {
|
||||
t.Errorf("expected QPS %f, got %f", originalCfg.QPS, derivedCfg.QPS)
|
||||
}
|
||||
if derivedCfg.Burst != originalCfg.Burst {
|
||||
t.Errorf("expected Burst %d, got %d", originalCfg.Burst, derivedCfg.Burst)
|
||||
}
|
||||
if derivedCfg.Timeout != originalCfg.Timeout {
|
||||
t.Errorf("expected Timeout %v, got %v", originalCfg.Timeout, derivedCfg.Timeout)
|
||||
}
|
||||
|
||||
if derivedCfg.TLSClientConfig.Insecure != originalCfg.TLSClientConfig.Insecure {
|
||||
t.Errorf("expected TLS Insecure %v, got %v", originalCfg.TLSClientConfig.Insecure, derivedCfg.TLSClientConfig.Insecure)
|
||||
}
|
||||
if derivedCfg.TLSClientConfig.ServerName != originalCfg.TLSClientConfig.ServerName {
|
||||
t.Errorf("expected TLS ServerName %s, got %s", originalCfg.TLSClientConfig.ServerName, derivedCfg.TLSClientConfig.ServerName)
|
||||
}
|
||||
if derivedCfg.TLSClientConfig.CAFile != originalCfg.TLSClientConfig.CAFile {
|
||||
t.Errorf("expected TLS CAFile %s, got %s", originalCfg.TLSClientConfig.CAFile, derivedCfg.TLSClientConfig.CAFile)
|
||||
}
|
||||
if string(derivedCfg.TLSClientConfig.CAData) != string(originalCfg.TLSClientConfig.CAData) {
|
||||
t.Errorf("expected TLS CAData %s, got %s", string(originalCfg.TLSClientConfig.CAData), string(derivedCfg.TLSClientConfig.CAData))
|
||||
}
|
||||
|
||||
if derivedCfg.BearerToken != testBearerToken {
|
||||
t.Errorf("expected BearerToken %s, got %s", testBearerToken, derivedCfg.BearerToken)
|
||||
}
|
||||
if derivedCfg.UserAgent != CustomUserAgent {
|
||||
t.Errorf("expected UserAgent %s, got %s", CustomUserAgent, derivedCfg.UserAgent)
|
||||
}
|
||||
|
||||
// Verify that sensitive fields are NOT copied to prevent credential leakage
|
||||
// The derived config should only use the bearer token from the Authorization header
|
||||
// and not inherit any authentication credentials from the original kubeconfig
|
||||
if derivedCfg.TLSClientConfig.CertFile != "" {
|
||||
t.Errorf("expected TLS CertFile to be empty, got %s", derivedCfg.TLSClientConfig.CertFile)
|
||||
}
|
||||
if derivedCfg.TLSClientConfig.KeyFile != "" {
|
||||
t.Errorf("expected TLS KeyFile to be empty, got %s", derivedCfg.TLSClientConfig.KeyFile)
|
||||
}
|
||||
if len(derivedCfg.TLSClientConfig.CertData) != 0 {
|
||||
t.Errorf("expected TLS CertData to be empty, got %v", derivedCfg.TLSClientConfig.CertData)
|
||||
}
|
||||
if len(derivedCfg.TLSClientConfig.KeyData) != 0 {
|
||||
t.Errorf("expected TLS KeyData to be empty, got %v", derivedCfg.TLSClientConfig.KeyData)
|
||||
}
|
||||
|
||||
if derivedCfg.Username != "" {
|
||||
t.Errorf("expected Username to be empty, got %s", derivedCfg.Username)
|
||||
}
|
||||
if derivedCfg.Password != "" {
|
||||
t.Errorf("expected Password to be empty, got %s", derivedCfg.Password)
|
||||
}
|
||||
if derivedCfg.AuthProvider != nil {
|
||||
t.Errorf("expected AuthProvider to be nil, got %v", derivedCfg.AuthProvider)
|
||||
}
|
||||
if derivedCfg.ExecProvider != nil {
|
||||
t.Errorf("expected ExecProvider to be nil, got %v", derivedCfg.ExecProvider)
|
||||
}
|
||||
if derivedCfg.BearerTokenFile != "" {
|
||||
t.Errorf("expected BearerTokenFile to be empty, got %s", derivedCfg.BearerTokenFile)
|
||||
}
|
||||
if derivedCfg.Impersonate.UserName != "" {
|
||||
t.Errorf("expected Impersonate.UserName to be empty, got %s", derivedCfg.Impersonate.UserName)
|
||||
}
|
||||
|
||||
// Verify that the original manager still has the sensitive data
|
||||
if originalCfg.Username == "" && originalCfg.Password == "" {
|
||||
t.Logf("original kubeconfig shouldn't be modified")
|
||||
}
|
||||
|
||||
// Verify that the derived manager has proper clients initialized
|
||||
if derived.manager.accessControlClientSet == nil {
|
||||
t.Error("expected accessControlClientSet to be initialized")
|
||||
}
|
||||
if derived.manager.accessControlClientSet.staticConfig != testStaticConfig {
|
||||
t.Errorf("staticConfig not properly wired to derived manager")
|
||||
}
|
||||
if derived.manager.discoveryClient == nil {
|
||||
t.Error("expected discoveryClient to be initialized")
|
||||
}
|
||||
if derived.manager.accessControlRESTMapper == nil {
|
||||
t.Error("expected accessControlRESTMapper to be initialized")
|
||||
}
|
||||
if derived.manager.accessControlRESTMapper.staticConfig != testStaticConfig {
|
||||
t.Errorf("staticConfig not properly wired to derived manager")
|
||||
}
|
||||
if derived.manager.dynamicClient == nil {
|
||||
t.Error("expected dynamicClient to be initialized")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -65,7 +65,7 @@ func NewServer(configuration Configuration) (*Server, error) {
|
||||
}
|
||||
|
||||
func (s *Server) reloadKubernetesClient() error {
|
||||
k, err := internalk8s.NewManager(s.configuration.StaticConfig.KubeConfig, s.configuration.StaticConfig)
|
||||
k, err := internalk8s.NewManager(s.configuration.StaticConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user