mirror of
https://github.com/containers/kubernetes-mcp-server.git
synced 2025-10-23 01:22:57 +03:00
refactor(kubernetes): streamline provider configuration and in-cluster detection (#378)
* refactor(kubernetes): streamline provider configuration and in-cluster detection - Removed IsInCluster method from Manager and created function scoped to the runtime environment. As a method, the implementation was not correct. Removed GetAPIServerHost method from Manager which is no used. - **Temporarily** added an `inCluster` field to the Manager struct but should be eventually removed since it doesn't really make sense to hava a Manager in-cluster or out-of-cluster in the multi-cluster scenario. - Provider resolution (resolveStrategy) is now clearer, added complete coverage for all scenarios. - Added additional coverage for provider and manager. Signed-off-by: Marc Nuri <marc@marcnuri.com> * refactor(kubernetes): update NewManager to accept kubeconfig context and simplify manager creation - Removes Provider.newForContext(context string) method. Signed-off-by: Marc Nuri <marc@marcnuri.com> --------- Signed-off-by: Marc Nuri <marc@marcnuri.com>
This commit is contained in:
15
internal/test/env.go
Normal file
15
internal/test/env.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RestoreEnv(originalEnv []string) {
|
||||||
|
os.Clearenv()
|
||||||
|
for _, env := range originalEnv {
|
||||||
|
if key, value, found := strings.Cut(env, "="); found {
|
||||||
|
_ = os.Setenv(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package kubernetes
|
package kubernetes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/containers/kubernetes-mcp-server/pkg/config"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
|
||||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
"k8s.io/client-go/tools/clientcmd/api/latest"
|
"k8s.io/client-go/tools/clientcmd/api/latest"
|
||||||
)
|
)
|
||||||
@@ -22,29 +22,13 @@ var InClusterConfig = func() (*rest.Config, error) {
|
|||||||
return inClusterConfig, err
|
return inClusterConfig, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveKubernetesConfigurations resolves the required kubernetes configurations and sets them in the Kubernetes struct
|
func IsInCluster(cfg *config.StaticConfig) bool {
|
||||||
func resolveKubernetesConfigurations(kubernetes *Manager) error {
|
// Even if running in-cluster, if a kubeconfig is provided, we consider it as out-of-cluster
|
||||||
// Always set clientCmdConfig
|
if cfg != nil && cfg.KubeConfig != "" {
|
||||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
return false
|
||||||
if kubernetes.staticConfig.KubeConfig != "" {
|
|
||||||
pathOptions.LoadingRules.ExplicitPath = kubernetes.staticConfig.KubeConfig
|
|
||||||
}
|
}
|
||||||
kubernetes.clientCmdConfig = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
|
restConfig, err := InClusterConfig()
|
||||||
pathOptions.LoadingRules,
|
return err == nil && restConfig != nil
|
||||||
&clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: ""}})
|
|
||||||
var err error
|
|
||||||
if kubernetes.IsInCluster() {
|
|
||||||
kubernetes.cfg, err = InClusterConfig()
|
|
||||||
if err == nil && kubernetes.cfg != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Out of cluster
|
|
||||||
kubernetes.cfg, err = kubernetes.clientCmdConfig.ClientConfig()
|
|
||||||
if kubernetes.cfg != nil && kubernetes.cfg.UserAgent == "" {
|
|
||||||
kubernetes.cfg.UserAgent = rest.DefaultKubernetesUserAgent()
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *Kubernetes) NamespaceOrDefault(namespace string) string {
|
func (k *Kubernetes) NamespaceOrDefault(namespace string) string {
|
||||||
@@ -54,7 +38,7 @@ func (k *Kubernetes) NamespaceOrDefault(namespace string) string {
|
|||||||
// ConfigurationContextsDefault returns the current context name
|
// ConfigurationContextsDefault returns the current context name
|
||||||
// TODO: Should be moved to the Provider level ?
|
// TODO: Should be moved to the Provider level ?
|
||||||
func (k *Kubernetes) ConfigurationContextsDefault() (string, error) {
|
func (k *Kubernetes) ConfigurationContextsDefault() (string, error) {
|
||||||
if k.manager.IsInCluster() {
|
if k.manager.inCluster {
|
||||||
return inClusterKubeConfigDefaultContext, nil
|
return inClusterKubeConfigDefaultContext, nil
|
||||||
}
|
}
|
||||||
cfg, err := k.manager.clientCmdConfig.RawConfig()
|
cfg, err := k.manager.clientCmdConfig.RawConfig()
|
||||||
@@ -67,7 +51,7 @@ func (k *Kubernetes) ConfigurationContextsDefault() (string, error) {
|
|||||||
// ConfigurationContextsList returns the list of available context names
|
// ConfigurationContextsList returns the list of available context names
|
||||||
// TODO: Should be moved to the Provider level ?
|
// TODO: Should be moved to the Provider level ?
|
||||||
func (k *Kubernetes) ConfigurationContextsList() (map[string]string, error) {
|
func (k *Kubernetes) ConfigurationContextsList() (map[string]string, error) {
|
||||||
if k.manager.IsInCluster() {
|
if k.manager.inCluster {
|
||||||
return map[string]string{inClusterKubeConfigDefaultContext: ""}, nil
|
return map[string]string{inClusterKubeConfigDefaultContext: ""}, nil
|
||||||
}
|
}
|
||||||
cfg, err := k.manager.clientCmdConfig.RawConfig()
|
cfg, err := k.manager.clientCmdConfig.RawConfig()
|
||||||
@@ -93,7 +77,7 @@ func (k *Kubernetes) ConfigurationContextsList() (map[string]string, error) {
|
|||||||
func (k *Kubernetes) ConfigurationView(minify bool) (runtime.Object, error) {
|
func (k *Kubernetes) ConfigurationView(minify bool) (runtime.Object, error) {
|
||||||
var cfg clientcmdapi.Config
|
var cfg clientcmdapi.Config
|
||||||
var err error
|
var err error
|
||||||
if k.manager.IsInCluster() {
|
if k.manager.inCluster {
|
||||||
cfg = *clientcmdapi.NewConfig()
|
cfg = *clientcmdapi.NewConfig()
|
||||||
cfg.Clusters["cluster"] = &clientcmdapi.Cluster{
|
cfg.Clusters["cluster"] = &clientcmdapi.Cluster{
|
||||||
Server: k.manager.cfg.Host,
|
Server: k.manager.cfg.Host,
|
||||||
|
|||||||
@@ -1,155 +0,0 @@
|
|||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"k8s.io/client-go/rest"
|
|
||||||
|
|
||||||
"github.com/containers/kubernetes-mcp-server/pkg/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestKubernetes_IsInCluster(t *testing.T) {
|
|
||||||
t.Run("with explicit kubeconfig", func(t *testing.T) {
|
|
||||||
m := Manager{
|
|
||||||
staticConfig: &config.StaticConfig{
|
|
||||||
KubeConfig: "kubeconfig",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if m.IsInCluster() {
|
|
||||||
t.Errorf("expected not in cluster, got in cluster")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.Run("with empty kubeconfig and in cluster", func(t *testing.T) {
|
|
||||||
originalFunction := InClusterConfig
|
|
||||||
InClusterConfig = func() (*rest.Config, error) {
|
|
||||||
return &rest.Config{}, nil
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
InClusterConfig = originalFunction
|
|
||||||
}()
|
|
||||||
m := Manager{
|
|
||||||
staticConfig: &config.StaticConfig{
|
|
||||||
KubeConfig: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if !m.IsInCluster() {
|
|
||||||
t.Errorf("expected in cluster, got not in cluster")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.Run("with empty kubeconfig and not in cluster (empty)", func(t *testing.T) {
|
|
||||||
originalFunction := InClusterConfig
|
|
||||||
InClusterConfig = func() (*rest.Config, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
InClusterConfig = originalFunction
|
|
||||||
}()
|
|
||||||
m := Manager{
|
|
||||||
staticConfig: &config.StaticConfig{
|
|
||||||
KubeConfig: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if m.IsInCluster() {
|
|
||||||
t.Errorf("expected not in cluster, got in cluster")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.Run("with empty kubeconfig and not in cluster (error)", func(t *testing.T) {
|
|
||||||
originalFunction := InClusterConfig
|
|
||||||
InClusterConfig = func() (*rest.Config, error) {
|
|
||||||
return nil, errors.New("error")
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
InClusterConfig = originalFunction
|
|
||||||
}()
|
|
||||||
m := Manager{
|
|
||||||
staticConfig: &config.StaticConfig{
|
|
||||||
KubeConfig: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if m.IsInCluster() {
|
|
||||||
t.Errorf("expected not in cluster, got in cluster")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKubernetes_ResolveKubernetesConfigurations_Explicit(t *testing.T) {
|
|
||||||
t.Run("with missing file", func(t *testing.T) {
|
|
||||||
if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
|
|
||||||
t.Skip("Skipping test on non-linux platforms")
|
|
||||||
}
|
|
||||||
tempDir := t.TempDir()
|
|
||||||
m := Manager{staticConfig: &config.StaticConfig{
|
|
||||||
KubeConfig: path.Join(tempDir, "config"),
|
|
||||||
}}
|
|
||||||
err := resolveKubernetesConfigurations(&m)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("expected error, got nil")
|
|
||||||
}
|
|
||||||
if !errors.Is(err, os.ErrNotExist) {
|
|
||||||
t.Errorf("expected file not found error, got %v", err)
|
|
||||||
}
|
|
||||||
if !strings.HasSuffix(err.Error(), ": no such file or directory") {
|
|
||||||
t.Errorf("expected file not found error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.Run("with empty file", func(t *testing.T) {
|
|
||||||
tempDir := t.TempDir()
|
|
||||||
kubeconfigPath := path.Join(tempDir, "config")
|
|
||||||
if err := os.WriteFile(kubeconfigPath, []byte(""), 0644); err != nil {
|
|
||||||
t.Fatalf("failed to create kubeconfig file: %v", err)
|
|
||||||
}
|
|
||||||
m := Manager{staticConfig: &config.StaticConfig{
|
|
||||||
KubeConfig: kubeconfigPath,
|
|
||||||
}}
|
|
||||||
err := resolveKubernetesConfigurations(&m)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("expected error, got nil")
|
|
||||||
}
|
|
||||||
if !strings.Contains(err.Error(), "no configuration has been provided") {
|
|
||||||
t.Errorf("expected no kubeconfig error, got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.Run("with valid file", func(t *testing.T) {
|
|
||||||
tempDir := t.TempDir()
|
|
||||||
kubeconfigPath := path.Join(tempDir, "config")
|
|
||||||
kubeconfigContent := `
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Config
|
|
||||||
clusters:
|
|
||||||
- cluster:
|
|
||||||
server: https://example.com
|
|
||||||
name: example-cluster
|
|
||||||
contexts:
|
|
||||||
- context:
|
|
||||||
cluster: example-cluster
|
|
||||||
user: example-user
|
|
||||||
name: example-context
|
|
||||||
current-context: example-context
|
|
||||||
users:
|
|
||||||
- name: example-user
|
|
||||||
user:
|
|
||||||
token: example-token
|
|
||||||
`
|
|
||||||
if err := os.WriteFile(kubeconfigPath, []byte(kubeconfigContent), 0644); err != nil {
|
|
||||||
t.Fatalf("failed to create kubeconfig file: %v", err)
|
|
||||||
}
|
|
||||||
m := Manager{staticConfig: &config.StaticConfig{
|
|
||||||
KubeConfig: kubeconfigPath,
|
|
||||||
}}
|
|
||||||
err := resolveKubernetesConfigurations(&m)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("expected no error, got %v", err)
|
|
||||||
}
|
|
||||||
if m.cfg == nil {
|
|
||||||
t.Errorf("expected non-nil config, got nil")
|
|
||||||
}
|
|
||||||
if m.cfg.Host != "https://example.com" {
|
|
||||||
t.Errorf("expected host https://example.com, got %s", m.cfg.Host)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -47,7 +47,7 @@ users:
|
|||||||
kubeconfig = "` + strings.ReplaceAll(kubeconfigPath, `\`, `\\`) + `"
|
kubeconfig = "` + strings.ReplaceAll(kubeconfigPath, `\`, `\\`) + `"
|
||||||
`)))
|
`)))
|
||||||
s.Run("without authorization header returns original manager", func() {
|
s.Run("without authorization header returns original manager", func() {
|
||||||
testManager, err := NewManager(testStaticConfig)
|
testManager, err := NewManager(testStaticConfig, "")
|
||||||
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
|
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
|
||||||
s.T().Cleanup(testManager.Close)
|
s.T().Cleanup(testManager.Close)
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ users:
|
|||||||
})
|
})
|
||||||
|
|
||||||
s.Run("with invalid authorization header returns original manager", func() {
|
s.Run("with invalid authorization header returns original manager", func() {
|
||||||
testManager, err := NewManager(testStaticConfig)
|
testManager, err := NewManager(testStaticConfig, "")
|
||||||
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
|
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
|
||||||
s.T().Cleanup(testManager.Close)
|
s.T().Cleanup(testManager.Close)
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ users:
|
|||||||
})
|
})
|
||||||
|
|
||||||
s.Run("with valid bearer token creates derived manager with correct configuration", func() {
|
s.Run("with valid bearer token creates derived manager with correct configuration", func() {
|
||||||
testManager, err := NewManager(testStaticConfig)
|
testManager, err := NewManager(testStaticConfig, "")
|
||||||
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
|
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
|
||||||
s.T().Cleanup(testManager.Close)
|
s.T().Cleanup(testManager.Close)
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ users:
|
|||||||
`)))
|
`)))
|
||||||
|
|
||||||
s.Run("with no authorization header returns oauth token required error", func() {
|
s.Run("with no authorization header returns oauth token required error", func() {
|
||||||
testManager, err := NewManager(testStaticConfig)
|
testManager, err := NewManager(testStaticConfig, "")
|
||||||
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
|
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
|
||||||
s.T().Cleanup(testManager.Close)
|
s.T().Cleanup(testManager.Close)
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ users:
|
|||||||
})
|
})
|
||||||
|
|
||||||
s.Run("with invalid authorization header returns oauth token required error", func() {
|
s.Run("with invalid authorization header returns oauth token required error", func() {
|
||||||
testManager, err := NewManager(testStaticConfig)
|
testManager, err := NewManager(testStaticConfig, "")
|
||||||
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
|
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
|
||||||
s.T().Cleanup(testManager.Close)
|
s.T().Cleanup(testManager.Close)
|
||||||
|
|
||||||
@@ -161,7 +161,7 @@ users:
|
|||||||
})
|
})
|
||||||
|
|
||||||
s.Run("with valid bearer token creates derived manager", func() {
|
s.Run("with valid bearer token creates derived manager", func() {
|
||||||
testManager, err := NewManager(testStaticConfig)
|
testManager, err := NewManager(testStaticConfig, "")
|
||||||
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
|
s.Require().NoErrorf(err, "failed to create test manager: %v", err)
|
||||||
s.T().Cleanup(testManager.Close)
|
s.T().Cleanup(testManager.Close)
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import (
|
|||||||
type Manager struct {
|
type Manager struct {
|
||||||
cfg *rest.Config
|
cfg *rest.Config
|
||||||
clientCmdConfig clientcmd.ClientConfig
|
clientCmdConfig clientcmd.ClientConfig
|
||||||
|
inCluster bool
|
||||||
discoveryClient discovery.CachedDiscoveryInterface
|
discoveryClient discovery.CachedDiscoveryInterface
|
||||||
accessControlClientSet *AccessControlClientset
|
accessControlClientSet *AccessControlClientset
|
||||||
accessControlRESTMapper *AccessControlRESTMapper
|
accessControlRESTMapper *AccessControlRESTMapper
|
||||||
@@ -37,18 +38,37 @@ type Manager struct {
|
|||||||
var _ helm.Kubernetes = (*Manager)(nil)
|
var _ helm.Kubernetes = (*Manager)(nil)
|
||||||
var _ Openshift = (*Manager)(nil)
|
var _ Openshift = (*Manager)(nil)
|
||||||
|
|
||||||
func NewManager(config *config.StaticConfig) (*Manager, error) {
|
func NewManager(config *config.StaticConfig, kubeconfigContext string) (*Manager, error) {
|
||||||
k8s := &Manager{
|
k8s := &Manager{
|
||||||
staticConfig: config,
|
staticConfig: config,
|
||||||
}
|
}
|
||||||
if err := resolveKubernetesConfigurations(k8s); err != nil {
|
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||||
return nil, err
|
if k8s.staticConfig.KubeConfig != "" {
|
||||||
|
pathOptions.LoadingRules.ExplicitPath = k8s.staticConfig.KubeConfig
|
||||||
|
}
|
||||||
|
k8s.clientCmdConfig = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
|
||||||
|
pathOptions.LoadingRules,
|
||||||
|
&clientcmd.ConfigOverrides{
|
||||||
|
ClusterInfo: clientcmdapi.Cluster{Server: ""},
|
||||||
|
CurrentContext: kubeconfigContext,
|
||||||
|
})
|
||||||
|
var err error
|
||||||
|
if IsInCluster(k8s.staticConfig) {
|
||||||
|
k8s.cfg, err = InClusterConfig()
|
||||||
|
k8s.inCluster = true
|
||||||
|
} else {
|
||||||
|
k8s.cfg, err = k8s.clientCmdConfig.ClientConfig()
|
||||||
|
}
|
||||||
|
if err != nil || k8s.cfg == nil {
|
||||||
|
return nil, fmt.Errorf("failed to create kubernetes rest config: %v", err)
|
||||||
|
}
|
||||||
|
if k8s.cfg.UserAgent == "" {
|
||||||
|
k8s.cfg.UserAgent = rest.DefaultKubernetesUserAgent()
|
||||||
}
|
}
|
||||||
// TODO: Won't work because not all client-go clients use the shared context (e.g. discovery client uses context.TODO())
|
// TODO: Won't work because not all client-go clients use the shared context (e.g. discovery client uses context.TODO())
|
||||||
//k8s.cfg.Wrap(func(original http.RoundTripper) http.RoundTripper {
|
//k8s.cfg.Wrap(func(original http.RoundTripper) http.RoundTripper {
|
||||||
// return &impersonateRoundTripper{original}
|
// return &impersonateRoundTripper{original}
|
||||||
//})
|
//})
|
||||||
var err error
|
|
||||||
k8s.accessControlClientSet, err = NewAccessControlClientset(k8s.cfg, k8s.staticConfig)
|
k8s.accessControlClientSet, err = NewAccessControlClientset(k8s.cfg, k8s.staticConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -107,21 +127,6 @@ func (m *Manager) Close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) GetAPIServerHost() string {
|
|
||||||
if m.cfg == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return m.cfg.Host
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) IsInCluster() bool {
|
|
||||||
if m.staticConfig.KubeConfig != "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
cfg, err := InClusterConfig()
|
|
||||||
return err == nil && cfg != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) configuredNamespace() string {
|
func (m *Manager) configuredNamespace() string {
|
||||||
if ns, _, nsErr := m.clientCmdConfig.Namespace(); nsErr == nil {
|
if ns, _, nsErr := m.clientCmdConfig.Namespace(); nsErr == nil {
|
||||||
return ns
|
return ns
|
||||||
@@ -221,11 +226,14 @@ func (m *Manager) Derived(ctx context.Context) (*Kubernetes, error) {
|
|||||||
return &Kubernetes{manager: m}, nil
|
return &Kubernetes{manager: m}, nil
|
||||||
}
|
}
|
||||||
clientCmdApiConfig.AuthInfos = make(map[string]*clientcmdapi.AuthInfo)
|
clientCmdApiConfig.AuthInfos = make(map[string]*clientcmdapi.AuthInfo)
|
||||||
derived := &Kubernetes{manager: &Manager{
|
derived := &Kubernetes{
|
||||||
clientCmdConfig: clientcmd.NewDefaultClientConfig(clientCmdApiConfig, nil),
|
manager: &Manager{
|
||||||
cfg: derivedCfg,
|
clientCmdConfig: clientcmd.NewDefaultClientConfig(clientCmdApiConfig, nil),
|
||||||
staticConfig: m.staticConfig,
|
inCluster: m.inCluster,
|
||||||
}}
|
cfg: derivedCfg,
|
||||||
|
staticConfig: m.staticConfig,
|
||||||
|
},
|
||||||
|
}
|
||||||
derived.manager.accessControlClientSet, err = NewAccessControlClientset(derived.manager.cfg, derived.manager.staticConfig)
|
derived.manager.accessControlClientSet, err = NewAccessControlClientset(derived.manager.cfg, derived.manager.staticConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if m.staticConfig.RequireOAuth {
|
if m.staticConfig.RequireOAuth {
|
||||||
|
|||||||
163
pkg/kubernetes/manager_test.go
Normal file
163
pkg/kubernetes/manager_test.go
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
package kubernetes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/containers/kubernetes-mcp-server/internal/test"
|
||||||
|
"github.com/containers/kubernetes-mcp-server/pkg/config"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ManagerTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
originalEnv []string
|
||||||
|
originalInClusterConfig func() (*rest.Config, error)
|
||||||
|
mockServer *test.MockServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ManagerTestSuite) SetupTest() {
|
||||||
|
s.originalEnv = os.Environ()
|
||||||
|
s.originalInClusterConfig = InClusterConfig
|
||||||
|
s.mockServer = test.NewMockServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ManagerTestSuite) TearDownTest() {
|
||||||
|
test.RestoreEnv(s.originalEnv)
|
||||||
|
InClusterConfig = s.originalInClusterConfig
|
||||||
|
if s.mockServer != nil {
|
||||||
|
s.mockServer.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ManagerTestSuite) TestNewManagerInCluster() {
|
||||||
|
InClusterConfig = func() (*rest.Config, error) {
|
||||||
|
return &rest.Config{}, nil
|
||||||
|
}
|
||||||
|
s.Run("with default StaticConfig (empty kubeconfig)", func() {
|
||||||
|
manager, err := NewManager(&config.StaticConfig{}, "")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotNil(manager)
|
||||||
|
s.Run("behaves as in cluster", func() {
|
||||||
|
s.True(manager.inCluster, "expected in cluster, got not in cluster")
|
||||||
|
})
|
||||||
|
s.Run("sets default user-agent", func() {
|
||||||
|
s.Contains(manager.cfg.UserAgent, "("+runtime.GOOS+"/"+runtime.GOARCH+")")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
s.Run("with explicit kubeconfig", func() {
|
||||||
|
manager, err := NewManager(&config.StaticConfig{
|
||||||
|
KubeConfig: s.mockServer.KubeconfigFile(s.T()),
|
||||||
|
}, "")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotNil(manager)
|
||||||
|
s.Run("behaves as NOT in cluster", func() {
|
||||||
|
s.False(manager.inCluster, "expected not in cluster, got in cluster")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ManagerTestSuite) TestNewManagerLocal() {
|
||||||
|
InClusterConfig = func() (*rest.Config, error) {
|
||||||
|
return nil, rest.ErrNotInCluster
|
||||||
|
}
|
||||||
|
s.Run("with valid kubeconfig in env", func() {
|
||||||
|
kubeconfig := s.mockServer.KubeconfigFile(s.T())
|
||||||
|
s.Require().NoError(os.Setenv("KUBECONFIG", kubeconfig))
|
||||||
|
manager, err := NewManager(&config.StaticConfig{}, "")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotNil(manager)
|
||||||
|
s.Run("behaves as NOT in cluster", func() {
|
||||||
|
s.False(manager.inCluster, "expected not in cluster, got in cluster")
|
||||||
|
})
|
||||||
|
s.Run("loads correct config", func() {
|
||||||
|
s.Contains(manager.clientCmdConfig.ConfigAccess().GetLoadingPrecedence(), kubeconfig, "expected kubeconfig path to match")
|
||||||
|
})
|
||||||
|
s.Run("sets default user-agent", func() {
|
||||||
|
s.Contains(manager.cfg.UserAgent, "("+runtime.GOOS+"/"+runtime.GOARCH+")")
|
||||||
|
})
|
||||||
|
s.Run("rest config host points to mock server", func() {
|
||||||
|
s.Equal(s.mockServer.Config().Host, manager.cfg.Host, "expected rest config host to match mock server")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
s.Run("with valid kubeconfig in env and explicit kubeconfig in config", func() {
|
||||||
|
kubeconfigInEnv := s.mockServer.KubeconfigFile(s.T())
|
||||||
|
s.Require().NoError(os.Setenv("KUBECONFIG", kubeconfigInEnv))
|
||||||
|
kubeconfigExplicit := s.mockServer.KubeconfigFile(s.T())
|
||||||
|
manager, err := NewManager(&config.StaticConfig{
|
||||||
|
KubeConfig: kubeconfigExplicit,
|
||||||
|
}, "")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotNil(manager)
|
||||||
|
s.Run("behaves as NOT in cluster", func() {
|
||||||
|
s.False(manager.inCluster, "expected not in cluster, got in cluster")
|
||||||
|
})
|
||||||
|
s.Run("loads correct config (explicit)", func() {
|
||||||
|
s.NotContains(manager.clientCmdConfig.ConfigAccess().GetLoadingPrecedence(), kubeconfigInEnv, "expected kubeconfig path to NOT match env")
|
||||||
|
s.Contains(manager.clientCmdConfig.ConfigAccess().GetLoadingPrecedence(), kubeconfigExplicit, "expected kubeconfig path to match explicit")
|
||||||
|
})
|
||||||
|
s.Run("rest config host points to mock server", func() {
|
||||||
|
s.Equal(s.mockServer.Config().Host, manager.cfg.Host, "expected rest config host to match mock server")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
s.Run("with valid kubeconfig in env and explicit kubeconfig context (valid)", func() {
|
||||||
|
kubeconfig := s.mockServer.Kubeconfig()
|
||||||
|
kubeconfig.Contexts["not-the-mock-server"] = clientcmdapi.NewContext()
|
||||||
|
kubeconfig.Contexts["not-the-mock-server"].Cluster = "not-the-mock-server"
|
||||||
|
kubeconfig.Clusters["not-the-mock-server"] = clientcmdapi.NewCluster()
|
||||||
|
kubeconfig.Clusters["not-the-mock-server"].Server = "https://not-the-mock-server:6443" // REST configuration should point to mock server, not this
|
||||||
|
kubeconfig.CurrentContext = "not-the-mock-server"
|
||||||
|
kubeconfigFile := test.KubeconfigFile(s.T(), kubeconfig)
|
||||||
|
s.Require().NoError(os.Setenv("KUBECONFIG", kubeconfigFile))
|
||||||
|
manager, err := NewManager(&config.StaticConfig{}, "fake-context") // fake-context is the one mock-server serves
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotNil(manager)
|
||||||
|
s.Run("behaves as NOT in cluster", func() {
|
||||||
|
s.False(manager.inCluster, "expected not in cluster, got in cluster")
|
||||||
|
})
|
||||||
|
s.Run("loads correct config", func() {
|
||||||
|
s.Contains(manager.clientCmdConfig.ConfigAccess().GetLoadingPrecedence(), kubeconfigFile, "expected kubeconfig path to match")
|
||||||
|
})
|
||||||
|
s.Run("rest config host points to mock server", func() {
|
||||||
|
s.Equal(s.mockServer.Config().Host, manager.cfg.Host, "expected rest config host to match mock server")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
s.Run("with valid kubeconfig in env and explicit kubeconfig context (invalid)", func() {
|
||||||
|
kubeconfigInEnv := s.mockServer.KubeconfigFile(s.T())
|
||||||
|
s.Require().NoError(os.Setenv("KUBECONFIG", kubeconfigInEnv))
|
||||||
|
manager, err := NewManager(&config.StaticConfig{}, "i-do-not-exist")
|
||||||
|
s.Run("returns error", func() {
|
||||||
|
s.Error(err)
|
||||||
|
s.Nil(manager)
|
||||||
|
s.ErrorContains(err, `failed to create kubernetes rest config: context "i-do-not-exist" does not exist`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
s.Run("with invalid path kubeconfig in env", func() {
|
||||||
|
s.Require().NoError(os.Setenv("KUBECONFIG", "i-dont-exist"))
|
||||||
|
manager, err := NewManager(&config.StaticConfig{}, "")
|
||||||
|
s.Run("returns error", func() {
|
||||||
|
s.Error(err)
|
||||||
|
s.Nil(manager)
|
||||||
|
s.ErrorContains(err, "failed to create kubernetes rest config")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
s.Run("with empty kubeconfig in env", func() {
|
||||||
|
kubeconfigPath := filepath.Join(s.T().TempDir(), "config")
|
||||||
|
s.Require().NoError(os.WriteFile(kubeconfigPath, []byte(""), 0644))
|
||||||
|
s.Require().NoError(os.Setenv("KUBECONFIG", kubeconfigPath))
|
||||||
|
manager, err := NewManager(&config.StaticConfig{}, "")
|
||||||
|
s.Run("returns error", func() {
|
||||||
|
s.Error(err)
|
||||||
|
s.Nil(manager)
|
||||||
|
s.ErrorContains(err, "no configuration has been provided")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManager(t *testing.T) {
|
||||||
|
suite.Run(t, new(ManagerTestSuite))
|
||||||
|
}
|
||||||
@@ -4,11 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/containers/kubernetes-mcp-server/pkg/config"
|
"github.com/containers/kubernetes-mcp-server/pkg/config"
|
||||||
"k8s.io/client-go/discovery/cached/memory"
|
|
||||||
"k8s.io/client-go/dynamic"
|
|
||||||
"k8s.io/client-go/rest"
|
|
||||||
"k8s.io/client-go/restmapper"
|
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Provider interface {
|
type Provider interface {
|
||||||
@@ -28,14 +23,14 @@ type Provider interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewProvider(cfg *config.StaticConfig) (Provider, error) {
|
func NewProvider(cfg *config.StaticConfig) (Provider, error) {
|
||||||
m, err := NewManager(cfg)
|
strategy := resolveStrategy(cfg)
|
||||||
|
|
||||||
|
factory, err := getProviderFactory(strategy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
strategy := resolveStrategy(cfg, m)
|
m, err := NewManager(cfg, "")
|
||||||
|
|
||||||
factory, err := getProviderFactory(strategy)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -43,61 +38,16 @@ func NewProvider(cfg *config.StaticConfig) (Provider, error) {
|
|||||||
return factory(m, cfg)
|
return factory(m, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) newForContext(context string) (*Manager, error) {
|
func resolveStrategy(cfg *config.StaticConfig) string {
|
||||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
|
||||||
if m.staticConfig.KubeConfig != "" {
|
|
||||||
pathOptions.LoadingRules.ExplicitPath = m.staticConfig.KubeConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
clientCmdConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
|
|
||||||
pathOptions.LoadingRules,
|
|
||||||
&clientcmd.ConfigOverrides{
|
|
||||||
CurrentContext: context,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
cfg, err := clientCmdConfig.ClientConfig()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.UserAgent == "" {
|
|
||||||
cfg.UserAgent = rest.DefaultKubernetesUserAgent()
|
|
||||||
}
|
|
||||||
|
|
||||||
manager := &Manager{
|
|
||||||
cfg: cfg,
|
|
||||||
clientCmdConfig: clientCmdConfig,
|
|
||||||
staticConfig: m.staticConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize clients for new manager
|
|
||||||
manager.accessControlClientSet, err = NewAccessControlClientset(manager.cfg, manager.staticConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
manager.discoveryClient = memory.NewMemCacheClient(manager.accessControlClientSet.DiscoveryClient())
|
|
||||||
|
|
||||||
manager.accessControlRESTMapper = NewAccessControlRESTMapper(
|
|
||||||
restmapper.NewDeferredDiscoveryRESTMapper(manager.discoveryClient),
|
|
||||||
manager.staticConfig,
|
|
||||||
)
|
|
||||||
|
|
||||||
manager.dynamicClient, err = dynamic.NewForConfig(manager.cfg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return manager, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveStrategy(cfg *config.StaticConfig, m *Manager) string {
|
|
||||||
if cfg.ClusterProviderStrategy != "" {
|
if cfg.ClusterProviderStrategy != "" {
|
||||||
return cfg.ClusterProviderStrategy
|
return cfg.ClusterProviderStrategy
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.IsInCluster() {
|
if cfg.KubeConfig != "" {
|
||||||
|
return config.ClusterProviderKubeConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, inClusterConfigErr := InClusterConfig(); inClusterConfigErr == nil {
|
||||||
return config.ClusterProviderInCluster
|
return config.ClusterProviderInCluster
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func init() {
|
|||||||
// via kubeconfig contexts. Returns an error if the manager is in-cluster mode.
|
// via kubeconfig contexts. Returns an error if the manager is in-cluster mode.
|
||||||
func newKubeConfigClusterProvider(m *Manager, cfg *config.StaticConfig) (Provider, error) {
|
func newKubeConfigClusterProvider(m *Manager, cfg *config.StaticConfig) (Provider, error) {
|
||||||
// Handle in-cluster mode
|
// Handle in-cluster mode
|
||||||
if m.IsInCluster() {
|
if IsInCluster(cfg) {
|
||||||
return nil, fmt.Errorf("kubeconfig ClusterProviderStrategy is invalid for in-cluster deployments")
|
return nil, fmt.Errorf("kubeconfig ClusterProviderStrategy is invalid for in-cluster deployments")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,12 +65,7 @@ func (p *kubeConfigClusterProvider) managerForContext(context string) (*Manager,
|
|||||||
|
|
||||||
baseManager := p.managers[p.defaultContext]
|
baseManager := p.managers[p.defaultContext]
|
||||||
|
|
||||||
if baseManager.IsInCluster() {
|
m, err := NewManager(baseManager.staticConfig, context)
|
||||||
// In cluster mode, so context switching is not applicable
|
|
||||||
return baseManager, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err := baseManager.newForContext(context)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -92,7 +87,7 @@ func (p *kubeConfigClusterProvider) VerifyToken(ctx context.Context, context, to
|
|||||||
return m.VerifyToken(ctx, token, audience)
|
return m.VerifyToken(ctx, token, audience)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *kubeConfigClusterProvider) GetTargets(ctx context.Context) ([]string, error) {
|
func (p *kubeConfigClusterProvider) GetTargets(_ context.Context) ([]string, error) {
|
||||||
contextNames := make([]string, 0, len(p.managers))
|
contextNames := make([]string, 0, len(p.managers))
|
||||||
for contextName := range p.managers {
|
for contextName := range p.managers {
|
||||||
contextNames = append(contextNames, contextName)
|
contextNames = append(contextNames, contextName)
|
||||||
|
|||||||
@@ -96,6 +96,13 @@ func (s *ProviderKubeconfigTestSuite) TestVerifyToken() {
|
|||||||
s.Len(audiences, 1, "Expected audiences from VerifyToken with empty target")
|
s.Len(audiences, 1, "Expected audiences from VerifyToken with empty target")
|
||||||
s.Containsf(audiences, "the-audience", "Expected audience the-audience in %v", audiences)
|
s.Containsf(audiences, "the-audience", "Expected audience the-audience in %v", audiences)
|
||||||
})
|
})
|
||||||
|
s.Run("VerifyToken returns error for invalid context", func() {
|
||||||
|
userInfo, audiences, err := s.provider.VerifyToken(s.T().Context(), "invalid-context", "some-token", "the-audience")
|
||||||
|
s.Require().Error(err, "Expected error from VerifyToken with invalid target")
|
||||||
|
s.ErrorContainsf(err, `context "invalid-context" does not exist`, "Expected context does not exist error, got: %v", err)
|
||||||
|
s.Nil(userInfo, "Expected no UserInfo from VerifyToken with invalid target")
|
||||||
|
s.Nil(audiences, "Expected no audiences from VerifyToken with invalid target")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ProviderKubeconfigTestSuite) TestGetTargets() {
|
func (s *ProviderKubeconfigTestSuite) TestGetTargets() {
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ func init() {
|
|||||||
// Validates that the manager is in-cluster when the in-cluster strategy is used.
|
// Validates that the manager is in-cluster when the in-cluster strategy is used.
|
||||||
func newSingleClusterProvider(strategy string) ProviderFactory {
|
func newSingleClusterProvider(strategy string) ProviderFactory {
|
||||||
return func(m *Manager, cfg *config.StaticConfig) (Provider, error) {
|
return func(m *Manager, cfg *config.StaticConfig) (Provider, error) {
|
||||||
if strategy == config.ClusterProviderInCluster && !m.IsInCluster() {
|
if cfg != nil && cfg.KubeConfig != "" && strategy == config.ClusterProviderInCluster {
|
||||||
|
return nil, fmt.Errorf("kubeconfig file %s cannot be used with the in-cluster ClusterProviderStrategy", cfg.KubeConfig)
|
||||||
|
}
|
||||||
|
if strategy == config.ClusterProviderInCluster && !IsInCluster(cfg) {
|
||||||
return nil, fmt.Errorf("server must be deployed in cluster for the in-cluster ClusterProviderStrategy")
|
return nil, fmt.Errorf("server must be deployed in cluster for the in-cluster ClusterProviderStrategy")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +52,7 @@ func (p *singleClusterProvider) VerifyToken(ctx context.Context, target, token,
|
|||||||
return p.manager.VerifyToken(ctx, token, audience)
|
return p.manager.VerifyToken(ctx, token, audience)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *singleClusterProvider) GetTargets(ctx context.Context) ([]string, error) {
|
func (p *singleClusterProvider) GetTargets(_ context.Context) ([]string, error) {
|
||||||
return []string{""}, nil
|
return []string{""}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package kubernetes
|
package kubernetes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -31,13 +32,30 @@ func (s *BaseProviderSuite) TearDownTest() {
|
|||||||
|
|
||||||
type ProviderTestSuite struct {
|
type ProviderTestSuite struct {
|
||||||
BaseProviderSuite
|
BaseProviderSuite
|
||||||
|
originalEnv []string
|
||||||
|
originalInClusterConfig func() (*rest.Config, error)
|
||||||
|
mockServer *test.MockServer
|
||||||
|
kubeconfigPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ProviderTestSuite) TestNewManagerProviderInCluster() {
|
func (s *ProviderTestSuite) SetupTest() {
|
||||||
originalIsInClusterConfig := InClusterConfig
|
s.BaseProviderSuite.SetupTest()
|
||||||
s.T().Cleanup(func() {
|
s.originalEnv = os.Environ()
|
||||||
InClusterConfig = originalIsInClusterConfig
|
s.originalInClusterConfig = InClusterConfig
|
||||||
})
|
s.mockServer = test.NewMockServer()
|
||||||
|
s.kubeconfigPath = strings.ReplaceAll(s.mockServer.KubeconfigFile(s.T()), `\`, `\\`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProviderTestSuite) TearDownTest() {
|
||||||
|
s.BaseProviderSuite.TearDownTest()
|
||||||
|
test.RestoreEnv(s.originalEnv)
|
||||||
|
InClusterConfig = s.originalInClusterConfig
|
||||||
|
if s.mockServer != nil {
|
||||||
|
s.mockServer.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProviderTestSuite) TestNewProviderInCluster() {
|
||||||
InClusterConfig = func() (*rest.Config, error) {
|
InClusterConfig = func() (*rest.Config, error) {
|
||||||
return &rest.Config{}, nil
|
return &rest.Config{}, nil
|
||||||
}
|
}
|
||||||
@@ -48,7 +66,7 @@ func (s *ProviderTestSuite) TestNewManagerProviderInCluster() {
|
|||||||
s.NotNil(provider, "Expected provider instance")
|
s.NotNil(provider, "Expected provider instance")
|
||||||
s.IsType(&singleClusterProvider{}, provider, "Expected singleClusterProvider type")
|
s.IsType(&singleClusterProvider{}, provider, "Expected singleClusterProvider type")
|
||||||
})
|
})
|
||||||
s.Run("With configured in-cluster cluster_provider_strategy, returns single-cluster provider", func() {
|
s.Run("With cluster_provider_strategy=in-cluster, returns single-cluster provider", func() {
|
||||||
cfg := test.Must(config.ReadToml([]byte(`
|
cfg := test.Must(config.ReadToml([]byte(`
|
||||||
cluster_provider_strategy = "in-cluster"
|
cluster_provider_strategy = "in-cluster"
|
||||||
`)))
|
`)))
|
||||||
@@ -57,7 +75,7 @@ func (s *ProviderTestSuite) TestNewManagerProviderInCluster() {
|
|||||||
s.NotNil(provider, "Expected provider instance")
|
s.NotNil(provider, "Expected provider instance")
|
||||||
s.IsType(&singleClusterProvider{}, provider, "Expected singleClusterProvider type")
|
s.IsType(&singleClusterProvider{}, provider, "Expected singleClusterProvider type")
|
||||||
})
|
})
|
||||||
s.Run("With configured kubeconfig cluster_provider_strategy, returns error", func() {
|
s.Run("With cluster_provider_strategy=kubeconfig, returns error", func() {
|
||||||
cfg := test.Must(config.ReadToml([]byte(`
|
cfg := test.Must(config.ReadToml([]byte(`
|
||||||
cluster_provider_strategy = "kubeconfig"
|
cluster_provider_strategy = "kubeconfig"
|
||||||
`)))
|
`)))
|
||||||
@@ -66,7 +84,17 @@ func (s *ProviderTestSuite) TestNewManagerProviderInCluster() {
|
|||||||
s.ErrorContains(err, "kubeconfig ClusterProviderStrategy is invalid for in-cluster deployments")
|
s.ErrorContains(err, "kubeconfig ClusterProviderStrategy is invalid for in-cluster deployments")
|
||||||
s.Nilf(provider, "Expected no provider instance, got %v", provider)
|
s.Nilf(provider, "Expected no provider instance, got %v", provider)
|
||||||
})
|
})
|
||||||
s.Run("With configured non-existent cluster_provider_strategy, returns error", func() {
|
s.Run("With cluster_provider_strategy=kubeconfig and kubeconfig set to valid path, returns kubeconfig provider", func() {
|
||||||
|
cfg := test.Must(config.ReadToml([]byte(`
|
||||||
|
cluster_provider_strategy = "kubeconfig"
|
||||||
|
kubeconfig = "` + s.kubeconfigPath + `"
|
||||||
|
`)))
|
||||||
|
provider, err := NewProvider(cfg)
|
||||||
|
s.Require().NoError(err, "Expected no error for kubeconfig strategy")
|
||||||
|
s.NotNil(provider, "Expected provider instance")
|
||||||
|
s.IsType(&kubeConfigClusterProvider{}, provider, "Expected kubeConfigClusterProvider type")
|
||||||
|
})
|
||||||
|
s.Run("With cluster_provider_strategy=non-existent, returns error", func() {
|
||||||
cfg := test.Must(config.ReadToml([]byte(`
|
cfg := test.Must(config.ReadToml([]byte(`
|
||||||
cluster_provider_strategy = "i-do-not-exist"
|
cluster_provider_strategy = "i-do-not-exist"
|
||||||
`)))
|
`)))
|
||||||
@@ -77,22 +105,20 @@ func (s *ProviderTestSuite) TestNewManagerProviderInCluster() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ProviderTestSuite) TestNewManagerProviderLocal() {
|
func (s *ProviderTestSuite) TestNewProviderLocal() {
|
||||||
mockServer := test.NewMockServer()
|
InClusterConfig = func() (*rest.Config, error) {
|
||||||
s.T().Cleanup(mockServer.Close)
|
return nil, rest.ErrNotInCluster
|
||||||
kubeconfigPath := strings.ReplaceAll(mockServer.KubeconfigFile(s.T()), `\`, `\\`)
|
}
|
||||||
|
s.Require().NoError(os.Setenv("KUBECONFIG", s.kubeconfigPath))
|
||||||
s.Run("With no cluster_provider_strategy, returns kubeconfig provider", func() {
|
s.Run("With no cluster_provider_strategy, returns kubeconfig provider", func() {
|
||||||
cfg := test.Must(config.ReadToml([]byte(`
|
cfg := test.Must(config.ReadToml([]byte{}))
|
||||||
kubeconfig = "` + kubeconfigPath + `"
|
|
||||||
`)))
|
|
||||||
provider, err := NewProvider(cfg)
|
provider, err := NewProvider(cfg)
|
||||||
s.Require().NoError(err, "Expected no error for kubeconfig provider")
|
s.Require().NoError(err, "Expected no error for kubeconfig provider")
|
||||||
s.NotNil(provider, "Expected provider instance")
|
s.NotNil(provider, "Expected provider instance")
|
||||||
s.IsType(&kubeConfigClusterProvider{}, provider, "Expected kubeConfigClusterProvider type")
|
s.IsType(&kubeConfigClusterProvider{}, provider, "Expected kubeConfigClusterProvider type")
|
||||||
})
|
})
|
||||||
s.Run("With configured kubeconfig cluster_provider_strategy, returns kubeconfig provider", func() {
|
s.Run("With cluster_provider_strategy=kubeconfig, returns kubeconfig provider", func() {
|
||||||
cfg := test.Must(config.ReadToml([]byte(`
|
cfg := test.Must(config.ReadToml([]byte(`
|
||||||
kubeconfig = "` + kubeconfigPath + `"
|
|
||||||
cluster_provider_strategy = "kubeconfig"
|
cluster_provider_strategy = "kubeconfig"
|
||||||
`)))
|
`)))
|
||||||
provider, err := NewProvider(cfg)
|
provider, err := NewProvider(cfg)
|
||||||
@@ -100,9 +126,8 @@ func (s *ProviderTestSuite) TestNewManagerProviderLocal() {
|
|||||||
s.NotNil(provider, "Expected provider instance")
|
s.NotNil(provider, "Expected provider instance")
|
||||||
s.IsType(&kubeConfigClusterProvider{}, provider, "Expected kubeConfigClusterProvider type")
|
s.IsType(&kubeConfigClusterProvider{}, provider, "Expected kubeConfigClusterProvider type")
|
||||||
})
|
})
|
||||||
s.Run("With configured in-cluster cluster_provider_strategy, returns error", func() {
|
s.Run("With cluster_provider_strategy=in-cluster, returns error", func() {
|
||||||
cfg := test.Must(config.ReadToml([]byte(`
|
cfg := test.Must(config.ReadToml([]byte(`
|
||||||
kubeconfig = "` + kubeconfigPath + `"
|
|
||||||
cluster_provider_strategy = "in-cluster"
|
cluster_provider_strategy = "in-cluster"
|
||||||
`)))
|
`)))
|
||||||
provider, err := NewProvider(cfg)
|
provider, err := NewProvider(cfg)
|
||||||
@@ -110,9 +135,18 @@ func (s *ProviderTestSuite) TestNewManagerProviderLocal() {
|
|||||||
s.ErrorContains(err, "server must be deployed in cluster for the in-cluster ClusterProviderStrategy")
|
s.ErrorContains(err, "server must be deployed in cluster for the in-cluster ClusterProviderStrategy")
|
||||||
s.Nilf(provider, "Expected no provider instance, got %v", provider)
|
s.Nilf(provider, "Expected no provider instance, got %v", provider)
|
||||||
})
|
})
|
||||||
s.Run("With configured non-existent cluster_provider_strategy, returns error", func() {
|
s.Run("With cluster_provider_strategy=in-cluster and kubeconfig set to valid path, returns error", func() {
|
||||||
|
cfg := test.Must(config.ReadToml([]byte(`
|
||||||
|
kubeconfig = "` + s.kubeconfigPath + `"
|
||||||
|
cluster_provider_strategy = "in-cluster"
|
||||||
|
`)))
|
||||||
|
provider, err := NewProvider(cfg)
|
||||||
|
s.Require().Error(err, "Expected error for in-cluster strategy")
|
||||||
|
s.Regexp("kubeconfig file .+ cannot be used with the in-cluster ClusterProviderStrategy", err.Error())
|
||||||
|
s.Nilf(provider, "Expected no provider instance, got %v", provider)
|
||||||
|
})
|
||||||
|
s.Run("With configured cluster_provider_strategy=non-existent, returns error", func() {
|
||||||
cfg := test.Must(config.ReadToml([]byte(`
|
cfg := test.Must(config.ReadToml([]byte(`
|
||||||
kubeconfig = "` + kubeconfigPath + `"
|
|
||||||
cluster_provider_strategy = "i-do-not-exist"
|
cluster_provider_strategy = "i-do-not-exist"
|
||||||
`)))
|
`)))
|
||||||
provider, err := NewProvider(cfg)
|
provider, err := NewProvider(cfg)
|
||||||
|
|||||||
Reference in New Issue
Block a user