diff --git a/pkg/api/config/config.go b/pkg/api/config/config.go new file mode 100644 index 000000000..ee20d312a --- /dev/null +++ b/pkg/api/config/config.go @@ -0,0 +1,53 @@ +package config + +const ( + ClusterProviderKubeConfig = "kubeconfig" + ClusterProviderInCluster = "in-cluster" + ClusterProviderDisabled = "disabled" +) + +type AuthProvider interface { + // IsRequireOAuth indicates whether OAuth authentication is required. + IsRequireOAuth() bool +} + +type ClusterProvider interface { + // GetClusterProviderStrategy returns the cluster provider strategy (if configured). + GetClusterProviderStrategy() string + // GetKubeConfigPath returns the path to the kubeconfig file (if configured). + GetKubeConfigPath() string +} + +// Extended is the interface that all configuration extensions must implement. +// Each extended config manager registers a factory function to parse its config from TOML primitives +type Extended interface { + // Validate validates the extended configuration. Returns an error if the configuration is invalid. + Validate() error +} + +type ExtendedProvider interface { + // GetProviderConfig returns the extended configuration for the given provider strategy. + // The boolean return value indicates whether the configuration was found. + GetProviderConfig(strategy string) (Extended, bool) + // GetToolsetConfig returns the extended configuration for the given toolset name. + // The boolean return value indicates whether the configuration was found. + GetToolsetConfig(name string) (Extended, bool) +} + +type GroupVersionKind struct { + Group string `json:"group" toml:"group"` + Version string `json:"version" toml:"version"` + Kind string `json:"kind,omitempty" toml:"kind,omitempty"` +} + +type DeniedResourcesProvider interface { + // GetDeniedResources returns a list of GroupVersionKinds that are denied. + GetDeniedResources() []GroupVersionKind +} + +type BaseConfig interface { + AuthProvider + ClusterProvider + DeniedResourcesProvider + ExtendedProvider +} diff --git a/pkg/config/config.go b/pkg/config/config.go index 836b24825..80295112a 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/BurntSushi/toml" + configapi "github.com/containers/kubernetes-mcp-server/pkg/api/config" "k8s.io/klog/v2" ) @@ -17,16 +18,10 @@ const ( DefaultDropInConfigDir = "conf.d" ) -const ( - ClusterProviderKubeConfig = "kubeconfig" - ClusterProviderInCluster = "in-cluster" - ClusterProviderDisabled = "disabled" -) - // StaticConfig is the configuration for the server. // It allows to configure server specific settings and tools to be enabled or disabled. type StaticConfig struct { - DeniedResources []GroupVersionKind `toml:"denied_resources"` + DeniedResources []configapi.GroupVersionKind `toml:"denied_resources"` LogLevel int `toml:"log_level,omitzero"` Port string `toml:"port,omitempty"` @@ -80,19 +75,15 @@ type StaticConfig struct { ToolsetConfigs map[string]toml.Primitive `toml:"toolset_configs,omitempty"` // Internal: parsed provider configs (not exposed to TOML package) - parsedClusterProviderConfigs map[string]Extended + parsedClusterProviderConfigs map[string]configapi.Extended // Internal: parsed toolset configs (not exposed to TOML package) - parsedToolsetConfigs map[string]Extended + parsedToolsetConfigs map[string]configapi.Extended // Internal: the config.toml directory, to help resolve relative file paths configDirPath string } -type GroupVersionKind struct { - Group string `toml:"group"` - Version string `toml:"version"` - Kind string `toml:"kind,omitempty"` -} +var _ configapi.BaseConfig = (*StaticConfig)(nil) type ReadConfigOpt func(cfg *StaticConfig) @@ -292,13 +283,29 @@ func ReadToml(configData []byte, opts ...ReadConfigOpt) (*StaticConfig, error) { return config, nil } -func (c *StaticConfig) GetProviderConfig(strategy string) (Extended, bool) { - config, ok := c.parsedClusterProviderConfigs[strategy] +func (c *StaticConfig) GetClusterProviderStrategy() string { + return c.ClusterProviderStrategy +} + +func (c *StaticConfig) GetDeniedResources() []configapi.GroupVersionKind { + return c.DeniedResources +} - return config, ok +func (c *StaticConfig) GetKubeConfigPath() string { + return c.KubeConfig } -func (c *StaticConfig) GetToolsetConfig(name string) (Extended, bool) { +func (c *StaticConfig) GetProviderConfig(strategy string) (configapi.Extended, bool) { + cfg, ok := c.parsedClusterProviderConfigs[strategy] + + return cfg, ok +} + +func (c *StaticConfig) GetToolsetConfig(name string) (configapi.Extended, bool) { cfg, ok := c.parsedToolsetConfigs[name] return cfg, ok } + +func (c *StaticConfig) IsRequireOAuth() bool { + return c.RequireOAuth +} diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 2d5feba60..547bd1685 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + configapi "github.com/containers/kubernetes-mcp-server/pkg/api/config" "github.com/stretchr/testify/suite" ) @@ -136,11 +137,11 @@ func (s *ConfigSuite) TestReadConfigValid() { s.Run("denied_resources", func() { s.Require().Lenf(config.DeniedResources, 2, "Expected 2 denied resources, got %d", len(config.DeniedResources)) s.Run("contains apps/v1/Deployment", func() { - s.Contains(config.DeniedResources, GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}, + s.Contains(config.DeniedResources, configapi.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}, "Expected denied resources to contain apps/v1/Deployment") }) s.Run("contains rbac.authorization.k8s.io/v1/Role", func() { - s.Contains(config.DeniedResources, GroupVersionKind{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "Role"}, + s.Contains(config.DeniedResources, configapi.GroupVersionKind{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "Role"}, "Expected denied resources to contain rbac.authorization.k8s.io/v1/Role") }) }) @@ -777,16 +778,16 @@ func (s *ConfigSuite) TestDropInWithDeniedResources() { s.Run("drop-in replaces denied_resources array", func() { s.Len(config.DeniedResources, 2, "denied_resources should have 2 entries from drop-in") - s.Contains(config.DeniedResources, GroupVersionKind{ + s.Contains(config.DeniedResources, configapi.GroupVersionKind{ Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRole", }) - s.Contains(config.DeniedResources, GroupVersionKind{ + s.Contains(config.DeniedResources, configapi.GroupVersionKind{ Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBinding", }) }) s.Run("original denied_resources from main config are replaced", func() { - s.NotContains(config.DeniedResources, GroupVersionKind{ + s.NotContains(config.DeniedResources, configapi.GroupVersionKind{ Group: "apps", Version: "v1", Kind: "Deployment", }, "original entry should be replaced by drop-in") }) diff --git a/pkg/config/extended.go b/pkg/config/extended.go index a1f0598df..eb3e37eda 100644 --- a/pkg/config/extended.go +++ b/pkg/config/extended.go @@ -5,15 +5,10 @@ import ( "fmt" "github.com/BurntSushi/toml" + configapi "github.com/containers/kubernetes-mcp-server/pkg/api/config" ) -// Extended is the interface that all configuration extensions must implement. -// Each extended config manager registers a factory function to parse its config from TOML primitives -type Extended interface { - Validate() error -} - -type ExtendedConfigParser func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (Extended, error) +type ExtendedConfigParser func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (configapi.Extended, error) type extendedConfigRegistry struct { parsers map[string]ExtendedConfigParser @@ -33,11 +28,11 @@ func (r *extendedConfigRegistry) register(name string, parser ExtendedConfigPars r.parsers[name] = parser } -func (r *extendedConfigRegistry) parse(ctx context.Context, metaData toml.MetaData, configs map[string]toml.Primitive) (map[string]Extended, error) { +func (r *extendedConfigRegistry) parse(ctx context.Context, metaData toml.MetaData, configs map[string]toml.Primitive) (map[string]configapi.Extended, error) { if len(configs) == 0 { - return make(map[string]Extended), nil + return make(map[string]configapi.Extended), nil } - parsedConfigs := make(map[string]Extended, len(configs)) + parsedConfigs := make(map[string]configapi.Extended, len(configs)) for name, primitive := range configs { parser, ok := r.parsers[name] diff --git a/pkg/config/provider_config_test.go b/pkg/config/provider_config_test.go index f491131f2..28d511138 100644 --- a/pkg/config/provider_config_test.go +++ b/pkg/config/provider_config_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/BurntSushi/toml" + configapi "github.com/containers/kubernetes-mcp-server/pkg/api/config" "github.com/stretchr/testify/suite" ) @@ -31,7 +32,7 @@ type ProviderConfigForTest struct { IntProp int `toml:"int_prop"` } -var _ Extended = (*ProviderConfigForTest)(nil) +var _ configapi.Extended = (*ProviderConfigForTest)(nil) func (p *ProviderConfigForTest) Validate() error { if p.StrProp == "force-error" { @@ -40,7 +41,7 @@ func (p *ProviderConfigForTest) Validate() error { return nil } -func providerConfigForTestParser(_ context.Context, primitive toml.Primitive, md toml.MetaData) (Extended, error) { +func providerConfigForTestParser(_ context.Context, primitive toml.Primitive, md toml.MetaData) (configapi.Extended, error) { var providerConfigForTest ProviderConfigForTest if err := md.PrimitiveDecode(primitive, &providerConfigForTest); err != nil { return nil, err @@ -129,7 +130,7 @@ func (s *ProviderConfigSuite) TestReadConfigUnregisteredProviderConfig() { } func (s *ProviderConfigSuite) TestReadConfigParserError() { - RegisterProviderConfig("test", func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (Extended, error) { + RegisterProviderConfig("test", func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (configapi.Extended, error) { return nil, errors.New("parser error forced by test") }) invalidConfigPath := s.writeConfig(` @@ -152,7 +153,7 @@ func (s *ProviderConfigSuite) TestReadConfigParserError() { func (s *ProviderConfigSuite) TestConfigDirPathInContext() { var capturedDirPath string - RegisterProviderConfig("test", func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (Extended, error) { + RegisterProviderConfig("test", func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (configapi.Extended, error) { capturedDirPath = ConfigDirPathFromContext(ctx) var providerConfigForTest ProviderConfigForTest if err := md.PrimitiveDecode(primitive, &providerConfigForTest); err != nil { @@ -328,7 +329,7 @@ func (s *ProviderConfigSuite) TestStandaloneConfigDirWithExtendedConfig() { func (s *ProviderConfigSuite) TestConfigDirPathInContextStandalone() { // Test that configDirPath is correctly set in context for standalone --config-dir var capturedDirPath string - RegisterProviderConfig("test", func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (Extended, error) { + RegisterProviderConfig("test", func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (configapi.Extended, error) { capturedDirPath = ConfigDirPathFromContext(ctx) var providerConfigForTest ProviderConfigForTest if err := md.PrimitiveDecode(primitive, &providerConfigForTest); err != nil { diff --git a/pkg/config/toolset_config_test.go b/pkg/config/toolset_config_test.go index aff0b2979..b8045b064 100644 --- a/pkg/config/toolset_config_test.go +++ b/pkg/config/toolset_config_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/BurntSushi/toml" + configapi "github.com/containers/kubernetes-mcp-server/pkg/api/config" "github.com/stretchr/testify/suite" ) @@ -31,7 +32,7 @@ type ToolsetConfigForTest struct { Timeout int `toml:"timeout"` } -var _ Extended = (*ToolsetConfigForTest)(nil) +var _ configapi.Extended = (*ToolsetConfigForTest)(nil) func (t *ToolsetConfigForTest) Validate() error { if t.Endpoint == "force-error" { @@ -40,7 +41,7 @@ func (t *ToolsetConfigForTest) Validate() error { return nil } -func toolsetConfigForTestParser(_ context.Context, primitive toml.Primitive, md toml.MetaData) (Extended, error) { +func toolsetConfigForTestParser(_ context.Context, primitive toml.Primitive, md toml.MetaData) (configapi.Extended, error) { var toolsetConfigForTest ToolsetConfigForTest if err := md.PrimitiveDecode(primitive, &toolsetConfigForTest); err != nil { return nil, err @@ -127,7 +128,7 @@ func (s *ToolsetConfigSuite) TestReadConfigUnregisteredToolsetConfig() { func (s *ToolsetConfigSuite) TestConfigDirPathInContext() { var capturedDirPath string - RegisterToolsetConfig("test-toolset", func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (Extended, error) { + RegisterToolsetConfig("test-toolset", func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (configapi.Extended, error) { capturedDirPath = ConfigDirPathFromContext(ctx) var toolsetConfigForTest ToolsetConfigForTest if err := md.PrimitiveDecode(primitive, &toolsetConfigForTest); err != nil { @@ -299,7 +300,7 @@ func (s *ToolsetConfigSuite) TestStandaloneConfigDirWithExtendedConfig() { func (s *ToolsetConfigSuite) TestConfigDirPathInContextStandalone() { // Test that configDirPath is correctly set in context for standalone --config-dir var capturedDirPath string - RegisterToolsetConfig("test-toolset", func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (Extended, error) { + RegisterToolsetConfig("test-toolset", func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (configapi.Extended, error) { capturedDirPath = ConfigDirPathFromContext(ctx) var toolsetConfigForTest ToolsetConfigForTest if err := md.PrimitiveDecode(primitive, &toolsetConfigForTest); err != nil { diff --git a/pkg/http/http_test.go b/pkg/http/http_test.go index 091fca2e1..0b0f0de0a 100644 --- a/pkg/http/http_test.go +++ b/pkg/http/http_test.go @@ -26,6 +26,7 @@ import ( "k8s.io/klog/v2" "k8s.io/klog/v2/textlogger" + configapi "github.com/containers/kubernetes-mcp-server/pkg/api/config" "github.com/containers/kubernetes-mcp-server/pkg/config" "github.com/containers/kubernetes-mcp-server/pkg/mcp" ) @@ -240,7 +241,7 @@ func TestHealthCheck(t *testing.T) { }) }) // Health exposed even when require Authorization - testCaseWithContext(t, &httpContext{StaticConfig: &config.StaticConfig{RequireOAuth: true, ValidateToken: true, ClusterProviderStrategy: config.ClusterProviderKubeConfig}}, func(ctx *httpContext) { + testCaseWithContext(t, &httpContext{StaticConfig: &config.StaticConfig{RequireOAuth: true, ValidateToken: true, ClusterProviderStrategy: configapi.ClusterProviderKubeConfig}}, func(ctx *httpContext) { resp, err := http.Get(fmt.Sprintf("http://%s/healthz", ctx.HttpAddress)) if err != nil { t.Fatalf("Failed to get health check endpoint with OAuth: %v", err) @@ -261,7 +262,7 @@ func TestWellKnownReverseProxy(t *testing.T) { ".well-known/openid-configuration", } // With No Authorization URL configured - testCaseWithContext(t, &httpContext{StaticConfig: &config.StaticConfig{RequireOAuth: true, ValidateToken: true, ClusterProviderStrategy: config.ClusterProviderKubeConfig}}, func(ctx *httpContext) { + testCaseWithContext(t, &httpContext{StaticConfig: &config.StaticConfig{RequireOAuth: true, ValidateToken: true, ClusterProviderStrategy: configapi.ClusterProviderKubeConfig}}, func(ctx *httpContext) { for _, path := range cases { resp, err := http.Get(fmt.Sprintf("http://%s/%s", ctx.HttpAddress, path)) t.Cleanup(func() { _ = resp.Body.Close() }) @@ -285,7 +286,7 @@ func TestWellKnownReverseProxy(t *testing.T) { AuthorizationURL: invalidPayloadServer.URL, RequireOAuth: true, ValidateToken: true, - ClusterProviderStrategy: config.ClusterProviderKubeConfig, + ClusterProviderStrategy: configapi.ClusterProviderKubeConfig, } testCaseWithContext(t, &httpContext{StaticConfig: invalidPayloadConfig}, func(ctx *httpContext) { for _, path := range cases { @@ -315,7 +316,7 @@ func TestWellKnownReverseProxy(t *testing.T) { AuthorizationURL: testServer.URL, RequireOAuth: true, ValidateToken: true, - ClusterProviderStrategy: config.ClusterProviderKubeConfig, + ClusterProviderStrategy: configapi.ClusterProviderKubeConfig, } testCaseWithContext(t, &httpContext{StaticConfig: staticConfig}, func(ctx *httpContext) { for _, path := range cases { @@ -365,7 +366,7 @@ func TestWellKnownHeaderPropagation(t *testing.T) { AuthorizationURL: testServer.URL, RequireOAuth: true, ValidateToken: true, - ClusterProviderStrategy: config.ClusterProviderKubeConfig, + ClusterProviderStrategy: configapi.ClusterProviderKubeConfig, } testCaseWithContext(t, &httpContext{StaticConfig: staticConfig}, func(ctx *httpContext) { for _, path := range cases { @@ -479,7 +480,7 @@ func TestWellKnownOverrides(t *testing.T) { AuthorizationURL: testServer.URL, RequireOAuth: true, ValidateToken: true, - ClusterProviderStrategy: config.ClusterProviderKubeConfig, + ClusterProviderStrategy: configapi.ClusterProviderKubeConfig, } // With Dynamic Client Registration disabled disableDynamicRegistrationConfig := baseConfig diff --git a/pkg/kiali/config.go b/pkg/kiali/config.go index 6ff9f5ba6..b36899015 100644 --- a/pkg/kiali/config.go +++ b/pkg/kiali/config.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/BurntSushi/toml" + configapi "github.com/containers/kubernetes-mcp-server/pkg/api/config" "github.com/containers/kubernetes-mcp-server/pkg/config" ) @@ -20,7 +21,7 @@ type Config struct { CertificateAuthority string `toml:"certificate_authority,omitempty"` } -var _ config.Extended = (*Config)(nil) +var _ configapi.Extended = (*Config)(nil) func (c *Config) Validate() error { if c == nil { @@ -45,7 +46,7 @@ func (c *Config) Validate() error { return nil } -func kialiToolsetParser(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (config.Extended, error) { +func kialiToolsetParser(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (configapi.Extended, error) { var cfg Config if err := md.PrimitiveDecode(primitive, &cfg); err != nil { return nil, err diff --git a/pkg/kiali/kiali.go b/pkg/kiali/kiali.go index 6a3edb785..66c23fe82 100644 --- a/pkg/kiali/kiali.go +++ b/pkg/kiali/kiali.go @@ -11,7 +11,7 @@ import ( "os" "strings" - "github.com/containers/kubernetes-mcp-server/pkg/config" + "github.com/containers/kubernetes-mcp-server/pkg/api/config" "k8s.io/client-go/rest" "k8s.io/klog/v2" ) @@ -24,9 +24,9 @@ type Kiali struct { } // NewKiali creates a new Kiali instance -func NewKiali(config *config.StaticConfig, kubernetes *rest.Config) *Kiali { +func NewKiali(configProvider config.ExtendedProvider, kubernetes *rest.Config) *Kiali { kiali := &Kiali{bearerToken: kubernetes.BearerToken} - if cfg, ok := config.GetToolsetConfig("kiali"); ok { + if cfg, ok := configProvider.GetToolsetConfig("kiali"); ok { if kc, ok := cfg.(*Config); ok && kc != nil { kiali.kialiURL = kc.Url kiali.kialiInsecure = kc.Insecure diff --git a/pkg/kubernetes-mcp-server/cmd/root.go b/pkg/kubernetes-mcp-server/cmd/root.go index 42ed61135..e6c50b8ea 100644 --- a/pkg/kubernetes-mcp-server/cmd/root.go +++ b/pkg/kubernetes-mcp-server/cmd/root.go @@ -24,6 +24,7 @@ import ( "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/templates" + configapi "github.com/containers/kubernetes-mcp-server/pkg/api/config" "github.com/containers/kubernetes-mcp-server/pkg/config" internalhttp "github.com/containers/kubernetes-mcp-server/pkg/http" "github.com/containers/kubernetes-mcp-server/pkg/mcp" @@ -223,7 +224,7 @@ func (m *MCPServerOptions) loadFlags(cmd *cobra.Command) { m.StaticConfig.CertificateAuthority = m.CertificateAuthority } if cmd.Flag(flagDisableMultiCluster).Changed && m.DisableMultiCluster { - m.StaticConfig.ClusterProviderStrategy = config.ClusterProviderDisabled + m.StaticConfig.ClusterProviderStrategy = configapi.ClusterProviderDisabled } } diff --git a/pkg/kubernetes/accesscontrol_client_set.go b/pkg/kubernetes/accesscontrol_client_set.go index abafb75cd..c27fbf983 100644 --- a/pkg/kubernetes/accesscontrol_client_set.go +++ b/pkg/kubernetes/accesscontrol_client_set.go @@ -4,7 +4,7 @@ import ( "fmt" "net/http" - "github.com/containers/kubernetes-mcp-server/pkg/config" + configapi "github.com/containers/kubernetes-mcp-server/pkg/api/config" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/client-go/discovery" "k8s.io/client-go/discovery/cached/memory" @@ -24,7 +24,7 @@ import ( // apiVersion and kinds are checked for allowed access type AccessControlClientset struct { kubernetes.Interface - staticConfig *config.StaticConfig + config configapi.BaseConfig clientCmdConfig clientcmd.ClientConfig cfg *rest.Config restMapper meta.ResettableRESTMapper @@ -33,9 +33,9 @@ type AccessControlClientset struct { metricsV1beta1 *metricsv1beta1.MetricsV1beta1Client } -func NewAccessControlClientset(staticConfig *config.StaticConfig, clientCmdConfig clientcmd.ClientConfig, restConfig *rest.Config) (*AccessControlClientset, error) { +func NewAccessControlClientset(config configapi.BaseConfig, clientCmdConfig clientcmd.ClientConfig, restConfig *rest.Config) (*AccessControlClientset, error) { acc := &AccessControlClientset{ - staticConfig: staticConfig, + config: config, clientCmdConfig: clientCmdConfig, cfg: rest.CopyConfig(restConfig), } @@ -44,9 +44,9 @@ func NewAccessControlClientset(staticConfig *config.StaticConfig, clientCmdConfi } acc.cfg.Wrap(func(original http.RoundTripper) http.RoundTripper { return &AccessControlRoundTripper{ - delegate: original, - staticConfig: staticConfig, - restMapper: acc.restMapper, + delegate: original, + deniedResourcesProvider: config, + restMapper: acc.restMapper, } }) discoveryClient, err := discovery.NewDiscoveryClientForConfig(acc.cfg) diff --git a/pkg/kubernetes/accesscontrol_round_tripper.go b/pkg/kubernetes/accesscontrol_round_tripper.go index c818bb717..887a94f13 100644 --- a/pkg/kubernetes/accesscontrol_round_tripper.go +++ b/pkg/kubernetes/accesscontrol_round_tripper.go @@ -5,15 +5,15 @@ import ( "net/http" "strings" - "github.com/containers/kubernetes-mcp-server/pkg/config" + configapi "github.com/containers/kubernetes-mcp-server/pkg/api/config" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime/schema" ) type AccessControlRoundTripper struct { - delegate http.RoundTripper - staticConfig *config.StaticConfig - restMapper meta.RESTMapper + delegate http.RoundTripper + deniedResourcesProvider configapi.DeniedResourcesProvider + restMapper meta.RESTMapper } func (rt *AccessControlRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { @@ -39,11 +39,11 @@ func (rt *AccessControlRoundTripper) RoundTrip(req *http.Request) (*http.Respons func (rt *AccessControlRoundTripper) isAllowed( gvk schema.GroupVersionKind, ) bool { - if rt.staticConfig == nil { + if rt.deniedResourcesProvider == nil { return true } - for _, val := range rt.staticConfig.DeniedResources { + for _, val := range rt.deniedResourcesProvider.GetDeniedResources() { // If kind is empty, that means Group/Version pair is denied entirely if val.Kind == "" { if gvk.Group == val.Group && gvk.Version == val.Version { diff --git a/pkg/kubernetes/accesscontrol_round_tripper_test.go b/pkg/kubernetes/accesscontrol_round_tripper_test.go index 8706df20f..6d0541fd8 100644 --- a/pkg/kubernetes/accesscontrol_round_tripper_test.go +++ b/pkg/kubernetes/accesscontrol_round_tripper_test.go @@ -56,9 +56,9 @@ func (s *AccessControlRoundTripperTestSuite) TestRoundTripForNonAPIResources() { } rt := &AccessControlRoundTripper{ - delegate: mockDelegate, - staticConfig: nil, - restMapper: s.restMapper, + delegate: mockDelegate, + deniedResourcesProvider: nil, + restMapper: s.restMapper, } testCases := []string{"healthz", "readyz", "livez", "metrics", "version"} @@ -84,9 +84,9 @@ func (s *AccessControlRoundTripperTestSuite) TestRoundTripForDiscoveryRequests() } rt := &AccessControlRoundTripper{ - delegate: mockDelegate, - staticConfig: nil, - restMapper: s.restMapper, + delegate: mockDelegate, + deniedResourcesProvider: nil, + restMapper: s.restMapper, } testCases := []string{"/api", "/apis", "/api/v1", "/api/v1/", "/apis/apps", "/apis/apps/v1", "/apis/batch/v1"} @@ -112,9 +112,9 @@ func (s *AccessControlRoundTripperTestSuite) TestRoundTripForAllowedAPIResources } rt := &AccessControlRoundTripper{ - delegate: mockDelegate, - staticConfig: nil, // nil config allows all resources - restMapper: s.restMapper, + delegate: mockDelegate, + deniedResourcesProvider: nil, // nil config allows all resources + restMapper: s.restMapper, } s.Run("List all pods is allowed", func() { @@ -182,15 +182,15 @@ func (s *AccessControlRoundTripperTestSuite) TestRoundTripForDeniedAPIResources( }, } rt := &AccessControlRoundTripper{ - delegate: mockDelegate, - staticConfig: config.Default(), - restMapper: s.restMapper, + delegate: mockDelegate, + deniedResourcesProvider: config.Default(), + restMapper: s.restMapper, } s.Run("Specific resource kind is denied", func() { s.Require().NoError(toml.Unmarshal([]byte(` denied_resources = [ { version = "v1", kind = "Pod" } ] - `), rt.staticConfig), "Expected to parse denied resources config") + `), rt.deniedResourcesProvider), "Expected to parse denied resources config") s.Run("List pods is denied", func() { delegateCalled = false @@ -217,7 +217,7 @@ func (s *AccessControlRoundTripperTestSuite) TestRoundTripForDeniedAPIResources( s.Run("Entire group/version is denied", func() { s.Require().NoError(toml.Unmarshal([]byte(` denied_resources = [ { version = "v1", kind = "" } ] - `), rt.staticConfig), "Expected to v1 denied resources config") + `), rt.deniedResourcesProvider), "Expected to v1 denied resources config") s.Run("Pods in core/v1 are denied", func() { delegateCalled = false @@ -231,7 +231,7 @@ func (s *AccessControlRoundTripperTestSuite) TestRoundTripForDeniedAPIResources( }) s.Run("RESTMapper error for unknown resource", func() { - rt.staticConfig = nil + rt.deniedResourcesProvider = nil delegateCalled = false req := httptest.NewRequest("GET", "/api/v1/unknownresources", nil) resp, err := rt.RoundTrip(req) diff --git a/pkg/kubernetes/configuration.go b/pkg/kubernetes/configuration.go index 8983eb34f..93566a809 100644 --- a/pkg/kubernetes/configuration.go +++ b/pkg/kubernetes/configuration.go @@ -1,7 +1,7 @@ package kubernetes import ( - "github.com/containers/kubernetes-mcp-server/pkg/config" + configapi "github.com/containers/kubernetes-mcp-server/pkg/api/config" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" @@ -22,9 +22,9 @@ var InClusterConfig = func() (*rest.Config, error) { return inClusterConfig, err } -func IsInCluster(cfg *config.StaticConfig) bool { +func IsInCluster(cfg configapi.ClusterProvider) bool { // Even if running in-cluster, if a kubeconfig is provided, we consider it as out-of-cluster - if cfg != nil && cfg.KubeConfig != "" { + if cfg != nil && cfg.GetKubeConfigPath() != "" { return false } restConfig, err := InClusterConfig() diff --git a/pkg/kubernetes/kubernetes.go b/pkg/kubernetes/kubernetes.go index 78296c546..e594cf072 100644 --- a/pkg/kubernetes/kubernetes.go +++ b/pkg/kubernetes/kubernetes.go @@ -47,7 +47,7 @@ func (k *Kubernetes) NewHelm() *helm.Helm { // 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.AccessControlClientset().staticConfig, k.AccessControlClientset().cfg) + return kiali.NewKiali(k.AccessControlClientset().config, k.AccessControlClientset().cfg) } func (k *Kubernetes) configuredNamespace() string { diff --git a/pkg/kubernetes/kubernetes_derived_test.go b/pkg/kubernetes/kubernetes_derived_test.go index 0fdd86c17..f75e65993 100644 --- a/pkg/kubernetes/kubernetes_derived_test.go +++ b/pkg/kubernetes/kubernetes_derived_test.go @@ -77,7 +77,7 @@ users: s.Require().NoErrorf(err, "failed to create derived kubernetes: %v", err) s.NotEqual(derived.AccessControlClientset(), testManager.accessControlClientset, "expected new derived clientset, got original clientset") - s.Equal(derived.AccessControlClientset().staticConfig, testStaticConfig, "staticConfig not properly wired to derived clientset") + s.Equal(derived.AccessControlClientset().config, testStaticConfig, "config not properly wired to derived clientset") s.Run("RestConfig is correctly copied and sensitive fields are omitted", func() { derivedCfg := derived.AccessControlClientset().cfg @@ -130,7 +130,7 @@ users: s.Run("derived kubernetes has initialized clients", func() { // Verify that the derived kubernetes has proper clients initialized s.NotNilf(derived.AccessControlClientset(), "expected accessControlClientSet to be initialized") - s.Equalf(testStaticConfig, derived.AccessControlClientset().staticConfig, "staticConfig not properly wired to derived clientset") + s.Equalf(testStaticConfig, derived.AccessControlClientset().config, "config not properly wired to derived clientset") s.NotNilf(derived.AccessControlClientset().RESTMapper(), "expected accessControlRESTMapper to be initialized") s.NotNilf(derived.AccessControlClientset().DiscoveryClient(), "expected discoveryClient to be initialized") s.NotNilf(derived.AccessControlClientset().DynamicClient(), "expected dynamicClient to be initialized") @@ -283,7 +283,7 @@ users: s.Require().NoErrorf(err, "failed to create derived kubernetes: %v", err) s.NotEqual(derived.AccessControlClientset(), testManager.accessControlClientset, "expected new derived clientset, got original clientset") - s.Equal(derived.AccessControlClientset().staticConfig, testStaticConfig, "staticConfig not properly wired to derived clientset") + s.Equal(derived.AccessControlClientset().config, testStaticConfig, "config not properly wired to derived clientset") derivedCfg := derived.AccessControlClientset().cfg s.Require().NotNil(derivedCfg, "derived config is nil") diff --git a/pkg/kubernetes/manager.go b/pkg/kubernetes/manager.go index a8382ff42..bd75fac21 100644 --- a/pkg/kubernetes/manager.go +++ b/pkg/kubernetes/manager.go @@ -8,7 +8,7 @@ import ( "strconv" "strings" - "github.com/containers/kubernetes-mcp-server/pkg/config" + configapi "github.com/containers/kubernetes-mcp-server/pkg/api/config" authenticationv1api "k8s.io/api/authentication/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/rest" @@ -20,7 +20,7 @@ import ( type Manager struct { accessControlClientset *AccessControlClientset - staticConfig *config.StaticConfig + config configapi.BaseConfig } var _ Openshift = (*Manager)(nil) @@ -30,14 +30,14 @@ var ( ErrorInClusterNotInCluster = errors.New("in-cluster manager cannot be used outside of a cluster") ) -func NewKubeconfigManager(config *config.StaticConfig, kubeconfigContext string) (*Manager, error) { +func NewKubeconfigManager(config configapi.BaseConfig, kubeconfigContext string) (*Manager, error) { if IsInCluster(config) { return nil, ErrorKubeconfigInClusterNotAllowed } pathOptions := clientcmd.NewDefaultPathOptions() - if config.KubeConfig != "" { - pathOptions.LoadingRules.ExplicitPath = config.KubeConfig + if config.GetKubeConfigPath() != "" { + pathOptions.LoadingRules.ExplicitPath = config.GetKubeConfigPath() } clientCmdConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( pathOptions.LoadingRules, @@ -54,9 +54,9 @@ func NewKubeconfigManager(config *config.StaticConfig, kubeconfigContext string) return NewManager(config, restConfig, clientCmdConfig) } -func NewInClusterManager(config *config.StaticConfig) (*Manager, error) { - if config.KubeConfig != "" { - return nil, fmt.Errorf("kubeconfig file %s cannot be used with the in-cluster deployments: %v", config.KubeConfig, ErrorKubeconfigInClusterNotAllowed) +func NewInClusterManager(config configapi.BaseConfig) (*Manager, error) { + if config.GetKubeConfigPath() != "" { + return nil, fmt.Errorf("kubeconfig file %s cannot be used with the in-cluster deployments: %v", config.GetKubeConfigPath(), ErrorKubeconfigInClusterNotAllowed) } if !IsInCluster(config) { @@ -86,7 +86,7 @@ func NewInClusterManager(config *config.StaticConfig) (*Manager, error) { return NewManager(config, restConfig, clientcmd.NewDefaultClientConfig(*clientCmdConfig, nil)) } -func NewManager(config *config.StaticConfig, restConfig *rest.Config, clientCmdConfig clientcmd.ClientConfig) (*Manager, error) { +func NewManager(config configapi.BaseConfig, restConfig *rest.Config, clientCmdConfig clientcmd.ClientConfig) (*Manager, error) { if config == nil { return nil, errors.New("config cannot be nil") } @@ -101,14 +101,14 @@ func NewManager(config *config.StaticConfig, restConfig *rest.Config, clientCmdC applyRateLimitFromEnv(restConfig) k8s := &Manager{ - staticConfig: config, + config: config, } var err error // 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 { // return &impersonateRoundTripper{original} //}) - k8s.accessControlClientset, err = NewAccessControlClientset(k8s.staticConfig, clientCmdConfig, restConfig) + k8s.accessControlClientset, err = NewAccessControlClientset(k8s.config, clientCmdConfig, restConfig) if err != nil { return nil, err } @@ -146,7 +146,7 @@ func (m *Manager) VerifyToken(ctx context.Context, token, audience string) (*aut func (m *Manager) Derived(ctx context.Context) (*Kubernetes, error) { authorization, ok := ctx.Value(OAuthAuthorizationHeader).(string) if !ok || !strings.HasPrefix(authorization, "Bearer ") { - if m.staticConfig.RequireOAuth { + if m.config.IsRequireOAuth() { return nil, errors.New("oauth token required") } return &Kubernetes{m.accessControlClientset}, nil @@ -173,16 +173,16 @@ func (m *Manager) Derived(ctx context.Context) (*Kubernetes, error) { } clientCmdApiConfig, err := m.accessControlClientset.clientCmdConfig.RawConfig() if err != nil { - if m.staticConfig.RequireOAuth { + if m.config.IsRequireOAuth() { klog.Errorf("failed to get kubeconfig: %v", err) return nil, fmt.Errorf("failed to get kubeconfig: %w", err) } return &Kubernetes{m.accessControlClientset}, nil } clientCmdApiConfig.AuthInfos = make(map[string]*clientcmdapi.AuthInfo) - derived, err := NewAccessControlClientset(m.staticConfig, clientcmd.NewDefaultClientConfig(clientCmdApiConfig, nil), derivedCfg) + derived, err := NewAccessControlClientset(m.config, clientcmd.NewDefaultClientConfig(clientCmdApiConfig, nil), derivedCfg) if err != nil { - if m.staticConfig.RequireOAuth { + if m.config.IsRequireOAuth() { klog.Errorf("failed to create derived clientset: %v", err) return nil, fmt.Errorf("failed to create derived clientset: %w", err) } diff --git a/pkg/kubernetes/provider.go b/pkg/kubernetes/provider.go index 4b38381d6..ee7cc2d56 100644 --- a/pkg/kubernetes/provider.go +++ b/pkg/kubernetes/provider.go @@ -3,7 +3,7 @@ package kubernetes import ( "context" - "github.com/containers/kubernetes-mcp-server/pkg/config" + configapi "github.com/containers/kubernetes-mcp-server/pkg/api/config" ) // McpReload is a function type that defines a callback for reloading MCP toolsets (including tools, prompts, or other configurations) @@ -26,7 +26,7 @@ type Provider interface { Close() } -func NewProvider(cfg *config.StaticConfig) (Provider, error) { +func NewProvider(cfg configapi.BaseConfig) (Provider, error) { strategy := resolveStrategy(cfg) factory, err := getProviderFactory(strategy) @@ -37,18 +37,18 @@ func NewProvider(cfg *config.StaticConfig) (Provider, error) { return factory(cfg) } -func resolveStrategy(cfg *config.StaticConfig) string { - if cfg.ClusterProviderStrategy != "" { - return cfg.ClusterProviderStrategy +func resolveStrategy(cfg configapi.BaseConfig) string { + if cfg.GetClusterProviderStrategy() != "" { + return cfg.GetClusterProviderStrategy() } - if cfg.KubeConfig != "" { - return config.ClusterProviderKubeConfig + if cfg.GetKubeConfigPath() != "" { + return configapi.ClusterProviderKubeConfig } if _, inClusterConfigErr := InClusterConfig(); inClusterConfigErr == nil { - return config.ClusterProviderInCluster + return configapi.ClusterProviderInCluster } - return config.ClusterProviderKubeConfig + return configapi.ClusterProviderKubeConfig } diff --git a/pkg/kubernetes/provider_kubeconfig.go b/pkg/kubernetes/provider_kubeconfig.go index 7f66a4af1..45332e352 100644 --- a/pkg/kubernetes/provider_kubeconfig.go +++ b/pkg/kubernetes/provider_kubeconfig.go @@ -6,7 +6,7 @@ import ( "fmt" "reflect" - "github.com/containers/kubernetes-mcp-server/pkg/config" + configapi "github.com/containers/kubernetes-mcp-server/pkg/api/config" "github.com/containers/kubernetes-mcp-server/pkg/kubernetes/watcher" authenticationv1api "k8s.io/api/authentication/v1" ) @@ -19,7 +19,7 @@ const KubeConfigTargetParameterName = "context" // Kubernetes clusters using different contexts from a kubeconfig file. // It lazily initializes managers for each context as they are requested. type kubeConfigClusterProvider struct { - staticConfig *config.StaticConfig + config configapi.BaseConfig defaultContext string managers map[string]*Manager kubeconfigWatcher *watcher.Kubeconfig @@ -29,15 +29,15 @@ type kubeConfigClusterProvider struct { var _ Provider = &kubeConfigClusterProvider{} func init() { - RegisterProvider(config.ClusterProviderKubeConfig, newKubeConfigClusterProvider) + RegisterProvider(configapi.ClusterProviderKubeConfig, newKubeConfigClusterProvider) } // newKubeConfigClusterProvider creates a provider that manages multiple clusters // via kubeconfig contexts. // Internally, it leverages a KubeconfigManager for each context, initializing them // lazily when requested. -func newKubeConfigClusterProvider(cfg *config.StaticConfig) (Provider, error) { - ret := &kubeConfigClusterProvider{staticConfig: cfg} +func newKubeConfigClusterProvider(cfg configapi.BaseConfig) (Provider, error) { + ret := &kubeConfigClusterProvider{config: cfg} if err := ret.reset(); err != nil { return nil, err } @@ -45,7 +45,7 @@ func newKubeConfigClusterProvider(cfg *config.StaticConfig) (Provider, error) { } func (p *kubeConfigClusterProvider) reset() error { - m, err := NewKubeconfigManager(p.staticConfig, "") + m, err := NewKubeconfigManager(p.config, "") if err != nil { if errors.Is(err, ErrorKubeconfigInClusterNotAllowed) { return fmt.Errorf("kubeconfig ClusterProviderStrategy is invalid for in-cluster deployments: %v", err) @@ -85,7 +85,7 @@ func (p *kubeConfigClusterProvider) managerForContext(context string) (*Manager, baseManager := p.managers[p.defaultContext] - m, err := NewKubeconfigManager(baseManager.staticConfig, context) + m, err := NewKubeconfigManager(baseManager.config, context) if err != nil { return nil, err } diff --git a/pkg/kubernetes/provider_registry.go b/pkg/kubernetes/provider_registry.go index b9077f15b..9dc84666f 100644 --- a/pkg/kubernetes/provider_registry.go +++ b/pkg/kubernetes/provider_registry.go @@ -4,13 +4,13 @@ import ( "fmt" "sort" - "github.com/containers/kubernetes-mcp-server/pkg/config" + configapi "github.com/containers/kubernetes-mcp-server/pkg/api/config" ) // ProviderFactory creates a new Provider instance for a given strategy. // Implementations should validate that the Manager is compatible with their strategy // (e.g., kubeconfig provider should reject in-cluster managers). -type ProviderFactory func(cfg *config.StaticConfig) (Provider, error) +type ProviderFactory func(cfg configapi.BaseConfig) (Provider, error) var providerFactories = make(map[string]ProviderFactory) diff --git a/pkg/kubernetes/provider_registry_test.go b/pkg/kubernetes/provider_registry_test.go index c94e1ec19..590a2d5cc 100644 --- a/pkg/kubernetes/provider_registry_test.go +++ b/pkg/kubernetes/provider_registry_test.go @@ -3,7 +3,7 @@ package kubernetes import ( "testing" - "github.com/containers/kubernetes-mcp-server/pkg/config" + configapi "github.com/containers/kubernetes-mcp-server/pkg/api/config" "github.com/stretchr/testify/suite" ) @@ -13,18 +13,18 @@ type ProviderRegistryTestSuite struct { func (s *ProviderRegistryTestSuite) TestRegisterProvider() { s.Run("With no pre-existing provider, registers the provider", func() { - RegisterProvider("test-strategy", func(cfg *config.StaticConfig) (Provider, error) { + RegisterProvider("test-strategy", func(cfg configapi.BaseConfig) (Provider, error) { return nil, nil }) _, exists := providerFactories["test-strategy"] s.True(exists, "Provider should be registered") }) s.Run("With pre-existing provider, panics", func() { - RegisterProvider("test-pre-existent", func(cfg *config.StaticConfig) (Provider, error) { + RegisterProvider("test-pre-existent", func(cfg configapi.BaseConfig) (Provider, error) { return nil, nil }) s.Panics(func() { - RegisterProvider("test-pre-existent", func(cfg *config.StaticConfig) (Provider, error) { + RegisterProvider("test-pre-existent", func(cfg configapi.BaseConfig) (Provider, error) { return nil, nil }) }, "Registering a provider with an existing strategy should panic") @@ -39,10 +39,10 @@ func (s *ProviderRegistryTestSuite) TestGetRegisteredStrategies() { }) s.Run("With multiple registered providers, returns sorted list", func() { providerFactories = make(map[string]ProviderFactory) - RegisterProvider("foo-strategy", func(cfg *config.StaticConfig) (Provider, error) { + RegisterProvider("foo-strategy", func(cfg configapi.BaseConfig) (Provider, error) { return nil, nil }) - RegisterProvider("bar-strategy", func(cfg *config.StaticConfig) (Provider, error) { + RegisterProvider("bar-strategy", func(cfg configapi.BaseConfig) (Provider, error) { return nil, nil }) strategies := GetRegisteredStrategies() diff --git a/pkg/kubernetes/provider_single.go b/pkg/kubernetes/provider_single.go index 0996e4874..dbdacbe0e 100644 --- a/pkg/kubernetes/provider_single.go +++ b/pkg/kubernetes/provider_single.go @@ -6,7 +6,7 @@ import ( "fmt" "reflect" - "github.com/containers/kubernetes-mcp-server/pkg/config" + configapi "github.com/containers/kubernetes-mcp-server/pkg/api/config" "github.com/containers/kubernetes-mcp-server/pkg/kubernetes/watcher" authenticationv1api "k8s.io/api/authentication/v1" ) @@ -15,7 +15,7 @@ import ( // Kubernetes cluster. Used for in-cluster deployments or when multi-cluster // support is disabled. type singleClusterProvider struct { - staticConfig *config.StaticConfig + config configapi.BaseConfig strategy string manager *Manager kubeconfigWatcher *watcher.Kubeconfig @@ -25,18 +25,18 @@ type singleClusterProvider struct { var _ Provider = &singleClusterProvider{} func init() { - RegisterProvider(config.ClusterProviderInCluster, newSingleClusterProvider(config.ClusterProviderInCluster)) - RegisterProvider(config.ClusterProviderDisabled, newSingleClusterProvider(config.ClusterProviderDisabled)) + RegisterProvider(configapi.ClusterProviderInCluster, newSingleClusterProvider(configapi.ClusterProviderInCluster)) + RegisterProvider(configapi.ClusterProviderDisabled, newSingleClusterProvider(configapi.ClusterProviderDisabled)) } // newSingleClusterProvider creates a provider that manages a single cluster. // When used within a cluster or with an 'in-cluster' strategy, it uses an InClusterManager. // Otherwise, it uses a KubeconfigManager. func newSingleClusterProvider(strategy string) ProviderFactory { - return func(cfg *config.StaticConfig) (Provider, error) { + return func(cfg configapi.BaseConfig) (Provider, error) { ret := &singleClusterProvider{ - staticConfig: cfg, - strategy: strategy, + config: cfg, + strategy: strategy, } if err := ret.reset(); err != nil { return nil, err @@ -46,16 +46,16 @@ func newSingleClusterProvider(strategy string) ProviderFactory { } func (p *singleClusterProvider) reset() error { - if p.staticConfig != nil && p.staticConfig.KubeConfig != "" && p.strategy == config.ClusterProviderInCluster { + if p.config != nil && p.config.GetKubeConfigPath() != "" && p.strategy == configapi.ClusterProviderInCluster { return fmt.Errorf("kubeconfig file %s cannot be used with the in-cluster ClusterProviderStrategy", - p.staticConfig.KubeConfig) + p.config.GetKubeConfigPath()) } var err error - if p.strategy == config.ClusterProviderInCluster || IsInCluster(p.staticConfig) { - p.manager, err = NewInClusterManager(p.staticConfig) + if p.strategy == configapi.ClusterProviderInCluster || IsInCluster(p.config) { + p.manager, err = NewInClusterManager(p.config) } else { - p.manager, err = NewKubeconfigManager(p.staticConfig, "") + p.manager, err = NewKubeconfigManager(p.config, "") } if err != nil { if errors.Is(err, ErrorInClusterNotInCluster) { diff --git a/pkg/kubernetes/provider_watch_test.go b/pkg/kubernetes/provider_watch_test.go index 42ad229d2..c57d3d497 100644 --- a/pkg/kubernetes/provider_watch_test.go +++ b/pkg/kubernetes/provider_watch_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/containers/kubernetes-mcp-server/internal/test" + configapi "github.com/containers/kubernetes-mcp-server/pkg/api/config" "github.com/containers/kubernetes-mcp-server/pkg/config" "github.com/stretchr/testify/suite" "k8s.io/client-go/tools/clientcmd" @@ -51,7 +52,7 @@ func (s *ProviderWatchTargetsTestSuite) TestClusterStateChanges() { testCases := []func() (Provider, error){ func() (Provider, error) { return newKubeConfigClusterProvider(s.staticConfig) }, func() (Provider, error) { - return newSingleClusterProvider(config.ClusterProviderDisabled)(s.staticConfig) + return newSingleClusterProvider(configapi.ClusterProviderDisabled)(s.staticConfig) }, } for _, tc := range testCases { @@ -116,7 +117,7 @@ func (s *ProviderWatchTargetsTestSuite) TestKubeConfigClusterProvider() { } func (s *ProviderWatchTargetsTestSuite) TestSingleClusterProvider() { - provider, err := newSingleClusterProvider(config.ClusterProviderDisabled)(s.staticConfig) + provider, err := newSingleClusterProvider(configapi.ClusterProviderDisabled)(s.staticConfig) s.Require().NoError(err, "Expected no error from provider creation") callback, waitForCallback := CallbackWaiter()