Skip to content

Commit 3fbfd8d

Browse files
authored
fix(lint): add golangci-lint make target + lint
Signed-off-by: Marc Nuri <[email protected]>
1 parent a3e8818 commit 3fbfd8d

18 files changed

+91
-85
lines changed

Makefile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ LD_FLAGS = -s -w \
1515
-X '$(PACKAGE)/pkg/version.BinaryName=$(BINARY_NAME)'
1616
COMMON_BUILD_ARGS = -ldflags "$(LD_FLAGS)"
1717

18+
GOLANGCI_LINT = $(shell pwd)/_output/tools/bin/golangci-lint
19+
GOLANGCI_LINT_VERSION ?= v2.2.2
20+
1821
# NPM version should not append the -dirty flag
1922
NPM_VERSION ?= $(shell echo $(shell git describe --tags --always) | sed 's/^v//')
2023
OSES = darwin linux windows
@@ -97,3 +100,14 @@ format: ## Format the code
97100
.PHONY: tidy
98101
tidy: ## Tidy up the go modules
99102
go mod tidy
103+
104+
.PHONY: golangci-lint
105+
golangci-lint: ## Download and install golangci-lint if not already installed
106+
@[ -f $(GOLANGCI_LINT) ] || { \
107+
set -e ;\
108+
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell dirname $(GOLANGCI_LINT)) $(GOLANGCI_LINT_VERSION) ;\
109+
}
110+
111+
.PHONY: lint
112+
lint: golangci-lint ## Lint the code
113+
$(GOLANGCI_LINT) run --verbose --print-resources-usage

