diff --git a/cmd/headscale/cli/mockoidc.go b/cmd/headscale/cli/mockoidc.go index af28ce9f..c80c2a28 100644 --- a/cmd/headscale/cli/mockoidc.go +++ b/cmd/headscale/cli/mockoidc.go @@ -134,7 +134,7 @@ func getMockOIDC(clientID string, clientSecret string, users []mockoidc.MockUser ErrorQueue: &mockoidc.ErrorQueue{}, } - mock.AddMiddleware(func(h http.Handler) http.Handler { + _ = mock.AddMiddleware(func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Info().Msgf("Request: %+v", r) h.ServeHTTP(w, r) diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go index 882460dd..219f69c9 100644 --- a/cmd/headscale/cli/nodes.go +++ b/cmd/headscale/cli/nodes.go @@ -72,12 +72,12 @@ func init() { nodeCmd.AddCommand(deleteNodeCmd) tagCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)") - tagCmd.MarkFlagRequired("identifier") + _ = tagCmd.MarkFlagRequired("identifier") tagCmd.Flags().StringSliceP("tags", "t", []string{}, "List of tags to add to the node") nodeCmd.AddCommand(tagCmd) approveRoutesCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)") - approveRoutesCmd.MarkFlagRequired("identifier") + _ = approveRoutesCmd.MarkFlagRequired("identifier") approveRoutesCmd.Flags().StringSliceP("routes", "r", []string{}, `List of routes that will be approved (comma-separated, e.g. "10.0.0.0/8,192.168.0.0/24" or empty string to remove all approved routes)`) nodeCmd.AddCommand(approveRoutesCmd) diff --git a/cmd/headscale/cli/users.go b/cmd/headscale/cli/users.go index 6e4bdd02..9f0954c6 100644 --- a/cmd/headscale/cli/users.go +++ b/cmd/headscale/cli/users.go @@ -51,7 +51,7 @@ func init() { userCmd.AddCommand(renameUserCmd) usernameAndIDFlag(renameUserCmd) renameUserCmd.Flags().StringP("new-name", "r", "", "New username") - renameNodeCmd.MarkFlagRequired("new-name") + _ = renameUserCmd.MarkFlagRequired("new-name") } var errMissingParameter = errors.New("missing parameters") diff --git a/hscontrol/db/db.go b/hscontrol/db/db.go index 988675b9..1ef767ce 100644 --- a/hscontrol/db/db.go +++ b/hscontrol/db/db.go @@ -1035,7 +1035,7 @@ func (hsdb *HSDatabase) Close() error { } if hsdb.cfg.Database.Type == types.DatabaseSqlite && hsdb.cfg.Database.Sqlite.WriteAheadLog { - db.Exec("VACUUM") + _, _ = db.Exec("VACUUM") } return db.Close() diff --git a/hscontrol/debug.go b/hscontrol/debug.go index 4fdcac11..93200b95 100644 --- a/hscontrol/debug.go +++ b/hscontrol/debug.go @@ -34,14 +34,14 @@ func (h *Headscale) debugHTTPServer() *http.Server { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - w.Write(overviewJSON) + _, _ = w.Write(overviewJSON) } else { // Default to text/plain for backward compatibility overview := h.state.DebugOverview() w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) - w.Write([]byte(overview)) + _, _ = w.Write([]byte(overview)) } })) @@ -57,7 +57,7 @@ func (h *Headscale) debugHTTPServer() *http.Server { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - w.Write(configJSON) + _, _ = w.Write(configJSON) })) // Policy endpoint @@ -77,7 +77,7 @@ func (h *Headscale) debugHTTPServer() *http.Server { } w.WriteHeader(http.StatusOK) - w.Write([]byte(policy)) + _, _ = w.Write([]byte(policy)) })) // Filter rules endpoint @@ -96,7 +96,7 @@ func (h *Headscale) debugHTTPServer() *http.Server { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - w.Write(filterJSON) + _, _ = w.Write(filterJSON) })) // SSH policies endpoint @@ -111,7 +111,7 @@ func (h *Headscale) debugHTTPServer() *http.Server { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - w.Write(sshJSON) + _, _ = w.Write(sshJSON) })) // DERP map endpoint @@ -131,14 +131,14 @@ func (h *Headscale) debugHTTPServer() *http.Server { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - w.Write(derpJSON) + _, _ = w.Write(derpJSON) } else { // Default to text/plain for backward compatibility derpInfo := h.state.DebugDERPMap() w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) - w.Write([]byte(derpInfo)) + _, _ = w.Write([]byte(derpInfo)) } })) @@ -159,14 +159,14 @@ func (h *Headscale) debugHTTPServer() *http.Server { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - w.Write(nodeStoreJSON) + _, _ = w.Write(nodeStoreJSON) } else { // Default to text/plain for backward compatibility nodeStoreInfo := h.state.DebugNodeStore() w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) - w.Write([]byte(nodeStoreInfo)) + _, _ = w.Write([]byte(nodeStoreInfo)) } })) @@ -182,7 +182,7 @@ func (h *Headscale) debugHTTPServer() *http.Server { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - w.Write(cacheJSON) + _, _ = w.Write(cacheJSON) })) // Routes endpoint @@ -202,14 +202,14 @@ func (h *Headscale) debugHTTPServer() *http.Server { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - w.Write(routesJSON) + _, _ = w.Write(routesJSON) } else { // Default to text/plain for backward compatibility routes := h.state.DebugRoutesString() w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) - w.Write([]byte(routes)) + _, _ = w.Write([]byte(routes)) } })) @@ -230,14 +230,14 @@ func (h *Headscale) debugHTTPServer() *http.Server { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - w.Write(policyManagerJSON) + _, _ = w.Write(policyManagerJSON) } else { // Default to text/plain for backward compatibility policyManagerInfo := h.state.DebugPolicyManager() w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) - w.Write([]byte(policyManagerInfo)) + _, _ = w.Write([]byte(policyManagerInfo)) } })) @@ -250,7 +250,7 @@ func (h *Headscale) debugHTTPServer() *http.Server { if res == nil { w.WriteHeader(http.StatusOK) - w.Write([]byte("HEADSCALE_DEBUG_DUMP_MAPRESPONSE_PATH not set")) + _, _ = w.Write([]byte("HEADSCALE_DEBUG_DUMP_MAPRESPONSE_PATH not set")) return } @@ -263,7 +263,7 @@ func (h *Headscale) debugHTTPServer() *http.Server { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - w.Write(resJSON) + _, _ = w.Write(resJSON) })) // Batcher endpoint @@ -283,14 +283,14 @@ func (h *Headscale) debugHTTPServer() *http.Server { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - w.Write(batcherJSON) + _, _ = w.Write(batcherJSON) } else { // Default to text/plain for backward compatibility batcherInfo := h.debugBatcher() w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) - w.Write([]byte(batcherInfo)) + _, _ = w.Write([]byte(batcherInfo)) } })) diff --git a/hscontrol/handlers.go b/hscontrol/handlers.go index 7ec26994..ef214536 100644 --- a/hscontrol/handlers.go +++ b/hscontrol/handlers.go @@ -154,7 +154,9 @@ func (h *Headscale) KeyHandler( } writer.Header().Set("Content-Type", "application/json") - json.NewEncoder(writer).Encode(resp) + if err := json.NewEncoder(writer).Encode(resp); err != nil { + log.Error().Err(err).Msg("failed to encode key response") + } return } @@ -179,7 +181,9 @@ func (h *Headscale) HealthHandler( res.Status = "fail" } - json.NewEncoder(writer).Encode(res) + if err := json.NewEncoder(writer).Encode(res); err != nil { + log.Error().Err(err).Msg("failed to encode health response") + } } err := h.state.PingDB(req.Context()) @@ -268,7 +272,7 @@ func (a *AuthProviderWeb) RegisterHandler( writer.Header().Set("Content-Type", "text/html; charset=utf-8") writer.WriteHeader(http.StatusOK) - writer.Write([]byte(templates.RegisterWeb(registrationId).Render())) + _, _ = writer.Write([]byte(templates.RegisterWeb(registrationId).Render())) } func FaviconHandler(writer http.ResponseWriter, req *http.Request) { diff --git a/hscontrol/mapper/batcher_lockfree.go b/hscontrol/mapper/batcher_lockfree.go index 918b7049..3ff3406b 100644 --- a/hscontrol/mapper/batcher_lockfree.go +++ b/hscontrol/mapper/batcher_lockfree.go @@ -526,7 +526,7 @@ type multiChannelNodeConn struct { // generateConnectionID generates a unique connection identifier. func generateConnectionID() string { bytes := make([]byte, 8) - rand.Read(bytes) + _, _ = rand.Read(bytes) return hex.EncodeToString(bytes) } diff --git a/hscontrol/mapper/batcher_test.go b/hscontrol/mapper/batcher_test.go index 595fb252..0ff03404 100644 --- a/hscontrol/mapper/batcher_test.go +++ b/hscontrol/mapper/batcher_test.go @@ -549,7 +549,7 @@ func TestEnhancedTrackingWithBatcher(t *testing.T) { testNode.start() // Connect the node to the batcher - batcher.AddNode(testNode.n.ID, testNode.ch, tailcfg.CapabilityVersion(100)) + _ = batcher.AddNode(testNode.n.ID, testNode.ch, tailcfg.CapabilityVersion(100)) // Wait for connection to be established assert.EventuallyWithT(t, func(c *assert.CollectT) { @@ -658,7 +658,7 @@ func TestBatcherScalabilityAllToAll(t *testing.T) { for i := range allNodes { node := &allNodes[i] - batcher.AddNode(node.n.ID, node.ch, tailcfg.CapabilityVersion(100)) + _ = batcher.AddNode(node.n.ID, node.ch, tailcfg.CapabilityVersion(100)) // Issue full update after each join to ensure connectivity batcher.AddWork(change.FullUpdate()) @@ -827,7 +827,7 @@ func TestBatcherBasicOperations(t *testing.T) { tn2 := testData.Nodes[1] // Test AddNode with real node ID - batcher.AddNode(tn.n.ID, tn.ch, 100) + _ = batcher.AddNode(tn.n.ID, tn.ch, 100) if !batcher.IsConnected(tn.n.ID) { t.Error("Node should be connected after AddNode") @@ -848,7 +848,7 @@ func TestBatcherBasicOperations(t *testing.T) { drainChannelTimeout(tn.ch, "first node before second", 100*time.Millisecond) // Add the second node and verify update message - batcher.AddNode(tn2.n.ID, tn2.ch, 100) + _ = batcher.AddNode(tn2.n.ID, tn2.ch, 100) assert.True(t, batcher.IsConnected(tn2.n.ID)) // First node should get an update that second node has connected. @@ -1053,7 +1053,7 @@ func TestBatcherWorkQueueBatching(t *testing.T) { testNodes := testData.Nodes ch := make(chan *tailcfg.MapResponse, 10) - batcher.AddNode(testNodes[0].n.ID, ch, tailcfg.CapabilityVersion(100)) + _ = batcher.AddNode(testNodes[0].n.ID, ch, tailcfg.CapabilityVersion(100)) // Track update content for validation var receivedUpdates []*tailcfg.MapResponse @@ -1157,7 +1157,7 @@ func XTestBatcherChannelClosingRace(t *testing.T) { ch1 := make(chan *tailcfg.MapResponse, 1) wg.Go(func() { - batcher.AddNode(testNode.n.ID, ch1, tailcfg.CapabilityVersion(100)) + _ = batcher.AddNode(testNode.n.ID, ch1, tailcfg.CapabilityVersion(100)) }) // Add real work during connection chaos @@ -1170,7 +1170,7 @@ func XTestBatcherChannelClosingRace(t *testing.T) { wg.Go(func() { runtime.Gosched() // Yield to introduce timing variability - batcher.AddNode(testNode.n.ID, ch2, tailcfg.CapabilityVersion(100)) + _ = batcher.AddNode(testNode.n.ID, ch2, tailcfg.CapabilityVersion(100)) }) // Remove second connection @@ -1261,7 +1261,7 @@ func TestBatcherWorkerChannelSafety(t *testing.T) { ch := make(chan *tailcfg.MapResponse, 5) // Add node and immediately queue real work - batcher.AddNode(testNode.n.ID, ch, tailcfg.CapabilityVersion(100)) + _ = batcher.AddNode(testNode.n.ID, ch, tailcfg.CapabilityVersion(100)) batcher.AddWork(change.DERPMap()) // Consumer goroutine to validate data and detect channel issues @@ -1384,7 +1384,7 @@ func TestBatcherConcurrentClients(t *testing.T) { for _, node := range stableNodes { ch := make(chan *tailcfg.MapResponse, NORMAL_BUFFER_SIZE) stableChannels[node.n.ID] = ch - batcher.AddNode(node.n.ID, ch, tailcfg.CapabilityVersion(100)) + _ = batcher.AddNode(node.n.ID, ch, tailcfg.CapabilityVersion(100)) // Monitor updates for each stable client go func(nodeID types.NodeID, channel chan *tailcfg.MapResponse) { @@ -1458,7 +1458,7 @@ func TestBatcherConcurrentClients(t *testing.T) { churningChannelsMutex.Unlock() - batcher.AddNode(nodeID, ch, tailcfg.CapabilityVersion(100)) + _ = batcher.AddNode(nodeID, ch, tailcfg.CapabilityVersion(100)) // Consume updates to prevent blocking go func() { @@ -1771,7 +1771,7 @@ func XTestBatcherScalability(t *testing.T) { for i := range testNodes { node := &testNodes[i] - batcher.AddNode(node.n.ID, node.ch, tailcfg.CapabilityVersion(100)) + _ = batcher.AddNode(node.n.ID, node.ch, tailcfg.CapabilityVersion(100)) connectedNodesMutex.Lock() connectedNodes[node.n.ID] = true @@ -2149,7 +2149,7 @@ func TestBatcherFullPeerUpdates(t *testing.T) { // Connect nodes one at a time and wait for each to be connected for i, node := range allNodes { - batcher.AddNode(node.n.ID, node.ch, tailcfg.CapabilityVersion(100)) + _ = batcher.AddNode(node.n.ID, node.ch, tailcfg.CapabilityVersion(100)) t.Logf("Connected node %d (ID: %d)", i, node.n.ID) // Wait for node to be connected diff --git a/hscontrol/platform_config.go b/hscontrol/platform_config.go index 23c4d25d..c8cc3fd4 100644 --- a/hscontrol/platform_config.go +++ b/hscontrol/platform_config.go @@ -19,7 +19,7 @@ func (h *Headscale) WindowsConfigMessage( ) { writer.Header().Set("Content-Type", "text/html; charset=utf-8") writer.WriteHeader(http.StatusOK) - writer.Write([]byte(templates.Windows(h.cfg.ServerURL).Render())) + _, _ = writer.Write([]byte(templates.Windows(h.cfg.ServerURL).Render())) } // AppleConfigMessage shows a simple message in the browser to point the user to the iOS/MacOS profile and instructions for how to install it. @@ -29,7 +29,7 @@ func (h *Headscale) AppleConfigMessage( ) { writer.Header().Set("Content-Type", "text/html; charset=utf-8") writer.WriteHeader(http.StatusOK) - writer.Write([]byte(templates.Apple(h.cfg.ServerURL).Render())) + _, _ = writer.Write([]byte(templates.Apple(h.cfg.ServerURL).Render())) } func (h *Headscale) ApplePlatformConfig( @@ -98,7 +98,7 @@ func (h *Headscale) ApplePlatformConfig( writer.Header(). Set("Content-Type", "application/x-apple-aspen-config; charset=utf-8") writer.WriteHeader(http.StatusOK) - writer.Write(content.Bytes()) + _, _ = writer.Write(content.Bytes()) } type AppleMobileConfig struct { diff --git a/hscontrol/util/prompt.go b/hscontrol/util/prompt.go index 7d9cdbdf..410f6c2e 100644 --- a/hscontrol/util/prompt.go +++ b/hscontrol/util/prompt.go @@ -14,7 +14,7 @@ func YesNo(msg string) bool { fmt.Fprint(os.Stderr, msg+" [y/n] ") var resp string - fmt.Scanln(&resp) + _, _ = fmt.Scanln(&resp) resp = strings.ToLower(resp) switch resp { diff --git a/hscontrol/util/prompt_test.go b/hscontrol/util/prompt_test.go index fbed2ff8..c6fcb702 100644 --- a/hscontrol/util/prompt_test.go +++ b/hscontrol/util/prompt_test.go @@ -87,7 +87,7 @@ func TestYesNo(t *testing.T) { go func() { defer w.Close() - w.WriteString(tt.input) + _, _ = w.WriteString(tt.input) }() // Call the function @@ -106,7 +106,7 @@ func TestYesNo(t *testing.T) { // Check that the prompt was written to stderr var stderrBuf bytes.Buffer - io.Copy(&stderrBuf, stderrR) + _, _ = io.Copy(&stderrBuf, stderrR) stderrR.Close() expectedPrompt := "Test question [y/n] " @@ -134,7 +134,7 @@ func TestYesNoPromptMessage(t *testing.T) { go func() { defer w.Close() - w.WriteString("n\n") + _, _ = w.WriteString("n\n") }() // Call the function with a custom message @@ -149,7 +149,7 @@ func TestYesNoPromptMessage(t *testing.T) { // Check that the custom message was included in the prompt var stderrBuf bytes.Buffer - io.Copy(&stderrBuf, stderrR) + _, _ = io.Copy(&stderrBuf, stderrR) stderrR.Close() expectedPrompt := customMessage + " [y/n] " @@ -193,7 +193,7 @@ func TestYesNoCaseInsensitive(t *testing.T) { go func() { defer w.Close() - w.WriteString(tc.input) + _, _ = w.WriteString(tc.input) }() // Call the function @@ -206,7 +206,7 @@ func TestYesNoCaseInsensitive(t *testing.T) { stderrW.Close() // Drain stderr - io.Copy(io.Discard, stderrR) + _, _ = io.Copy(io.Discard, stderrR) stderrR.Close() if result != tc.expected { diff --git a/integration/auth_web_flow_test.go b/integration/auth_web_flow_test.go index a102b493..256d7e4d 100644 --- a/integration/auth_web_flow_test.go +++ b/integration/auth_web_flow_test.go @@ -308,7 +308,10 @@ func TestAuthWebFlowLogoutAndReloginNewUser(t *testing.T) { // Register all clients as user1 (this is where cross-user registration happens) // This simulates: headscale nodes register --user user1 --key - scenario.runHeadscaleRegister("user1", body) + err = scenario.runHeadscaleRegister("user1", body) + if err != nil { + t.Fatalf("failed to register client %s: %s", client.Hostname(), err) + } } // Wait for all clients to reach running state diff --git a/integration/dns_test.go b/integration/dns_test.go index e937a421..3432eb9b 100644 --- a/integration/dns_test.go +++ b/integration/dns_test.go @@ -93,7 +93,8 @@ func TestResolveMagicDNSExtraRecordsPath(t *testing.T) { Value: "6.6.6.6", }, } - b, _ := json.Marshal(extraRecords) + b, err := json.Marshal(extraRecords) + require.NoError(t, err) err = scenario.CreateHeadscaleEnv([]tsic.Option{ tsic.WithPackages("python3", "curl", "bind-tools"), @@ -133,13 +134,14 @@ func TestResolveMagicDNSExtraRecordsPath(t *testing.T) { require.NoError(t, err) // Write the file directly into place from the docker API. - b0, _ := json.Marshal([]tailcfg.DNSRecord{ + b0, err := json.Marshal([]tailcfg.DNSRecord{ { Name: "docker.myvpn.example.com", Type: "A", Value: "2.2.2.2", }, }) + require.NoError(t, err) err = hs.WriteFile(erPath, b0) require.NoError(t, err) @@ -155,7 +157,8 @@ func TestResolveMagicDNSExtraRecordsPath(t *testing.T) { Type: "A", Value: "7.7.7.7", }) - b2, _ := json.Marshal(extraRecords) + b2, err := json.Marshal(extraRecords) + require.NoError(t, err) err = hs.WriteFile(erPath+"2", b2) require.NoError(t, err) @@ -169,13 +172,14 @@ func TestResolveMagicDNSExtraRecordsPath(t *testing.T) { // Write a new file and copy it to the path to ensure the reload // works when a file is copied into place. - b3, _ := json.Marshal([]tailcfg.DNSRecord{ + b3, err := json.Marshal([]tailcfg.DNSRecord{ { Name: "copy.myvpn.example.com", Type: "A", Value: "8.8.8.8", }, }) + require.NoError(t, err) err = hs.WriteFile(erPath+"3", b3) require.NoError(t, err) @@ -187,13 +191,14 @@ func TestResolveMagicDNSExtraRecordsPath(t *testing.T) { } // Write in place to ensure pipe like behaviour works - b4, _ := json.Marshal([]tailcfg.DNSRecord{ + b4, err := json.Marshal([]tailcfg.DNSRecord{ { Name: "docker.myvpn.example.com", Type: "A", Value: "9.9.9.9", }, }) + require.NoError(t, err) command := []string{"echo", fmt.Sprintf("'%s'", string(b4)), ">", erPath} _, err = hs.Execute([]string{"bash", "-c", strings.Join(command, " ")}) require.NoError(t, err) diff --git a/integration/scenario.go b/integration/scenario.go index e209f6ea..0b388c0a 100644 --- a/integration/scenario.go +++ b/integration/scenario.go @@ -169,8 +169,8 @@ func NewScenario(spec ScenarioSpec) (*Scenario, error) { // Opportunity to clean up unreferenced networks. // This might be a no op, but it is worth a try as we sometime // dont clean up nicely after ourselves. - dockertestutil.CleanUnreferencedNetworks(pool) - dockertestutil.CleanImagesInCI(pool) + _ = dockertestutil.CleanUnreferencedNetworks(pool) + _ = dockertestutil.CleanImagesInCI(pool) if spec.MaxWait == 0 { pool.MaxWait = dockertestMaxWait() @@ -314,8 +314,8 @@ func (s *Scenario) Services(name string) ([]*dockertest.Resource, error) { } func (s *Scenario) ShutdownAssertNoPanics(t *testing.T) { - defer dockertestutil.CleanUnreferencedNetworks(s.pool) - defer dockertestutil.CleanImagesInCI(s.pool) + defer func() { _ = dockertestutil.CleanUnreferencedNetworks(s.pool) }() + defer func() { _ = dockertestutil.CleanImagesInCI(s.pool) }() s.controlServers.Range(func(_ string, control ControlServer) bool { stdoutPath, stderrPath, err := control.Shutdown() @@ -920,7 +920,7 @@ func (s *Scenario) RunTailscaleUpWithURL(userStr, loginServer string) error { // If the URL is not a OIDC URL, then we need to // run the register command to fully log in the client. if !strings.Contains(loginURL.String(), "/oidc/") { - s.runHeadscaleRegister(userStr, body) + _ = s.runHeadscaleRegister(userStr, body) } return nil