diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5b09238ed..4fe90c343 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -214,3 +214,20 @@ jobs: run: make build-${{ matrix.image }} working-directory: ./ + go-lint: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.24" + cache: true + cache-dependency-path: go/go.sum + - name: golangci-lint + uses: golangci/golangci-lint-action@v8 + with: + version: v2.1 + working-directory: go \ No newline at end of file diff --git a/go/.golangci.yaml b/go/.golangci.yaml new file mode 100644 index 000000000..b7bc9bf7a --- /dev/null +++ b/go/.golangci.yaml @@ -0,0 +1,11 @@ +version: "2" + +linters: + # Default set of linters. + # The value can be: + # - `standard`: https://golangci-lint.run/docs/linters/#enabled-by-default + # - `all`: enables all linters by default. + # - `none`: disables all linters by default. + # - `fast`: enables only linters considered as "fast" (`golangci-lint help linters --json | jq '[ .[] | select(.fast==true) ] | map(.name)'`). + # Default: standard + default: standard \ No newline at end of file diff --git a/go/Makefile b/go/Makefile index 94f700986..d5d7984de 100644 --- a/go/Makefile +++ b/go/Makefile @@ -134,7 +134,7 @@ CONTROLLER_TOOLS_VERSION ?= v0.17.1 ENVTEST_VERSION ?= $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-%d.%d", $$2, $$3}') #ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31) ENVTEST_K8S_VERSION ?= $(shell go list -m -f "{{ .Version }}" k8s.io/api | awk -F'[v.]' '{printf "1.%d", $$3}') -GOLANGCI_LINT_VERSION ?= v1.63.4 +GOLANGCI_LINT_VERSION ?= v2.4.0 .PHONY: controller-gen controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. @@ -157,7 +157,7 @@ $(ENVTEST): $(LOCALBIN) .PHONY: golangci-lint golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. $(GOLANGCI_LINT): $(LOCALBIN) - $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) + $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) # go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist # $1 - target path with name of binary diff --git a/go/cli/cmd/kagent/main.go b/go/cli/cmd/kagent/main.go index a4478d830..210bbbe71 100644 --- a/go/cli/cmd/kagent/main.go +++ b/go/cli/cmd/kagent/main.go @@ -70,7 +70,7 @@ func main() { invokeCmd.Flags().BoolVarP(&invokeCfg.Stream, "stream", "S", false, "Stream the response") invokeCmd.Flags().StringVarP(&invokeCfg.File, "file", "f", "", "File to read the task from") invokeCmd.Flags().StringVarP(&invokeCfg.URLOverride, "url-override", "u", "", "URL override") - invokeCmd.Flags().MarkHidden("url-override") + invokeCmd.Flags().MarkHidden("url-override") //nolint:errcheck bugReportCmd := &cobra.Command{ Use: "bug-report", @@ -123,7 +123,7 @@ func main() { Long: `Get a kagent resource`, Run: func(cmd *cobra.Command, args []string) { fmt.Fprintf(os.Stderr, "No resource type provided\n\n") - cmd.Help() + cmd.Help() //nolint:errcheck os.Exit(1) }, } diff --git a/go/cli/internal/cli/const_test.go b/go/cli/internal/cli/const_test.go index 4a19b69f4..263b45b15 100644 --- a/go/cli/internal/cli/const_test.go +++ b/go/cli/internal/cli/const_test.go @@ -62,10 +62,10 @@ func TestGetModelProvider(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { if tc.envVarValue == "" { - os.Unsetenv(KAGENT_DEFAULT_MODEL_PROVIDER) + os.Unsetenv(KAGENT_DEFAULT_MODEL_PROVIDER) //nolint:errcheck } else { - os.Setenv(KAGENT_DEFAULT_MODEL_PROVIDER, tc.expectedHelmKey) - defer os.Unsetenv(KAGENT_DEFAULT_MODEL_PROVIDER) + os.Setenv(KAGENT_DEFAULT_MODEL_PROVIDER, tc.expectedHelmKey) //nolint:errcheck + defer os.Unsetenv(KAGENT_DEFAULT_MODEL_PROVIDER) //nolint:errcheck } result := GetModelProvider() diff --git a/go/cli/internal/cli/get.go b/go/cli/internal/cli/get.go index 54bcec421..112ee24f2 100644 --- a/go/cli/internal/cli/get.go +++ b/go/cli/internal/cli/get.go @@ -41,7 +41,7 @@ func GetAgentCmd(cfg *config.Config, resourceName string) { return } byt, _ := json.MarshalIndent(agent, "", " ") - fmt.Fprintln(os.Stdout, string(byt)) + fmt.Fprintln(os.Stdout, string(byt)) //nolint:errcheck } } @@ -70,7 +70,7 @@ func GetSessionCmd(cfg *config.Config, resourceName string) { return } byt, _ := json.MarshalIndent(session, "", " ") - fmt.Fprintln(os.Stdout, string(byt)) + fmt.Fprintln(os.Stdout, string(byt)) //nolint:errcheck } } diff --git a/go/cli/internal/cli/install.go b/go/cli/internal/cli/install.go index 1c2acaab8..8d05804e4 100644 --- a/go/cli/internal/cli/install.go +++ b/go/cli/internal/cli/install.go @@ -79,9 +79,7 @@ func InstallCmd(ctx context.Context, cfg *config.Config) *PortForward { // split helmExtraArgs by "--set" to get additional values extraValues := strings.Split(helmExtraArgs, "--set") - for _, hev := range extraValues { - values = append(values, hev) - } + values = append(values, extraValues...) // spinner for installation progress s := spinner.New(spinner.CharSets[35], 100*time.Millisecond) @@ -119,7 +117,7 @@ func InstallCmd(ctx context.Context, cfg *config.Config) *PortForward { // Stop the spinner completely before printing the success message s.Stop() - fmt.Fprintln(os.Stdout, "kagent installed successfully") + fmt.Fprintln(os.Stdout, "kagent installed successfully") //nolint:errcheck pf, err := NewPortForward(ctx, cfg) if err != nil { @@ -146,11 +144,11 @@ func deleteCRDs(ctx context.Context) error { if out, err := deleteCmd.CombinedOutput(); err != nil { if !strings.Contains(string(out), "not found") { errMsg := fmt.Sprintf("Error deleting CRD %s: %s", crd, string(out)) - fmt.Fprintln(os.Stderr, errMsg) + fmt.Fprintln(os.Stderr, errMsg) //nolint:errcheck deleteErrors = append(deleteErrors, errMsg) } } else { - fmt.Fprintf(os.Stdout, "Successfully deleted CRD %s\n", crd) + fmt.Fprintf(os.Stdout, "Successfully deleted CRD %s\n", crd) //nolint:errcheck } } @@ -216,5 +214,5 @@ func UninstallCmd(ctx context.Context, cfg *config.Config) { } s.Stop() - fmt.Fprintln(os.Stdout, "\nkagent uninstalled successfully") + fmt.Fprintln(os.Stdout, "\nkagent uninstalled successfully") //nolint:errcheck } diff --git a/go/cli/internal/cli/invoke.go b/go/cli/internal/cli/invoke.go index 461564190..74a335581 100644 --- a/go/cli/internal/cli/invoke.go +++ b/go/cli/internal/cli/invoke.go @@ -134,6 +134,6 @@ func InvokeCmd(ctx context.Context, cfg *InvokeCfg) { return } - fmt.Fprintf(os.Stdout, "%+v\n", string(jsn)) + fmt.Fprintf(os.Stdout, "%+v\n", string(jsn)) //nolint:errcheck } } diff --git a/go/cli/internal/cli/utils.go b/go/cli/internal/cli/utils.go index 7ee90d34f..603bc523d 100644 --- a/go/cli/internal/cli/utils.go +++ b/go/cli/internal/cli/utils.go @@ -13,7 +13,7 @@ import ( ) var ( - ErrServerConnection = fmt.Errorf("Error connecting to server. Please run 'install' command first.") + ErrServerConnection = fmt.Errorf("error connecting to server. Please run 'install' command first") ) func CheckServerConnection(client *client.ClientSet) error { @@ -70,7 +70,7 @@ func (p *PortForward) Stop() { p.cancel() // This will terminate the kubectl process in case the cancel does not work. if p.cmd.Process != nil { - p.cmd.Process.Kill() + p.cmd.Process.Kill() //nolint:errcheck } // Don't wait for the process - just cancel the context and let it die @@ -85,15 +85,15 @@ func StreamA2AEvents(ch <-chan protocol.StreamingMessageEvent, verbose bool) { fmt.Fprintf(os.Stderr, "Error marshaling A2A event: %v\n", err) continue } - fmt.Fprintf(os.Stdout, "%+v\n", string(json)) + fmt.Fprintf(os.Stdout, "%+v\n", string(json)) //nolint:errcheck } else { json, err := event.MarshalJSON() if err != nil { fmt.Fprintf(os.Stderr, "Error marshaling A2A event: %v\n", err) continue } - fmt.Fprintf(os.Stdout, "%+v\n", string(json)) + fmt.Fprintf(os.Stdout, "%+v\n", string(json)) //nolint:errcheck } } - fmt.Fprintln(os.Stdout) // Add a newline after streaming is complete + fmt.Fprintln(os.Stdout) //nolint:errcheck // Add a newline after streaming is complete } diff --git a/go/cli/internal/cli/version.go b/go/cli/internal/cli/version.go index e76da8e7e..4d699efdd 100644 --- a/go/cli/internal/cli/version.go +++ b/go/cli/internal/cli/version.go @@ -28,5 +28,5 @@ func VersionCmd(cfg *config.Config) { versionInfo["backend_version"] = version.KAgentVersion } - json.NewEncoder(os.Stdout).Encode(versionInfo) + json.NewEncoder(os.Stdout).Encode(versionInfo) //nolint:errcheck } diff --git a/go/controller/cmd/main.go b/go/controller/cmd/main.go index 3e6b3b601..0621ad682 100644 --- a/go/controller/cmd/main.go +++ b/go/controller/cmd/main.go @@ -442,6 +442,10 @@ func main() { Authorizer: authorizer, Authenticator: authenticator, }) + if err != nil { + setupLog.Error(err, "unable to create HTTP server") + os.Exit(1) + } if err := mgr.Add(httpServer); err != nil { setupLog.Error(err, "unable to set up HTTP server") os.Exit(1) diff --git a/go/controller/internal/a2a/a2a_reconciler.go b/go/controller/internal/a2a/a2a_reconciler.go index 9c7d7b218..8e3f1fc20 100644 --- a/go/controller/internal/a2a/a2a_reconciler.go +++ b/go/controller/internal/a2a/a2a_reconciler.go @@ -8,15 +8,10 @@ import ( "github.com/kagent-dev/kagent/go/internal/a2a" "github.com/kagent-dev/kagent/go/internal/httpserver/auth" common "github.com/kagent-dev/kagent/go/internal/utils" - ctrl "sigs.k8s.io/controller-runtime" a2aclient "trpc.group/trpc-go/trpc-a2a-go/client" "trpc.group/trpc-go/trpc-a2a-go/server" ) -var ( - reconcileLog = ctrl.Log.WithName("a2a_reconcile") -) - type A2AReconciler interface { ReconcileAgent( ctx context.Context, diff --git a/go/controller/internal/reconciler/reconciler.go b/go/controller/internal/reconciler/reconciler.go index ce138b535..54020d38a 100644 --- a/go/controller/internal/reconciler/reconciler.go +++ b/go/controller/internal/reconciler/reconciler.go @@ -11,7 +11,6 @@ import ( "github.com/hashicorp/go-multierror" appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" k8s_errors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -93,11 +92,11 @@ func (a *kagentReconciler) ReconcileKagentAgent(ctx context.Context, req ctrl.Re func (a *kagentReconciler) handleAgentDeletion(req ctrl.Request) error { // remove a2a handler if it exists - a.a2aReconciler.ReconcileAgentDeletion(req.NamespacedName.String()) + a.a2aReconciler.ReconcileAgentDeletion(req.String()) - if err := a.dbClient.DeleteAgent(req.NamespacedName.String()); err != nil { + if err := a.dbClient.DeleteAgent(req.String()); err != nil { return fmt.Errorf("failed to delete agent %s: %w", - req.NamespacedName.String(), err) + req.String(), err) } reconcileLog.Info("Agent was deleted", "namespace", req.Namespace, "name", req.Name) @@ -180,7 +179,7 @@ func (a *kagentReconciler) reconcileAgentStatus(ctx context.Context, agent *v1al } } - conditionChanged = meta.SetStatusCondition(&agent.Status.Conditions, deployedCondition) + conditionChanged = conditionChanged || meta.SetStatusCondition(&agent.Status.Conditions, deployedCondition) // Only update the config hash if the config hash has changed and there was no error configHashChanged := len(configHash) > 0 && !bytes.Equal((agent.Status.ConfigHash)[:], configHash[:]) @@ -205,15 +204,15 @@ func (a *kagentReconciler) ReconcileKagentMCPService(ctx context.Context, req ct if k8s_errors.IsNotFound(err) { // Delete from DB if the service is deleted dbService := &database.ToolServer{ - Name: req.NamespacedName.String(), + Name: req.String(), GroupKind: schema.GroupKind{Group: "", Kind: "Service"}.String(), } if err := a.dbClient.DeleteToolServer(dbService.Name, dbService.GroupKind); err != nil { - reconcileLog.Error(err, "failed to delete tool server for mcp service", "service", req.NamespacedName.String()) + reconcileLog.Error(err, "failed to delete tool server for mcp service", "service", req.String()) } - reconcileLog.Info("mcp service was deleted", "service", req.NamespacedName.String()) + reconcileLog.Info("mcp service was deleted", "service", req.String()) if err := a.dbClient.DeleteToolsForServer(dbService.Name, dbService.GroupKind); err != nil { - reconcileLog.Error(err, "failed to delete tools for mcp service", "service", req.NamespacedName.String()) + reconcileLog.Error(err, "failed to delete tools for mcp service", "service", req.String()) } return nil } @@ -248,8 +247,8 @@ func (a *kagentReconciler) ReconcileKagentModelConfig(ctx context.Context, req c var err error if modelConfig.Spec.APIKeySecret != "" { - secret := &v1.Secret{} - if err := a.kube.Get(ctx, types.NamespacedName{Namespace: modelConfig.Namespace, Name: modelConfig.Spec.APIKeySecret}, secret); err != nil { + secret := &corev1.Secret{} + if err = a.kube.Get(ctx, types.NamespacedName{Namespace: modelConfig.Namespace, Name: modelConfig.Spec.APIKeySecret}, secret); err != nil { err = fmt.Errorf("failed to get secret %s: %v", modelConfig.Spec.APIKeySecret, err) } } @@ -301,15 +300,15 @@ func (a *kagentReconciler) ReconcileKagentMCPServer(ctx context.Context, req ctr if k8s_errors.IsNotFound(err) { // Delete from DB if the mcp server is deleted dbServer := &database.ToolServer{ - Name: req.NamespacedName.String(), + Name: req.String(), GroupKind: schema.GroupKind{Group: "kagent.dev", Kind: "MCPServer"}.String(), } if err := a.dbClient.DeleteToolServer(dbServer.Name, dbServer.GroupKind); err != nil { - reconcileLog.Error(err, "failed to delete tool server for mcp server", "mcpServer", req.NamespacedName.String()) + reconcileLog.Error(err, "failed to delete tool server for mcp server", "mcpServer", req.String()) } - reconcileLog.Info("mcp server was deleted", "mcpServer", req.NamespacedName.String()) + reconcileLog.Info("mcp server was deleted", "mcpServer", req.String()) if err := a.dbClient.DeleteToolsForServer(dbServer.Name, dbServer.GroupKind); err != nil { - reconcileLog.Error(err, "failed to delete tools for mcp server", "mcpServer", req.NamespacedName.String()) + reconcileLog.Error(err, "failed to delete tools for mcp server", "mcpServer", req.String()) } return nil } @@ -340,15 +339,15 @@ func (a *kagentReconciler) ReconcileKagentRemoteMCPServer(ctx context.Context, r if k8s_errors.IsNotFound(err) { // Delete from DB if the remote mcp server is deleted dbServer := &database.ToolServer{ - Name: req.NamespacedName.String(), + Name: req.String(), GroupKind: schema.GroupKind{Group: "kagent.dev", Kind: "RemoteMCPServer"}.String(), } if err := a.dbClient.DeleteToolServer(dbServer.Name, dbServer.GroupKind); err != nil { - reconcileLog.Error(err, "failed to delete tool server for remote mcp server", "remoteMCPServer", req.NamespacedName.String()) + reconcileLog.Error(err, "failed to delete tool server for remote mcp server", "remoteMCPServer", req.String()) } - reconcileLog.Info("remote mcp server was deleted", "remoteMCPServer", req.NamespacedName.String()) + reconcileLog.Info("remote mcp server was deleted", "remoteMCPServer", req.String()) if err := a.dbClient.DeleteToolsForServer(dbServer.Name, dbServer.GroupKind); err != nil { - reconcileLog.Error(err, "failed to delete tools for remote mcp server", "remoteMCPServer", req.NamespacedName.String()) + reconcileLog.Error(err, "failed to delete tools for remote mcp server", "remoteMCPServer", req.String()) } return nil } @@ -513,8 +512,8 @@ func (a *kagentReconciler) createMcpTransport(ctx context.Context, s *v1alpha2.R return nil, err } - switch { - case s.Protocol == v1alpha2.RemoteMCPServerProtocolSse: + switch s.Protocol { + case v1alpha2.RemoteMCPServerProtocolSse: return transport.NewSSE(s.URL, transport.WithHeaders(headers)) default: return transport.NewStreamableHTTP(s.URL, transport.WithHTTPHeaders(headers)) @@ -527,7 +526,7 @@ func (a *kagentReconciler) listTools(ctx context.Context, tsp transport.Interfac if err != nil { return nil, fmt.Errorf("failed to start client for toolServer %s: %v", toolServer.Name, err) } - defer client.Close() + defer client.Close() //nolint:errcheck _, err = client.Initialize(ctx, mcp.InitializeRequest{ Params: mcp.InitializeParams{ ProtocolVersion: mcp.LATEST_PROTOCOL_VERSION, diff --git a/go/controller/translator/adk_api_translator.go b/go/controller/translator/adk_api_translator.go index 26b06d6e5..79e63755b 100644 --- a/go/controller/translator/adk_api_translator.go +++ b/go/controller/translator/adk_api_translator.go @@ -15,7 +15,6 @@ import ( "github.com/kagent-dev/kagent/go/controller/api/v1alpha2" "github.com/kagent-dev/kagent/go/internal/adk" "github.com/kagent-dev/kagent/go/internal/utils" - common "github.com/kagent-dev/kagent/go/internal/utils" "github.com/kagent-dev/kagent/go/internal/version" "github.com/kagent-dev/kmcp/api/v1alpha1" appsv1 "k8s.io/api/apps/v1" @@ -28,7 +27,6 @@ import ( "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - ctrllog "sigs.k8s.io/controller-runtime/pkg/log" "trpc.group/trpc-go/trpc-a2a-go/server" ) @@ -63,8 +61,6 @@ type AgentOutputs struct { AgentCard server.AgentCard `json:"agentCard"` } -var adkLog = ctrllog.Log.WithName("adk") - type AdkApiTranslator interface { TranslateAgent( ctx context.Context, @@ -82,7 +78,6 @@ func NewAdkApiTranslator(kube client.Client, defaultModelConfig types.Namespaced type adkApiTranslator struct { kube client.Client defaultModelConfig types.NamespacedName - imageConfig ImageConfig } const MAX_DEPTH = 10 @@ -98,7 +93,7 @@ type tState struct { func (s *tState) with(agent *v1alpha2.Agent) *tState { s.depth++ - s.visitedAgents = append(s.visitedAgents, common.GetObjectRef(agent)) + s.visitedAgents = append(s.visitedAgents, utils.GetObjectRef(agent)) return s } @@ -199,7 +194,7 @@ func (a *adkApiTranslator) buildManifest( }, corev1.EnvVar{ Name: "KAGENT_URL", - Value: fmt.Sprintf("http://kagent-controller.%s:8083", common.GetResourceNamespace()), + Value: fmt.Sprintf("http://kagent-controller.%s:8083", utils.GetResourceNamespace()), }, ) @@ -402,9 +397,7 @@ func (a *adkApiTranslator) translateInlineAgent(ctx context.Context, agent *v1al // Skip tools that are not applicable to the model provider switch { case tool.McpServer != nil: - for _, toolName := range tool.McpServer.ToolNames { - toolsByServer[tool.McpServer.TypedLocalReference] = append(toolsByServer[tool.McpServer.TypedLocalReference], toolName) - } + toolsByServer[tool.McpServer.TypedLocalReference] = append(toolsByServer[tool.McpServer.TypedLocalReference], tool.McpServer.ToolNames...) case tool.Agent != nil: agentRef := types.NamespacedName{ @@ -647,7 +640,7 @@ func (a *adkApiTranslator) translateModel(ctx context.Context, namespace, modelC return anthropic, modelDeploymentData, nil case v1alpha2.ModelProviderOllama: if model.Spec.Ollama == nil { - return nil, nil, fmt.Errorf("Ollama model config is required") + return nil, nil, fmt.Errorf("ollama model config is required") } modelDeploymentData.EnvVars = append(modelDeploymentData.EnvVars, corev1.EnvVar{ Name: "OLLAMA_API_BASE", @@ -839,7 +832,7 @@ func ConvertServiceToRemoteMCPServer(svc *corev1.Service) (*v1alpha2.RemoteMCPSe func ConvertMCPServerToRemoteMCPServer(mcpServer *v1alpha1.MCPServer) (*v1alpha2.RemoteMCPServerSpec, error) { if mcpServer.Spec.Deployment.Port == 0 { - return nil, fmt.Errorf("Cannot determine port for MCP server %s", mcpServer.Name) + return nil, fmt.Errorf("cannot determine port for MCP server %s", mcpServer.Name) } return &v1alpha2.RemoteMCPServerSpec{ @@ -848,8 +841,8 @@ func ConvertMCPServerToRemoteMCPServer(mcpServer *v1alpha1.MCPServer) (*v1alpha2 }, nil } func (a *adkApiTranslator) translateRemoteMCPServerTarget(ctx context.Context, agent *adk.AgentConfig, remoteMcpServer *v1alpha2.RemoteMCPServerSpec, toolNames []string, agentNamespace string) error { - switch { - case remoteMcpServer.Protocol == v1alpha2.RemoteMCPServerProtocolSse: + switch remoteMcpServer.Protocol { + case v1alpha2.RemoteMCPServerProtocolSse: tool, err := a.translateSseHttpTool(ctx, remoteMcpServer, agentNamespace) if err != nil { return err diff --git a/go/internal/a2a/a2a_handler_mux.go b/go/internal/a2a/a2a_handler_mux.go index b1752701e..c4f9b09ab 100644 --- a/go/internal/a2a/a2a_handler_mux.go +++ b/go/internal/a2a/a2a_handler_mux.go @@ -84,7 +84,7 @@ func (a *handlerMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { http.Error(w, "Agent namespace not provided", http.StatusBadRequest) return } - agentName, remainingPath := popPath(remainingPath) + agentName, _ := popPath(remainingPath) if agentName == "" { http.Error(w, "Agent name not provided", http.StatusBadRequest) return diff --git a/go/internal/database/fake/client.go b/go/internal/database/fake/client.go index de9701008..1423fed9e 100644 --- a/go/internal/database/fake/client.go +++ b/go/internal/database/fake/client.go @@ -42,17 +42,6 @@ func NewClient() database.Client { nextFeedbackID: 1, } } -func (c *InMemoryFakeClient) messageKey(message *protocol.Message) string { - taskId := "none" - if message.TaskID != nil { - taskId = *message.TaskID - } - contextId := "none" - if message.ContextID != nil { - contextId = *message.ContextID - } - return fmt.Sprintf("%s_%s", taskId, contextId) -} func (c *InMemoryFakeClient) sessionKey(sessionID, userID string) string { return fmt.Sprintf("%s_%s", sessionID, userID) diff --git a/go/internal/httpserver/auth/authn.go b/go/internal/httpserver/auth/authn.go index ddaf49ad2..1068755ac 100644 --- a/go/internal/httpserver/auth/authn.go +++ b/go/internal/httpserver/auth/authn.go @@ -123,7 +123,7 @@ func A2ARequestHandler(authProvider AuthProvider) handler { var resp *http.Response defer func() { if err != nil && resp != nil { - resp.Body.Close() + resp.Body.Close() //nolint:errcheck } }() diff --git a/go/internal/httpserver/handlers/agents.go b/go/internal/httpserver/handlers/agents.go index 829c23890..e5173cfec 100644 --- a/go/internal/httpserver/handlers/agents.go +++ b/go/internal/httpserver/handlers/agents.go @@ -10,7 +10,6 @@ import ( "github.com/kagent-dev/kagent/go/internal/httpserver/auth" "github.com/kagent-dev/kagent/go/internal/httpserver/errors" "github.com/kagent-dev/kagent/go/internal/utils" - common "github.com/kagent-dev/kagent/go/internal/utils" "github.com/kagent-dev/kagent/go/pkg/client/api" k8serrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" @@ -45,7 +44,7 @@ func (h *AgentsHandler) HandleListAgents(w ErrorResponseWriter, r *http.Request) agentsWithID := make([]api.AgentResponse, 0) for _, agent := range agentList.Items { - agentRef := common.GetObjectRef(&agent) + agentRef := utils.GetObjectRef(&agent) log.V(1).Info("Processing Agent", "agentRef", agentRef) agentResponse, err := h.getAgentResponse(r.Context(), log, &agent) @@ -64,7 +63,7 @@ func (h *AgentsHandler) HandleListAgents(w ErrorResponseWriter, r *http.Request) func (h *AgentsHandler) getAgentResponse(ctx context.Context, log logr.Logger, agent *v1alpha2.Agent) (api.AgentResponse, error) { - agentRef := common.GetObjectRef(agent) + agentRef := utils.GetObjectRef(agent) log.V(1).Info("Processing Agent", "agentRef", agentRef) deploymentReady := false @@ -76,7 +75,7 @@ func (h *AgentsHandler) getAgentResponse(ctx context.Context, log logr.Logger, a } response := api.AgentResponse{ - ID: common.ConvertToPythonIdentifier(agentRef), + ID: utils.ConvertToPythonIdentifier(agentRef), Agent: agent, DeploymentReady: deploymentReady, } @@ -102,7 +101,7 @@ func (h *AgentsHandler) getAgentResponse(ctx context.Context, log logr.Logger, a } response.ModelProvider = modelConfig.Spec.Provider response.Model = modelConfig.Spec.Model - response.ModelConfigRef = common.GetObjectRef(modelConfig) + response.ModelConfigRef = utils.GetObjectRef(modelConfig) response.Tools = agent.Spec.Declarative.Tools } @@ -165,11 +164,11 @@ func (h *AgentsHandler) HandleCreateAgent(w ErrorResponseWriter, r *http.Request return } if agentReq.Namespace == "" { - agentReq.Namespace = common.GetResourceNamespace() + agentReq.Namespace = utils.GetResourceNamespace() log.V(4).Info("Namespace not provided in request. Creating in controller installation namespace", "namespace", agentReq.Namespace) } - agentRef, err := common.ParseRefString(agentReq.Name, agentReq.Namespace) + agentRef, err := utils.ParseRefString(agentReq.Name, agentReq.Namespace) if err != nil { w.RespondWithError(errors.NewBadRequestError("Invalid agent metadata", err)) } @@ -185,7 +184,10 @@ func (h *AgentsHandler) HandleCreateAgent(w ErrorResponseWriter, r *http.Request } kubeClientWrapper := utils.NewKubeClientWrapper(h.KubeClient) - kubeClientWrapper.AddInMemory(&agentReq) + if err := kubeClientWrapper.AddInMemory(&agentReq); err != nil { + w.RespondWithError(errors.NewInternalServerError("Failed to add Agent to Kubernetes wrapper", err)) + return + } apiTranslator := translator.NewAdkApiTranslator( kubeClientWrapper, @@ -222,11 +224,11 @@ func (h *AgentsHandler) HandleUpdateAgent(w ErrorResponseWriter, r *http.Request } if agentReq.Namespace == "" { - agentReq.Namespace = common.GetResourceNamespace() + agentReq.Namespace = utils.GetResourceNamespace() log.V(4).Info("Namespace not provided in request. Creating in controller installation namespace", "namespace", agentReq.Namespace) } - agentRef, err := common.ParseRefString(agentReq.Name, agentReq.Namespace) + agentRef, err := utils.ParseRefString(agentReq.Name, agentReq.Namespace) if err != nil { w.RespondWithError(errors.NewBadRequestError("Invalid Agent metadata", err)) } @@ -245,10 +247,7 @@ func (h *AgentsHandler) HandleUpdateAgent(w ErrorResponseWriter, r *http.Request existingAgent := &v1alpha2.Agent{} err = h.KubeClient.Get( r.Context(), - client.ObjectKey{ - Namespace: agentRef.Namespace, - Name: agentRef.Name, - }, + agentRef, existingAgent, ) if err != nil { diff --git a/go/internal/httpserver/handlers/agents_test.go b/go/internal/httpserver/handlers/agents_test.go index fc92cda2a..0d649fc37 100644 --- a/go/internal/httpserver/handlers/agents_test.go +++ b/go/internal/httpserver/handlers/agents_test.go @@ -88,7 +88,7 @@ func createAgent(client database.Client, agent *v1alpha2.Agent) { Config: &adk.AgentConfig{}, ID: common.GetObjectRef(agent), } - client.StoreAgent(dbAgent) + client.StoreAgent(dbAgent) //nolint:errcheck } func TestHandleGetAgent(t *testing.T) { @@ -97,7 +97,7 @@ func TestHandleGetAgent(t *testing.T) { team := createTestAgent("test-team", modelConfig) handler, _ := setupTestHandler(team, modelConfig) - createAgent(handler.Base.DatabaseService, team) + createAgent(handler.DatabaseService, team) req := httptest.NewRequest("GET", "/api/agents/default/test-team", nil) req = mux.SetURLVars(req, map[string]string{"namespace": "default", "name": "test-team"}) @@ -135,7 +135,7 @@ func TestHandleGetAgent(t *testing.T) { agent := createTestAgentWithStatus("test-agent-ready", modelConfig, conditions) handler, _ := setupTestHandler(agent, modelConfig) - createAgent(handler.Base.DatabaseService, agent) + createAgent(handler.DatabaseService, agent) req := httptest.NewRequest("GET", "/api/agents/default/test-agent-ready", nil) req = mux.SetURLVars(req, map[string]string{"namespace": "default", "name": "test-agent-ready"}) @@ -164,7 +164,7 @@ func TestHandleGetAgent(t *testing.T) { agent := createTestAgentWithStatus("test-agent-not-ready", modelConfig, conditions) handler, _ := setupTestHandler(agent, modelConfig) - createAgent(handler.Base.DatabaseService, agent) + createAgent(handler.DatabaseService, agent) req := httptest.NewRequest("GET", "/api/agents/default/test-agent-not-ready", nil) req = mux.SetURLVars(req, map[string]string{"namespace": "default", "name": "test-agent-not-ready"}) @@ -193,7 +193,7 @@ func TestHandleGetAgent(t *testing.T) { agent := createTestAgentWithStatus("test-agent-different-reason", modelConfig, conditions) handler, _ := setupTestHandler(agent, modelConfig) - createAgent(handler.Base.DatabaseService, agent) + createAgent(handler.DatabaseService, agent) req := httptest.NewRequest("GET", "/api/agents/default/test-agent-different-reason", nil) req = mux.SetURLVars(req, map[string]string{"namespace": "default", "name": "test-agent-different-reason"}) @@ -242,8 +242,8 @@ func TestHandleListAgents(t *testing.T) { notReadyAgent := createTestAgent("not-ready-agent", modelConfig) handler, _ := setupTestHandler(readyAgent, notReadyAgent, modelConfig) - createAgent(handler.Base.DatabaseService, readyAgent) - createAgent(handler.Base.DatabaseService, notReadyAgent) + createAgent(handler.DatabaseService, readyAgent) + createAgent(handler.DatabaseService, notReadyAgent) req := httptest.NewRequest("GET", "/api/agents", nil) req = setUser(req, "test-user") @@ -384,7 +384,7 @@ func TestHandleDeleteTeam(t *testing.T) { } handler, _ := setupTestHandler(team) - createAgent(handler.Base.DatabaseService, team) + createAgent(handler.DatabaseService, team) req := httptest.NewRequest("DELETE", "/api/agents/default/test-team", nil) req = mux.SetURLVars(req, map[string]string{"namespace": "default", "name": "test-team"}) diff --git a/go/internal/httpserver/handlers/helpers.go b/go/internal/httpserver/handlers/helpers.go index 309b54b10..14d53acfe 100644 --- a/go/internal/httpserver/handlers/helpers.go +++ b/go/internal/httpserver/handlers/helpers.go @@ -36,7 +36,7 @@ func RespondWithJSON(w http.ResponseWriter, code int, payload interface{}) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) - w.Write(response) + w.Write(response) //nolint:errcheck log.V(2).Info("Sent JSON response", "statusCode", code, "responseSize", len(response)) } @@ -133,7 +133,7 @@ func DecodeJSONBody(r *http.Request, target interface{}) error { log.Info("Failed to decode JSON request body", "error", err.Error()) return err } - defer r.Body.Close() + defer r.Body.Close() //nolint:errcheck log.V(2).Info("Successfully decoded JSON request body") return nil diff --git a/go/internal/httpserver/handlers/memory.go b/go/internal/httpserver/handlers/memory.go index 4f3b8b053..a84cba771 100644 --- a/go/internal/httpserver/handlers/memory.go +++ b/go/internal/httpserver/handlers/memory.go @@ -105,10 +105,7 @@ func (h *MemoryHandler) HandleCreateMemory(w ErrorResponseWriter, r *http.Reques existingMemory := &v1alpha1.Memory{} err = h.KubeClient.Get( r.Context(), - client.ObjectKey{ - Namespace: memoryRef.Namespace, - Name: memoryRef.Name, - }, + memoryRef, existingMemory, ) if err == nil { diff --git a/go/internal/httpserver/handlers/modelconfig.go b/go/internal/httpserver/handlers/modelconfig.go index 355afb338..2beb5a81c 100644 --- a/go/internal/httpserver/handlers/modelconfig.go +++ b/go/internal/httpserver/handlers/modelconfig.go @@ -217,10 +217,7 @@ func (h *ModelConfigHandler) HandleCreateModelConfig(w ErrorResponseWriter, r *h existingConfig := &v1alpha2.ModelConfig{} err = h.KubeClient.Get( r.Context(), - client.ObjectKey{ - Namespace: modelConfigRef.Namespace, - Name: modelConfigRef.Name, - }, + modelConfigRef, existingConfig, ) if err == nil { diff --git a/go/internal/httpserver/handlers/sessions_test.go b/go/internal/httpserver/handlers/sessions_test.go index 49c65c391..cff407290 100644 --- a/go/internal/httpserver/handlers/sessions_test.go +++ b/go/internal/httpserver/handlers/sessions_test.go @@ -56,7 +56,7 @@ func TestSessionsHandler(t *testing.T) { agent := &database.Agent{ ID: agentRef, } - dbClient.StoreAgent(agent) + dbClient.StoreAgent(agent) //nolint:errcheck // The fake client should assign an ID, but we'll use a default for testing agent.ID = "1" // Simulate the ID that would be assigned by GORM return agent @@ -69,7 +69,7 @@ func TestSessionsHandler(t *testing.T) { UserID: userID, AgentID: &agentID, } - dbClient.StoreSession(session) + dbClient.StoreSession(session) //nolint:errcheck return session } diff --git a/go/internal/httpserver/handlers/utils.go b/go/internal/httpserver/handlers/utils.go index a0c5de76c..e5fd14da3 100644 --- a/go/internal/httpserver/handlers/utils.go +++ b/go/internal/httpserver/handlers/utils.go @@ -12,23 +12,8 @@ import ( "github.com/kagent-dev/kagent/go/controller/api/v1alpha1" "github.com/kagent-dev/kagent/go/controller/api/v1alpha2" - common "github.com/kagent-dev/kagent/go/internal/utils" ) -// Helper function to update a reference string -func updateRef(refPtr *string, namespace string) error { - if refPtr == nil { - return fmt.Errorf("reference pointer cannot be nil") - } - - ref, err := common.ParseRefString(*refPtr, namespace) - if err != nil { - return err - } - *refPtr = ref.String() - return nil -} - // createSecretWithOwnerReference creates a Kubernetes secret with owner reference. // Secret will have the same name and namespace as the owner object. func createSecretWithOwnerReference( diff --git a/go/internal/httpserver/middleware_error.go b/go/internal/httpserver/middleware_error.go index 4c2958935..f6593916b 100644 --- a/go/internal/httpserver/middleware_error.go +++ b/go/internal/httpserver/middleware_error.go @@ -63,5 +63,5 @@ func (w *errorResponseWriter) RespondWithError(err error) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(statusCode) - json.NewEncoder(w).Encode(map[string]string{"error": responseMessage}) + json.NewEncoder(w).Encode(map[string]string{"error": responseMessage}) //nolint:errcheck } diff --git a/go/internal/httpserver/server.go b/go/internal/httpserver/server.go index 84df1fc5a..5c3b1d5e2 100644 --- a/go/internal/httpserver/server.go +++ b/go/internal/httpserver/server.go @@ -60,7 +60,6 @@ type HTTPServer struct { router *mux.Router handlers *handlers.Handlers dbManager *database.Manager - dbClient database.Client authenticator auth.AuthProvider } diff --git a/go/internal/utils/client_wrapper_test.go b/go/internal/utils/client_wrapper_test.go index 5bc5a5717..ff04f3a24 100644 --- a/go/internal/utils/client_wrapper_test.go +++ b/go/internal/utils/client_wrapper_test.go @@ -372,7 +372,7 @@ type mockObject struct { func (m *mockObject) DeepCopyObject() runtime.Object { return &mockObject{ TypeMeta: m.TypeMeta, - ObjectMeta: *m.ObjectMeta.DeepCopy(), + ObjectMeta: *m.DeepCopy(), Data: m.Data, } } diff --git a/go/pkg/client/agent.go b/go/pkg/client/agent.go index 75341b2f7..81c3b5719 100644 --- a/go/pkg/client/agent.go +++ b/go/pkg/client/agent.go @@ -101,6 +101,6 @@ func (c *agentClient) DeleteAgent(ctx context.Context, agentRef string) error { if err != nil { return err } - resp.Body.Close() + resp.Body.Close() //nolint:errcheck return nil } diff --git a/go/pkg/client/base.go b/go/pkg/client/base.go index dce95127f..f930496f7 100644 --- a/go/pkg/client/base.go +++ b/go/pkg/client/base.go @@ -113,7 +113,7 @@ func (c *BaseClient) doRequest(ctx context.Context, method, path string, body in if resp.StatusCode >= 400 { bodyBytes, _ := io.ReadAll(resp.Body) - resp.Body.Close() + resp.Body.Close() //nolint:errcheck var apiErr api.APIError if json.Unmarshal(bodyBytes, &apiErr) == nil && apiErr.Error != "" { @@ -151,7 +151,7 @@ func (c *BaseClient) Delete(ctx context.Context, path string, userID string) (*h } func DecodeResponse(resp *http.Response, target interface{}) error { - defer resp.Body.Close() + defer resp.Body.Close() //nolint:errcheck return json.NewDecoder(resp.Body).Decode(target) } diff --git a/go/pkg/client/feedback.go b/go/pkg/client/feedback.go index 8b3ffa68d..accb7a420 100644 --- a/go/pkg/client/feedback.go +++ b/go/pkg/client/feedback.go @@ -28,11 +28,10 @@ func (c *feedbackClient) CreateFeedback(ctx context.Context, feedback *api.Feedb userID = c.client.GetUserIDOrDefault(userID) feedback.UserID = userID - resp, err := c.client.Post(ctx, "/api/feedback", feedback, "") + _, err := c.client.Post(ctx, "/api/feedback", feedback, "") if err != nil { return err } - resp.Body.Close() return nil } diff --git a/go/pkg/client/health.go b/go/pkg/client/health.go index 513c1a4c0..5e27d54c1 100644 --- a/go/pkg/client/health.go +++ b/go/pkg/client/health.go @@ -21,10 +21,9 @@ func NewHealthClient(client *BaseClient) Health { // Health checks if the server is healthy func (c *healthClient) Get(ctx context.Context) error { - resp, err := c.client.Get(ctx, "/health", "") + _, err := c.client.Get(ctx, "/health", "") if err != nil { return err } - resp.Body.Close() return nil } diff --git a/go/pkg/client/memory.go b/go/pkg/client/memory.go index 26fe331a9..fb427f42a 100644 --- a/go/pkg/client/memory.go +++ b/go/pkg/client/memory.go @@ -92,10 +92,9 @@ func (c *memoryClient) UpdateMemory(ctx context.Context, namespace, memoryName s // DeleteMemory deletes a memory func (c *memoryClient) DeleteMemory(ctx context.Context, namespace, memoryName string) error { path := fmt.Sprintf("/api/memories/%s/%s", namespace, memoryName) - resp, err := c.client.Delete(ctx, path, "") + _, err := c.client.Delete(ctx, path, "") if err != nil { return err } - resp.Body.Close() return nil } diff --git a/go/pkg/client/modelconfig.go b/go/pkg/client/modelconfig.go index 19dfb01a0..9291738c2 100644 --- a/go/pkg/client/modelconfig.go +++ b/go/pkg/client/modelconfig.go @@ -92,10 +92,9 @@ func (c *ModelConfigClient) UpdateModelConfig(ctx context.Context, namespace, co // DeleteModelConfig deletes a model configuration func (c *ModelConfigClient) DeleteModelConfig(ctx context.Context, namespace, configName string) error { path := fmt.Sprintf("/api/modelconfigs/%s/%s", namespace, configName) - resp, err := c.client.Delete(ctx, path, "") + _, err := c.client.Delete(ctx, path, "") if err != nil { return err } - resp.Body.Close() return nil } diff --git a/go/pkg/client/session.go b/go/pkg/client/session.go index 402dafac1..66440c08b 100644 --- a/go/pkg/client/session.go +++ b/go/pkg/client/session.go @@ -115,11 +115,10 @@ func (c *sessionClient) DeleteSession(ctx context.Context, sessionName string) e } path := fmt.Sprintf("/api/sessions/%s", sessionName) - resp, err := c.client.Delete(ctx, path, userID) + _, err := c.client.Delete(ctx, path, userID) if err != nil { return err } - resp.Body.Close() return nil } diff --git a/go/pkg/client/toolserver.go b/go/pkg/client/toolserver.go index 40bbb0b66..704e01e04 100644 --- a/go/pkg/client/toolserver.go +++ b/go/pkg/client/toolserver.go @@ -58,10 +58,9 @@ func (c *ToolServerClient) CreateToolServer(ctx context.Context, toolServer *v1a // DeleteToolServer deletes a tool server func (c *ToolServerClient) DeleteToolServer(ctx context.Context, namespace, toolServerName string) error { path := fmt.Sprintf("/api/toolservers/%s/%s", namespace, toolServerName) - resp, err := c.client.Delete(ctx, path, "") + _, err := c.client.Delete(ctx, path, "") if err != nil { return err } - resp.Body.Close() return nil } diff --git a/go/pkg/sse/sse.go b/go/pkg/sse/sse.go index 9831e0f07..17eabfbd8 100644 --- a/go/pkg/sse/sse.go +++ b/go/pkg/sse/sse.go @@ -16,7 +16,7 @@ func StreamSseResponse(r io.ReadCloser) <-chan *Event { ch := make(chan *Event, 10) go func() { defer close(ch) - defer r.Close() + defer r.Close() //nolint:errcheck currentEvent := &Event{} for scanner.Scan() { line := scanner.Bytes()