feat: configuration minification is optional

This commit is contained in:
Marc Nuri
2025-03-27 06:50:14 +01:00
parent fe62e31626
commit 50277ce954
4 changed files with 82 additions and 19 deletions

View File

@@ -5,7 +5,7 @@ import (
"k8s.io/client-go/tools/clientcmd/api/latest"
)
func ConfigurationView() (string, error) {
func ConfigurationView(minify bool) (string, error) {
var cfg clientcmdapi.Config
var err error
inClusterConfig, err := InClusterConfig()
@@ -26,8 +26,10 @@ func ConfigurationView() (string, error) {
} else if cfg, err = resolveConfig().RawConfig(); err != nil {
return "", err
}
if err = clientcmdapi.MinifyConfig(&cfg); err != nil {
return "", err
if minify {
if err = clientcmdapi.MinifyConfig(&cfg); err != nil {
return "", err
}
}
if err = clientcmdapi.FlattenConfig(&cfg); err != nil {
return "", err

View File

@@ -144,19 +144,24 @@ func testCase(t *testing.T, test func(c *mcpContext)) {
// withKubeConfig sets up a fake kubeconfig in the temp directory based on the provided rest.Config
func (c *mcpContext) withKubeConfig(rc *rest.Config) *api.Config {
fakeConfig := api.NewConfig()
fakeConfig.CurrentContext = "fake-context"
fakeConfig.Contexts["fake-context"] = api.NewContext()
fakeConfig.Contexts["fake-context"].Cluster = "fake"
fakeConfig.Contexts["fake-context"].AuthInfo = "fake"
fakeConfig.Clusters["fake"] = api.NewCluster()
fakeConfig.Clusters["fake"].Server = "https://example.com"
fakeConfig.Clusters["additional-cluster"] = api.NewCluster()
fakeConfig.AuthInfos["fake"] = api.NewAuthInfo()
fakeConfig.AuthInfos["additional-auth"] = api.NewAuthInfo()
if rc != nil {
fakeConfig.Clusters["fake"].Server = rc.Host
fakeConfig.Clusters["fake"].CertificateAuthorityData = rc.TLSClientConfig.CAData
fakeConfig.AuthInfos["fake"].ClientKeyData = rc.TLSClientConfig.KeyData
fakeConfig.AuthInfos["fake"].ClientCertificateData = rc.TLSClientConfig.CertData
}
fakeConfig.Contexts["fake-context"] = api.NewContext()
fakeConfig.Contexts["fake-context"].Cluster = "fake"
fakeConfig.Contexts["fake-context"].AuthInfo = "fake"
fakeConfig.Contexts["additional-context"] = api.NewContext()
fakeConfig.Contexts["additional-context"].Cluster = "additional-cluster"
fakeConfig.Contexts["additional-context"].AuthInfo = "additional-auth"
fakeConfig.CurrentContext = "fake-context"
kubeConfig := filepath.Join(c.tempDir, "config")
_ = clientcmd.WriteToFile(*fakeConfig, kubeConfig)
_ = os.Setenv("KUBECONFIG", kubeConfig)

View File

@@ -12,12 +12,21 @@ func (s *Server) initConfiguration() []server.ServerTool {
return []server.ServerTool{
{mcp.NewTool("configuration_view",
mcp.WithDescription("Get the current Kubernetes configuration content as a kubeconfig YAML"),
mcp.WithBoolean("minified", mcp.Description("Return a minified version of the configuration. "+
"If set to true, keeps only the current-context and the relevant pieces of the configuration for that context. "+
"If set to false, all contexts, clusters, auth-infos, and users are returned in the configuration. "+
"(Optional, default true)")),
), configurationView},
}
}
func configurationView(_ context.Context, _ mcp.CallToolRequest) (*mcp.CallToolResult, error) {
ret, err := kubernetes.ConfigurationView()
func configurationView(_ context.Context, ctr mcp.CallToolRequest) (*mcp.CallToolResult, error) {
minify := true
minified := ctr.Params.Arguments["minified"]
if _, ok := minified.(bool); ok {
minify = minified.(bool)
}
ret, err := kubernetes.ConfigurationView(minify)
if err != nil {
err = fmt.Errorf("failed to get configuration: %v", err)
}

View File

@@ -26,40 +26,87 @@ func TestConfigurationView(t *testing.T) {
})
t.Run("configuration_view returns current-context", func(t *testing.T) {
if decoded.CurrentContext != "fake-context" {
t.Fatalf("fake-context not found: %v", decoded.CurrentContext)
t.Errorf("fake-context not found: %v", decoded.CurrentContext)
}
})
t.Run("configuration_view returns context info", func(t *testing.T) {
if len(decoded.Contexts) != 1 {
t.Fatalf("invalid context count, expected 1, got %v", len(decoded.Contexts))
t.Errorf("invalid context count, expected 1, got %v", len(decoded.Contexts))
}
if decoded.Contexts[0].Name != "fake-context" {
t.Fatalf("fake-context not found: %v", decoded.Contexts)
t.Errorf("fake-context not found: %v", decoded.Contexts)
}
if decoded.Contexts[0].Context.Cluster != "fake" {
t.Fatalf("fake-cluster not found: %v", decoded.Contexts)
t.Errorf("fake-cluster not found: %v", decoded.Contexts)
}
if decoded.Contexts[0].Context.AuthInfo != "fake" {
t.Fatalf("fake-auth not found: %v", decoded.Contexts)
t.Errorf("fake-auth not found: %v", decoded.Contexts)
}
})
t.Run("configuration_view returns cluster info", func(t *testing.T) {
if len(decoded.Clusters) != 1 {
t.Fatalf("invalid cluster count, expected 1, got %v", len(decoded.Clusters))
t.Errorf("invalid cluster count, expected 1, got %v", len(decoded.Clusters))
}
if decoded.Clusters[0].Name != "fake" {
t.Fatalf("fake-cluster not found: %v", decoded.Clusters)
t.Errorf("fake-cluster not found: %v", decoded.Clusters)
}
if decoded.Clusters[0].Cluster.Server != "https://example.com" {
t.Fatalf("fake-server not found: %v", decoded.Clusters)
t.Errorf("fake-server not found: %v", decoded.Clusters)
}
})
t.Run("configuration_view returns auth info", func(t *testing.T) {
if len(decoded.AuthInfos) != 1 {
t.Fatalf("invalid auth info count, expected 1, got %v", len(decoded.AuthInfos))
t.Errorf("invalid auth info count, expected 1, got %v", len(decoded.AuthInfos))
}
if decoded.AuthInfos[0].Name != "fake" {
t.Fatalf("fake-auth not found: %v", decoded.AuthInfos)
t.Errorf("fake-auth not found: %v", decoded.AuthInfos)
}
})
toolResult, err = c.callTool("configuration_view", map[string]interface{}{
"minified": false,
})
t.Run("configuration_view with minified=false returns configuration", func(t *testing.T) {
if err != nil {
t.Fatalf("call tool failed %v", err)
}
})
err = yaml.Unmarshal([]byte(toolResult.Content[0].(mcp.TextContent).Text), &decoded)
t.Run("configuration_view with minified=false has yaml content", func(t *testing.T) {
if err != nil {
t.Fatalf("invalid tool result content %v", err)
}
})
t.Run("configuration_view with minified=false returns additional context info", func(t *testing.T) {
if len(decoded.Contexts) != 2 {
t.Errorf("invalid context count, expected2, got %v", len(decoded.Contexts))
}
if decoded.Contexts[0].Name != "additional-context" {
t.Errorf("additional-context not found: %v", decoded.Contexts)
}
if decoded.Contexts[0].Context.Cluster != "additional-cluster" {
t.Errorf("additional-cluster not found: %v", decoded.Contexts)
}
if decoded.Contexts[0].Context.AuthInfo != "additional-auth" {
t.Errorf("additional-auth not found: %v", decoded.Contexts)
}
if decoded.Contexts[1].Name != "fake-context" {
t.Errorf("fake-context not found: %v", decoded.Contexts)
}
})
t.Run("configuration_view with minified=false returns cluster info", func(t *testing.T) {
if len(decoded.Clusters) != 2 {
t.Errorf("invalid cluster count, expected 2, got %v", len(decoded.Clusters))
}
if decoded.Clusters[0].Name != "additional-cluster" {
t.Errorf("additional-cluster not found: %v", decoded.Clusters)
}
})
t.Run("configuration_view with minified=false returns auth info", func(t *testing.T) {
if len(decoded.AuthInfos) != 2 {
t.Errorf("invalid auth info count, expected 2, got %v", len(decoded.AuthInfos))
}
if decoded.AuthInfos[0].Name != "additional-auth" {
t.Errorf("additional-auth not found: %v", decoded.AuthInfos)
}
})
})