pkg/helm/helm.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,14 @@ func (h *Helm) Install(ctx context.Context, chart string, values map[string]inte
4545
install.Timeout = 5 * time.Minute
4646
install.DryRun = false
4747

48-
chartRequested, err := install.ChartPathOptions.LocateChart(chart, cli.New())
48+
chartRequested, err := install.LocateChart(chart, cli.New())
4949
if err != nil {
5050
return "", err
5151
}
5252
chartLoaded, err := loader.Load(chartRequested)
53+
if err != nil {
54+
return "", err
55+
}
5356

5457
installedRelease, err := install.RunWithContext(ctx, chartLoaded, values)
5558
if err != nil {

pkg/kubernetes-mcp-server/cmd/root.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,11 @@ func NewMCPServer(streams genericiooptions.IOStreams) *cobra.Command {
113113
cmd.Flags().BoolVar(&o.ReadOnly, "read-only", o.ReadOnly, "If true, only tools annotated with readOnlyHint=true are exposed")
114114
cmd.Flags().BoolVar(&o.DisableDestructive, "disable-destructive", o.DisableDestructive, "If true, tools annotated with destructiveHint=true are disabled")
115115
cmd.Flags().BoolVar(&o.RequireOAuth, "require-oauth", o.RequireOAuth, "If true, requires OAuth authorization as defined in the Model Context Protocol (MCP) specification. This flag is ignored if transport type is stdio")
116-
cmd.Flags().MarkHidden("require-oauth")
116+
_ = cmd.Flags().MarkHidden("require-oauth")
117117
cmd.Flags().StringVar(&o.AuthorizationURL, "authorization-url", o.AuthorizationURL, "OAuth authorization server URL for protected resource endpoint. If not provided, the Kubernetes API server host will be used. Only valid if require-oauth is enabled.")
118-
cmd.Flags().MarkHidden("authorization-url")
118+
_ = cmd.Flags().MarkHidden("authorization-url")
119119
cmd.Flags().StringVar(&o.ServerURL, "server-url", o.ServerURL, "Server URL of this application. Optional. If set, this url will be served in protected resource metadata endpoint and tokens will be validated with this audience. If not set, expected audience is kubernetes-mcp-server. Only valid if require-oauth is enabled.")
120-
cmd.Flags().MarkHidden("server-url")
120+
_ = cmd.Flags().MarkHidden("server-url")
121121
return cmd
122122
}
123123

@@ -228,11 +228,11 @@ func (m *MCPServerOptions) Validate() error {
228228
func (m *MCPServerOptions) Run() error {
229229
profile := mcp.ProfileFromString(m.Profile)
230230
if profile == nil {
231-
return fmt.Errorf("Invalid profile name: %s, valid names are: %s\n", m.Profile, strings.Join(mcp.ProfileNames, ", "))
231+
return fmt.Errorf("invalid profile name: %s, valid names are: %s", m.Profile, strings.Join(mcp.ProfileNames, ", "))
232232
}
233233
listOutput := output.FromString(m.StaticConfig.ListOutput)
234234
if listOutput == nil {
235-
return fmt.Errorf("Invalid output name: %s, valid names are: %s\n", m.StaticConfig.ListOutput, strings.Join(output.Names, ", "))
235+
return fmt.Errorf("invalid output name: %s, valid names are: %s", m.StaticConfig.ListOutput, strings.Join(output.Names, ", "))
236236
}
237237
klog.V(1).Info("Starting kubernetes-mcp-server")
238238
klog.V(1).Infof(" - Config: %s", m.ConfigPath)
@@ -261,7 +261,7 @@ func (m *MCPServerOptions) Run() error {
261261
StaticConfig: m.StaticConfig,
262262
})
263263
if err != nil {
264-
return fmt.Errorf("Failed to initialize MCP server: %w\n", err)
264+
return fmt.Errorf("failed to initialize MCP server: %w", err)
265265
}
266266
defer mcpServer.Close()
267267

pkg/kubernetes/configuration.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ func (m *Manager) ConfigurationView(minify bool) (runtime.Object, error) {
106106
return nil, err
107107
}
108108
}
109+
//nolint:staticcheck
109110
if err = clientcmdapi.FlattenConfig(&cfg); err != nil {
110111
// ignore error
111112
//return "", err

pkg/kubernetes/impersonate_roundtripper.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ package kubernetes
22

33
import "net/http"
44

5+
// nolint:unused
56
type impersonateRoundTripper struct {
67
delegate http.RoundTripper
78
}
89

10+
// nolint:unused
911
func (irt *impersonateRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
1012
// TODO: Solution won't work with discoveryclient which uses context.TODO() instead of the passed-in context
1113
if v, ok := req.Context().Value(OAuthAuthorizationHeader).(string); ok {

pkg/kubernetes/kubernetes.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ import (
2626
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
2727
)
2828

29+
type HeaderKey string
30+
2931
const (
30-
CustomAuthorizationHeader = "kubernetes-authorization"
31-
OAuthAuthorizationHeader = "Authorization"
32+
CustomAuthorizationHeader = HeaderKey("kubernetes-authorization")
33+
OAuthAuthorizationHeader = HeaderKey("Authorization")
3234

3335
CustomUserAgent = "kubernetes-mcp-server/bearer-token-auth"
3436
)
@@ -155,10 +157,10 @@ func (m *Manager) Derived(ctx context.Context) (*Kubernetes, error) {
155157
APIPath: m.cfg.APIPath,
156158
// Copy only server verification TLS settings (CA bundle and server name)
157159
TLSClientConfig: rest.TLSClientConfig{
158-
Insecure: m.cfg.TLSClientConfig.Insecure,
159-
ServerName: m.cfg.TLSClientConfig.ServerName,
160-
CAFile: m.cfg.TLSClientConfig.CAFile,
161-
CAData: m.cfg.TLSClientConfig.CAData,
160+
Insecure: m.cfg.Insecure,
161+
ServerName: m.cfg.ServerName,
162+
CAFile: m.cfg.CAFile,
163+
CAData: m.cfg.CAData,
162164
},
163165
BearerToken: strings.TrimPrefix(authorization, "Bearer "),
164166
// pass custom UserAgent to identify the client

pkg/kubernetes/kubernetes_test.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -137,17 +137,17 @@ users:
137137
t.Errorf("expected Timeout %v, got %v", originalCfg.Timeout, derivedCfg.Timeout)
138138
}
139139

140-
if derivedCfg.TLSClientConfig.Insecure != originalCfg.TLSClientConfig.Insecure {
141-
t.Errorf("expected TLS Insecure %v, got %v", originalCfg.TLSClientConfig.Insecure, derivedCfg.TLSClientConfig.Insecure)
140+
if derivedCfg.Insecure != originalCfg.Insecure {
141+
t.Errorf("expected TLS Insecure %v, got %v", originalCfg.Insecure, derivedCfg.Insecure)
142142
}
143-
if derivedCfg.TLSClientConfig.ServerName != originalCfg.TLSClientConfig.ServerName {
144-
t.Errorf("expected TLS ServerName %s, got %s", originalCfg.TLSClientConfig.ServerName, derivedCfg.TLSClientConfig.ServerName)
143+
if derivedCfg.ServerName != originalCfg.ServerName {
144+
t.Errorf("expected TLS ServerName %s, got %s", originalCfg.ServerName, derivedCfg.ServerName)
145145
}
146-
if derivedCfg.TLSClientConfig.CAFile != originalCfg.TLSClientConfig.CAFile {
147-
t.Errorf("expected TLS CAFile %s, got %s", originalCfg.TLSClientConfig.CAFile, derivedCfg.TLSClientConfig.CAFile)
146+
if derivedCfg.CAFile != originalCfg.CAFile {
147+
t.Errorf("expected TLS CAFile %s, got %s", originalCfg.CAFile, derivedCfg.CAFile)
148148
}
149-
if string(derivedCfg.TLSClientConfig.CAData) != string(originalCfg.TLSClientConfig.CAData) {
150-
t.Errorf("expected TLS CAData %s, got %s", string(originalCfg.TLSClientConfig.CAData), string(derivedCfg.TLSClientConfig.CAData))
149+
if string(derivedCfg.CAData) != string(originalCfg.CAData) {
150+
t.Errorf("expected TLS CAData %s, got %s", string(originalCfg.CAData), string(derivedCfg.CAData))
151151
}
152152

153153
if derivedCfg.BearerToken != testBearerToken {
@@ -160,17 +160,17 @@ users:
160160
// Verify that sensitive fields are NOT copied to prevent credential leakage
161161
// The derived config should only use the bearer token from the Authorization header
162162
// and not inherit any authentication credentials from the original kubeconfig
163-
if derivedCfg.TLSClientConfig.CertFile != "" {
164-
t.Errorf("expected TLS CertFile to be empty, got %s", derivedCfg.TLSClientConfig.CertFile)
163+
if derivedCfg.CertFile != "" {
164+
t.Errorf("expected TLS CertFile to be empty, got %s", derivedCfg.CertFile)
165165
}
166-
if derivedCfg.TLSClientConfig.KeyFile != "" {
167-
t.Errorf("expected TLS KeyFile to be empty, got %s", derivedCfg.TLSClientConfig.KeyFile)
166+
if derivedCfg.KeyFile != "" {
167+
t.Errorf("expected TLS KeyFile to be empty, got %s", derivedCfg.KeyFile)
168168
}
169-
if len(derivedCfg.TLSClientConfig.CertData) != 0 {
170-
t.Errorf("expected TLS CertData to be empty, got %v", derivedCfg.TLSClientConfig.CertData)
169+
if len(derivedCfg.CertData) != 0 {
170+
t.Errorf("expected TLS CertData to be empty, got %v", derivedCfg.CertData)
171171
}
172-
if len(derivedCfg.TLSClientConfig.KeyData) != 0 {
173-
t.Errorf("expected TLS KeyData to be empty, got %v", derivedCfg.TLSClientConfig.KeyData)
172+
if len(derivedCfg.KeyData) != 0 {
173+
t.Errorf("expected TLS KeyData to be empty, got %v", derivedCfg.KeyData)
174174
}
175175

176176
if derivedCfg.Username != "" {

pkg/mcp/common_test.go

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,9 @@ import (
2525
apiextensionsv1spec "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
2626
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1"
2727
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28-
"k8s.io/apimachinery/pkg/runtime/schema"
29-
"k8s.io/apimachinery/pkg/runtime/serializer"
3028
"k8s.io/apimachinery/pkg/watch"
3129
"k8s.io/client-go/kubernetes"
3230
"k8s.io/client-go/rest"
33-
"k8s.io/client-go/scale"
3431
"k8s.io/client-go/tools/clientcmd"
3532
"k8s.io/client-go/tools/clientcmd/api"
3633
toolswatch "k8s.io/client-go/tools/watch"
@@ -71,7 +68,7 @@ func TestMain(m *testing.M) {
7168
}
7269
envTestEnv.CheckCoherence()
7370
workflows.Use{}.Do(envTestEnv)
74-
versionDir := envTestEnv.Platform.Platform.BaseName(*envTestEnv.Version.AsConcrete())
71+
versionDir := envTestEnv.Platform.BaseName(*envTestEnv.Version.AsConcrete())
7572
envTest = &envtest.Environment{
7673
BinaryAssetsDirectory: filepath.Join(envTestDir, "k8s", versionDir),
7774
}
@@ -190,9 +187,9 @@ func (c *mcpContext) withKubeConfig(rc *rest.Config) *api.Config {
190187
fakeConfig.AuthInfos["additional-auth"] = api.NewAuthInfo()
191188
if rc != nil {
192189
fakeConfig.Clusters["fake"].Server = rc.Host
193-
fakeConfig.Clusters["fake"].CertificateAuthorityData = rc.TLSClientConfig.CAData
194-
fakeConfig.AuthInfos["fake"].ClientKeyData = rc.TLSClientConfig.KeyData
195-
fakeConfig.AuthInfos["fake"].ClientCertificateData = rc.TLSClientConfig.CertData
190+
fakeConfig.Clusters["fake"].CertificateAuthorityData = rc.CAData
191+
fakeConfig.AuthInfos["fake"].ClientKeyData = rc.KeyData
192+
fakeConfig.AuthInfos["fake"].ClientCertificateData = rc.CertData
196193
}
197194
fakeConfig.Contexts["fake-context"] = api.NewContext()
198195
fakeConfig.Contexts["fake-context"].Cluster = "fake"
@@ -264,18 +261,6 @@ func (c *mcpContext) newKubernetesClient() *kubernetes.Clientset {
264261
return kubernetes.NewForConfigOrDie(envTestRestConfig)
265262
}
266263

267-
func (c *mcpContext) newRestClient(groupVersion *schema.GroupVersion) *rest.RESTClient {
268-
config := *envTestRestConfig
269-
config.GroupVersion = groupVersion
270-
config.APIPath = "/api"
271-
config.NegotiatedSerializer = serializer.NewCodecFactory(scale.NewScaleConverter().Scheme()).WithoutConversion()
272-
rc, err := rest.RESTClientFor(&config)
273-
if err != nil {
274-
panic(err)
275-
}
276-
return rc
277-
}
278-
279264
// newApiExtensionsClient creates a new ApiExtensions client with the envTest kubeconfig
280265
func (c *mcpContext) newApiExtensionsClient() *apiextensionsv1.ApiextensionsV1Client {
281266
return apiextensionsv1.NewForConfigOrDie(envTestRestConfig)
@@ -286,6 +271,9 @@ func (c *mcpContext) crdApply(resource string) error {
286271
apiExtensionsV1Client := c.newApiExtensionsClient()
287272
var crd = &apiextensionsv1spec.CustomResourceDefinition{}
288273
err := json.Unmarshal([]byte(resource), crd)
274+
if err != nil {
275+
return fmt.Errorf("failed to create CRD %v", err)
276+
}
289277
_, err = apiExtensionsV1Client.CustomResourceDefinitions().Create(c.ctx, crd, metav1.CreateOptions{})
290278
if err != nil {
291279
return fmt.Errorf("failed to create CRD %v", err)

pkg/mcp/configuration.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212

1313
func (s *Server) initConfiguration() []server.ServerTool {
1414
tools := []server.ServerTool{
15-
{mcp.NewTool("configuration_view",
15+
{Tool: mcp.NewTool("configuration_view",
1616
mcp.WithDescription("Get the current Kubernetes configuration content as a kubeconfig YAML"),
1717
mcp.WithBoolean("minified", mcp.Description("Return a minified version of the configuration. "+
1818
"If set to true, keeps only the current-context and the relevant pieces of the configuration for that context. "+
@@ -23,7 +23,7 @@ func (s *Server) initConfiguration() []server.ServerTool {
2323
mcp.WithReadOnlyHintAnnotation(true),
2424
mcp.WithDestructiveHintAnnotation(false),
2525
mcp.WithOpenWorldHintAnnotation(true),
26-
), s.configurationView},
26+
), Handler: s.configurationView},
2727
}
2828
return tools
2929
}

pkg/mcp/events.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212

1313
func (s *Server) initEvents() []server.ServerTool {
1414
return []server.ServerTool{
15-
{mcp.NewTool("events_list",
15+
{Tool: mcp.NewTool("events_list",
1616
mcp.WithDescription("List all the Kubernetes events in the current cluster from all namespaces"),
1717
mcp.WithString("namespace",
1818
mcp.Description("Optional Namespace to retrieve the events from. If not provided, will list events from all namespaces")),
@@ -21,7 +21,7 @@ func (s *Server) initEvents() []server.ServerTool {
2121
mcp.WithReadOnlyHintAnnotation(true),
2222
mcp.WithDestructiveHintAnnotation(false),
2323
mcp.WithOpenWorldHintAnnotation(true),
24-
), s.eventsList},
24+
), Handler: s.eventsList},
2525
}
2626
}
2727

0 commit comments

Comments
 (0)