Skip to content

Commit 22ebe97

Browse files
committed
Kiali Options in a type and make kiali instance
Signed-off-by: Alberto Gutierrez <aljesusg@gmail.com>
1 parent d3c39c9 commit 22ebe97

File tree

13 files changed

+220
-267
lines changed

13 files changed

+220
-267
lines changed

pkg/api/toolsets.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"encoding/json"
66

7-
"github.com/containers/kubernetes-mcp-server/pkg/kiali"
87
internalk8s "github.com/containers/kubernetes-mcp-server/pkg/kubernetes"
98
"github.com/containers/kubernetes-mcp-server/pkg/output"
109
"github.com/google/jsonschema-go/jsonschema"
@@ -66,7 +65,6 @@ func NewToolCallResult(content string, err error) *ToolCallResult {
6665
type ToolHandlerParams struct {
6766
context.Context
6867
*internalk8s.Kubernetes
69-
*kiali.Kiali
7068
ToolCallRequest
7169
ListOutput output.Output
7270
}

pkg/config/config.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ const (
1616
ClusterProviderDisabled = "disabled"
1717
)
1818

19+
// KialiOptions is the configuration for the kiali toolset.
20+
type KialiOptions struct {
21+
Url string `toml:"url,omitempty"`
22+
Insecure bool `toml:"insecure,omitempty"`
23+
}
24+
1925
// StaticConfig is the configuration for the server.
2026
// It allows to configure server specific settings and tools to be enabled or disabled.
2127
type StaticConfig struct {
@@ -68,10 +74,8 @@ type StaticConfig struct {
6874
// This map holds raw TOML primitives that will be parsed by registered provider parsers
6975
ClusterProviderConfigs map[string]toml.Primitive `toml:"cluster_provider_configs,omitempty"`
7076

71-
// KialiServerURL is the URL of the Kiali server.
72-
KialiURL string `toml:"kiali_url,omitempty"`
73-
// KialiInsecure indicates whether the server should use insecure TLS for the Kiali server.
74-
KialiInsecure bool `toml:"kiali_insecure,omitempty"`
77+
// KialiOptions is the configuration for the kiali toolset.
78+
KialiOptions KialiOptions `toml:"kiali,omitempty"`
7579

7680
// Internal: parsed provider configs (not exposed to TOML package)
7781
parsedClusterProviderConfigs map[string]ProviderConfig

pkg/kiali/kiali.go

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@ import (
55
"crypto/tls"
66
"fmt"
77
"io"
8+
"k8s.io/klog/v2"
89
"net/http"
910
"net/url"
1011
"strings"
11-
12-
internalk8s "github.com/containers/kubernetes-mcp-server/pkg/kubernetes"
13-
"k8s.io/klog/v2"
1412
)
1513

1614
type Kiali struct {
@@ -63,21 +61,14 @@ func (k *Kiali) createHTTPClient() *http.Client {
6361
// CurrentAuthorizationHeader returns the Authorization header value that the
6462
// Kiali client is currently configured to use (Bearer <token>), or empty
6563
// if no bearer token is configured.
66-
func (k *Kiali) CurrentAuthorizationHeader(ctx context.Context) string {
67-
token, _ := ctx.Value(internalk8s.OAuthAuthorizationHeader).(string)
68-
token = strings.TrimSpace(token)
69-
64+
func (k *Kiali) authorizationHeader() string {
65+
if k == nil || k.manager == nil {
66+
return ""
67+
}
68+
token := strings.TrimSpace(k.manager.BearerToken)
7069
if token == "" {
71-
// Fall back to using the same token that the Kubernetes client is using
72-
if k == nil || k.manager == nil || k.manager.BearerToken == "" {
73-
return ""
74-
}
75-
token = strings.TrimSpace(k.manager.BearerToken)
76-
if token == "" {
77-
return ""
78-
}
70+
return ""
7971
}
80-
// Normalize to exactly "Bearer <token>" without double prefix
8172
lower := strings.ToLower(token)
8273
if strings.HasPrefix(lower, "bearer ") {
8374
return "Bearer " + strings.TrimSpace(token[7:])
@@ -97,7 +88,7 @@ func (k *Kiali) executeRequest(ctx context.Context, endpoint string) (string, er
9788
if err != nil {
9889
return "", err
9990
}
100-
authHeader := k.CurrentAuthorizationHeader(ctx)
91+
authHeader := k.authorizationHeader()
10192
if authHeader != "" {
10293
req.Header.Set("Authorization", authHeader)
10394
}

pkg/kiali/kiali_test.go

Lines changed: 59 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,75 @@
11
package kiali
22

33
import (
4-
"context"
5-
"net/http"
6-
"net/http/httptest"
7-
"net/url"
8-
"testing"
4+
"context"
5+
"net/http"
6+
"net/http/httptest"
7+
"net/url"
8+
"testing"
99

10-
"github.com/containers/kubernetes-mcp-server/pkg/config"
11-
internalk8s "github.com/containers/kubernetes-mcp-server/pkg/kubernetes"
10+
"github.com/containers/kubernetes-mcp-server/pkg/config"
1211
)
1312

1413
func TestValidateAndGetURL_JoinsProperly(t *testing.T) {
15-
m := NewManager(&config.StaticConfig{KialiURL: "https://kiali.example/"})
16-
k := m.GetKiali()
14+
m := NewManager(&config.StaticConfig{KialiOptions: config.KialiOptions{Url: "https://kiali.example/"}})
15+
k := m.GetKiali()
1716

18-
full, err := k.validateAndGetURL("/api/path")
19-
if err != nil {
20-
t.Fatalf("unexpected error: %v", err)
21-
}
22-
if full != "https://kiali.example/api/path" {
23-
t.Fatalf("unexpected url: %s", full)
24-
}
17+
full, err := k.validateAndGetURL("/api/path")
18+
if err != nil {
19+
t.Fatalf("unexpected error: %v", err)
20+
}
21+
if full != "https://kiali.example/api/path" {
22+
t.Fatalf("unexpected url: %s", full)
23+
}
2524

26-
m.KialiURL = "https://kiali.example"
27-
full, err = k.validateAndGetURL("api/path")
28-
if err != nil {
29-
t.Fatalf("unexpected error: %v", err)
30-
}
31-
if full != "https://kiali.example/api/path" {
32-
t.Fatalf("unexpected url: %s", full)
33-
}
25+
m.KialiURL = "https://kiali.example"
26+
full, err = k.validateAndGetURL("api/path")
27+
if err != nil {
28+
t.Fatalf("unexpected error: %v", err)
29+
}
30+
if full != "https://kiali.example/api/path" {
31+
t.Fatalf("unexpected url: %s", full)
32+
}
3433

35-
// preserve query
36-
m.KialiURL = "https://kiali.example"
37-
full, err = k.validateAndGetURL("/api/path?x=1&y=2")
38-
if err != nil {
39-
t.Fatalf("unexpected error: %v", err)
40-
}
41-
u, _ := url.Parse(full)
42-
if u.Path != "/api/path" || u.Query().Get("x") != "1" || u.Query().Get("y") != "2" {
43-
t.Fatalf("unexpected parsed url: %s", full)
44-
}
34+
// preserve query
35+
m.KialiURL = "https://kiali.example"
36+
full, err = k.validateAndGetURL("/api/path?x=1&y=2")
37+
if err != nil {
38+
t.Fatalf("unexpected error: %v", err)
39+
}
40+
u, _ := url.Parse(full)
41+
if u.Path != "/api/path" || u.Query().Get("x") != "1" || u.Query().Get("y") != "2" {
42+
t.Fatalf("unexpected parsed url: %s", full)
43+
}
4544
}
4645

47-
func TestCurrentAuthorizationHeader_FromContext(t *testing.T) {
48-
m := NewManager(&config.StaticConfig{KialiURL: "https://kiali.example"})
49-
k := m.GetKiali()
50-
ctx := context.WithValue(context.Background(), internalk8s.OAuthAuthorizationHeader, "bearer abc")
51-
got := k.CurrentAuthorizationHeader(ctx)
52-
if got != "Bearer abc" {
53-
t.Fatalf("expected normalized bearer header, got '%s'", got)
54-
}
55-
}
56-
57-
func TestCurrentAuthorizationHeader_FromManagerToken(t *testing.T) {
58-
m := NewManager(&config.StaticConfig{KialiURL: "https://kiali.example"})
59-
m.BearerToken = "abc"
60-
k := m.GetKiali()
61-
got := k.CurrentAuthorizationHeader(context.Background())
62-
if got != "Bearer abc" {
63-
t.Fatalf("expected 'Bearer abc', got '%s'", got)
64-
}
65-
}
46+
// CurrentAuthorizationHeader behavior is now implicit via executeRequest using Manager.BearerToken
6647

6748
func TestExecuteRequest_SetsAuthAndCallsServer(t *testing.T) {
68-
// setup test server to assert path and auth header
69-
var seenAuth string
70-
var seenPath string
71-
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
72-
seenAuth = r.Header.Get("Authorization")
73-
seenPath = r.URL.String()
74-
_, _ = w.Write([]byte("ok"))
75-
}))
76-
defer srv.Close()
49+
// setup test server to assert path and auth header
50+
var seenAuth string
51+
var seenPath string
52+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
53+
seenAuth = r.Header.Get("Authorization")
54+
seenPath = r.URL.String()
55+
_, _ = w.Write([]byte("ok"))
56+
}))
57+
defer srv.Close()
7758

78-
m := NewManager(&config.StaticConfig{KialiURL: srv.URL})
79-
k := m.GetKiali()
80-
ctx := context.WithValue(context.Background(), internalk8s.OAuthAuthorizationHeader, "Bearer token-xyz")
81-
82-
out, err := k.executeRequest(ctx, "/api/ping?q=1")
83-
if err != nil {
84-
t.Fatalf("unexpected error: %v", err)
85-
}
86-
if out != "ok" {
87-
t.Fatalf("unexpected body: %s", out)
88-
}
89-
if seenAuth != "Bearer token-xyz" {
90-
t.Fatalf("expected auth header to be set, got '%s'", seenAuth)
91-
}
92-
if seenPath != "/api/ping?q=1" {
93-
t.Fatalf("unexpected path: %s", seenPath)
94-
}
59+
m := NewManager(&config.StaticConfig{KialiOptions: config.KialiOptions{Url: srv.URL}})
60+
m.BearerToken = "token-xyz"
61+
k := m.GetKiali()
62+
out, err := k.executeRequest(context.Background(), "/api/ping?q=1")
63+
if err != nil {
64+
t.Fatalf("unexpected error: %v", err)
65+
}
66+
if out != "ok" {
67+
t.Fatalf("unexpected body: %s", out)
68+
}
69+
if seenAuth != "Bearer token-xyz" {
70+
t.Fatalf("expected auth header to be set, got '%s'", seenAuth)
71+
}
72+
if seenPath != "/api/ping?q=1" {
73+
t.Fatalf("unexpected path: %s", seenPath)
74+
}
9575
}
96-
97-

pkg/kiali/manager.go

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@ package kiali
22

33
import (
44
"context"
5-
"strings"
65

76
"github.com/containers/kubernetes-mcp-server/pkg/config"
8-
internalk8s "github.com/containers/kubernetes-mcp-server/pkg/kubernetes"
9-
"k8s.io/klog/v2"
107
)
118

129
type Manager struct {
@@ -18,22 +15,11 @@ type Manager struct {
1815
func NewManager(config *config.StaticConfig) *Manager {
1916
return &Manager{
2017
BearerToken: "",
21-
KialiURL: config.KialiURL,
22-
KialiInsecure: config.KialiInsecure,
18+
KialiURL: config.KialiOptions.Url,
19+
KialiInsecure: config.KialiOptions.Insecure,
2320
}
2421
}
2522

26-
func (m *Manager) Derived(ctx context.Context) (*Kiali, error) {
27-
authorization, ok := ctx.Value(internalk8s.OAuthAuthorizationHeader).(string)
28-
if !ok || !strings.HasPrefix(authorization, "Bearer ") {
29-
return &Kiali{manager: m}, nil
30-
}
31-
// Authorization header is present; nothing special is needed for the Kiali HTTP client
32-
klog.V(5).Infof("%s header found (Bearer), using provided bearer token", internalk8s.OAuthAuthorizationHeader)
33-
34-
return &Kiali{manager: &Manager{
35-
BearerToken: strings.TrimPrefix(authorization, "Bearer "),
36-
KialiURL: m.KialiURL,
37-
KialiInsecure: m.KialiInsecure,
38-
}}, nil
23+
func (m *Manager) Derived(_ context.Context) (*Kiali, error) {
24+
return &Kiali{manager: m}, nil
3925
}

pkg/kiali/manager_test.go

Lines changed: 40 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,53 @@
11
package kiali
22

33
import (
4-
"context"
5-
"testing"
4+
"context"
5+
"testing"
66

7-
"github.com/containers/kubernetes-mcp-server/pkg/config"
8-
internalk8s "github.com/containers/kubernetes-mcp-server/pkg/kubernetes"
7+
"github.com/containers/kubernetes-mcp-server/pkg/config"
98
)
109

1110
func TestNewManagerUsesConfigFields(t *testing.T) {
12-
cfg := &config.StaticConfig{KialiURL: "https://kiali.example", KialiInsecure: true}
13-
m := NewManager(cfg)
14-
if m == nil {
15-
t.Fatalf("expected manager, got nil")
16-
}
17-
if m.KialiURL != cfg.KialiURL {
18-
t.Fatalf("expected KialiURL %s, got %s", cfg.KialiURL, m.KialiURL)
19-
}
20-
if m.KialiInsecure != cfg.KialiInsecure {
21-
t.Fatalf("expected KialiInsecure %v, got %v", cfg.KialiInsecure, m.KialiInsecure)
22-
}
11+
cfg := &config.StaticConfig{KialiOptions: config.KialiOptions{Url: "https://kiali.example", Insecure: true}}
12+
m := NewManager(cfg)
13+
if m == nil {
14+
t.Fatalf("expected manager, got nil")
15+
}
16+
if m.KialiURL != cfg.KialiOptions.Url {
17+
t.Fatalf("expected KialiURL %s, got %s", cfg.KialiOptions.Url, m.KialiURL)
18+
}
19+
if m.KialiInsecure != cfg.KialiOptions.Insecure {
20+
t.Fatalf("expected KialiInsecure %v, got %v", cfg.KialiOptions.Insecure, m.KialiInsecure)
21+
}
2322
}
2423

2524
func TestDerivedWithoutAuthorizationReturnsOriginalManager(t *testing.T) {
26-
cfg := &config.StaticConfig{KialiURL: "https://kiali.example"}
27-
m := NewManager(cfg)
28-
k, err := m.Derived(context.Background())
29-
if err != nil {
30-
t.Fatalf("unexpected error: %v", err)
31-
}
32-
if k == nil || k.manager != m {
33-
t.Fatalf("expected derived Kiali to keep original manager")
34-
}
25+
cfg := &config.StaticConfig{KialiOptions: config.KialiOptions{Url: "https://kiali.example"}}
26+
m := NewManager(cfg)
27+
k, err := m.Derived(context.Background())
28+
if err != nil {
29+
t.Fatalf("unexpected error: %v", err)
30+
}
31+
if k == nil || k.manager != m {
32+
t.Fatalf("expected derived Kiali to keep original manager")
33+
}
3534
}
3635

37-
func TestDerivedWithAuthorizationPreservesURLAndToken(t *testing.T) {
38-
cfg := &config.StaticConfig{KialiURL: "https://kiali.example", KialiInsecure: true}
39-
m := NewManager(cfg)
40-
ctx := context.WithValue(context.Background(), internalk8s.OAuthAuthorizationHeader, "Bearer token-abc")
41-
k, err := m.Derived(ctx)
42-
if err != nil {
43-
t.Fatalf("unexpected error: %v", err)
44-
}
45-
if k == nil || k.manager == nil {
46-
t.Fatalf("expected derived Kiali with manager")
47-
}
48-
if k.manager.BearerToken != "token-abc" {
49-
t.Fatalf("expected bearer token 'token-abc', got '%s'", k.manager.BearerToken)
50-
}
51-
if k.manager.KialiURL != m.KialiURL || k.manager.KialiInsecure != m.KialiInsecure {
52-
t.Fatalf("expected Kiali URL/insecure preserved")
53-
}
36+
func TestDerivedPreservesURLAndToken(t *testing.T) {
37+
cfg := &config.StaticConfig{KialiOptions: config.KialiOptions{Url: "https://kiali.example", Insecure: true}}
38+
m := NewManager(cfg)
39+
m.BearerToken = "token-abc"
40+
k, err := m.Derived(context.Background())
41+
if err != nil {
42+
t.Fatalf("unexpected error: %v", err)
43+
}
44+
if k == nil || k.manager == nil {
45+
t.Fatalf("expected derived Kiali with manager")
46+
}
47+
if k.manager.BearerToken != "token-abc" {
48+
t.Fatalf("expected bearer token 'token-abc', got '%s'", k.manager.BearerToken)
49+
}
50+
if k.manager.KialiURL != m.KialiURL || k.manager.KialiInsecure != m.KialiInsecure {
51+
t.Fatalf("expected Kiali URL/insecure preserved")
52+
}
5453
}
55-
56-

0 commit comments

Comments
 (0)