all: check unchecked error returns

Fix errcheck and errchkjson lint issues by properly handling
or explicitly discarding error return values.

Changes include:
- cmd/headscale/cli: Check MarkFlagRequired and AddMiddleware errors
- hscontrol/db: Check VACUUM exec result
- hscontrol/debug: Check Write errors in HTTP handlers
- hscontrol/handlers: Add error logging for JSON encoding
- hscontrol/mapper: Check AddNode errors in tests
- hscontrol/platform_config: Check Write error
- hscontrol/util/prompt: Check Scanln and Write errors
- integration: Check json.Marshal and cleanup function errors
This commit is contained in:
Kristoffer Dalby 2026-01-20 14:52:28 +00:00
parent 15d0efbf9d
commit 52fc725cf1
14 changed files with 73 additions and 61 deletions

View file

@ -134,7 +134,7 @@ func getMockOIDC(clientID string, clientSecret string, users []mockoidc.MockUser
ErrorQueue: &mockoidc.ErrorQueue{}, 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) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Info().Msgf("Request: %+v", r) log.Info().Msgf("Request: %+v", r)
h.ServeHTTP(w, r) h.ServeHTTP(w, r)

View file

@ -72,12 +72,12 @@ func init() {
nodeCmd.AddCommand(deleteNodeCmd) nodeCmd.AddCommand(deleteNodeCmd)
tagCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)") 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") tagCmd.Flags().StringSliceP("tags", "t", []string{}, "List of tags to add to the node")
nodeCmd.AddCommand(tagCmd) nodeCmd.AddCommand(tagCmd)
approveRoutesCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)") 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)`) 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) nodeCmd.AddCommand(approveRoutesCmd)

View file

@ -51,7 +51,7 @@ func init() {
userCmd.AddCommand(renameUserCmd) userCmd.AddCommand(renameUserCmd)
usernameAndIDFlag(renameUserCmd) usernameAndIDFlag(renameUserCmd)
renameUserCmd.Flags().StringP("new-name", "r", "", "New username") renameUserCmd.Flags().StringP("new-name", "r", "", "New username")
renameNodeCmd.MarkFlagRequired("new-name") _ = renameUserCmd.MarkFlagRequired("new-name")
} }
var errMissingParameter = errors.New("missing parameters") var errMissingParameter = errors.New("missing parameters")

View file

@ -1035,7 +1035,7 @@ func (hsdb *HSDatabase) Close() error {
} }
if hsdb.cfg.Database.Type == types.DatabaseSqlite && hsdb.cfg.Database.Sqlite.WriteAheadLog { if hsdb.cfg.Database.Type == types.DatabaseSqlite && hsdb.cfg.Database.Sqlite.WriteAheadLog {
db.Exec("VACUUM") _, _ = db.Exec("VACUUM")
} }
return db.Close() return db.Close()

View file

@ -34,14 +34,14 @@ func (h *Headscale) debugHTTPServer() *http.Server {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write(overviewJSON) _, _ = w.Write(overviewJSON)
} else { } else {
// Default to text/plain for backward compatibility // Default to text/plain for backward compatibility
overview := h.state.DebugOverview() overview := h.state.DebugOverview()
w.Header().Set("Content-Type", "text/plain") w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK) 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.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write(configJSON) _, _ = w.Write(configJSON)
})) }))
// Policy endpoint // Policy endpoint
@ -77,7 +77,7 @@ func (h *Headscale) debugHTTPServer() *http.Server {
} }
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write([]byte(policy)) _, _ = w.Write([]byte(policy))
})) }))
// Filter rules endpoint // Filter rules endpoint
@ -96,7 +96,7 @@ func (h *Headscale) debugHTTPServer() *http.Server {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write(filterJSON) _, _ = w.Write(filterJSON)
})) }))
// SSH policies endpoint // SSH policies endpoint
@ -111,7 +111,7 @@ func (h *Headscale) debugHTTPServer() *http.Server {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write(sshJSON) _, _ = w.Write(sshJSON)
})) }))
// DERP map endpoint // DERP map endpoint
@ -131,14 +131,14 @@ func (h *Headscale) debugHTTPServer() *http.Server {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write(derpJSON) _, _ = w.Write(derpJSON)
} else { } else {
// Default to text/plain for backward compatibility // Default to text/plain for backward compatibility
derpInfo := h.state.DebugDERPMap() derpInfo := h.state.DebugDERPMap()
w.Header().Set("Content-Type", "text/plain") w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK) 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.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write(nodeStoreJSON) _, _ = w.Write(nodeStoreJSON)
} else { } else {
// Default to text/plain for backward compatibility // Default to text/plain for backward compatibility
nodeStoreInfo := h.state.DebugNodeStore() nodeStoreInfo := h.state.DebugNodeStore()
w.Header().Set("Content-Type", "text/plain") w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK) 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.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write(cacheJSON) _, _ = w.Write(cacheJSON)
})) }))
// Routes endpoint // Routes endpoint
@ -202,14 +202,14 @@ func (h *Headscale) debugHTTPServer() *http.Server {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write(routesJSON) _, _ = w.Write(routesJSON)
} else { } else {
// Default to text/plain for backward compatibility // Default to text/plain for backward compatibility
routes := h.state.DebugRoutesString() routes := h.state.DebugRoutesString()
w.Header().Set("Content-Type", "text/plain") w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK) 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.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write(policyManagerJSON) _, _ = w.Write(policyManagerJSON)
} else { } else {
// Default to text/plain for backward compatibility // Default to text/plain for backward compatibility
policyManagerInfo := h.state.DebugPolicyManager() policyManagerInfo := h.state.DebugPolicyManager()
w.Header().Set("Content-Type", "text/plain") w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK) 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 { if res == nil {
w.WriteHeader(http.StatusOK) 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 return
} }
@ -263,7 +263,7 @@ func (h *Headscale) debugHTTPServer() *http.Server {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write(resJSON) _, _ = w.Write(resJSON)
})) }))
// Batcher endpoint // Batcher endpoint
@ -283,14 +283,14 @@ func (h *Headscale) debugHTTPServer() *http.Server {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write(batcherJSON) _, _ = w.Write(batcherJSON)
} else { } else {
// Default to text/plain for backward compatibility // Default to text/plain for backward compatibility
batcherInfo := h.debugBatcher() batcherInfo := h.debugBatcher()
w.Header().Set("Content-Type", "text/plain") w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write([]byte(batcherInfo)) _, _ = w.Write([]byte(batcherInfo))
} }
})) }))

View file

@ -154,7 +154,9 @@ func (h *Headscale) KeyHandler(
} }
writer.Header().Set("Content-Type", "application/json") 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 return
} }
@ -179,7 +181,9 @@ func (h *Headscale) HealthHandler(
res.Status = "fail" 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()) 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.Header().Set("Content-Type", "text/html; charset=utf-8")
writer.WriteHeader(http.StatusOK) 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) { func FaviconHandler(writer http.ResponseWriter, req *http.Request) {

View file

@ -526,7 +526,7 @@ type multiChannelNodeConn struct {
// generateConnectionID generates a unique connection identifier. // generateConnectionID generates a unique connection identifier.
func generateConnectionID() string { func generateConnectionID() string {
bytes := make([]byte, 8) bytes := make([]byte, 8)
rand.Read(bytes) _, _ = rand.Read(bytes)
return hex.EncodeToString(bytes) return hex.EncodeToString(bytes)
} }

View file

@ -549,7 +549,7 @@ func TestEnhancedTrackingWithBatcher(t *testing.T) {
testNode.start() testNode.start()
// Connect the node to the batcher // 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 // Wait for connection to be established
assert.EventuallyWithT(t, func(c *assert.CollectT) { assert.EventuallyWithT(t, func(c *assert.CollectT) {
@ -658,7 +658,7 @@ func TestBatcherScalabilityAllToAll(t *testing.T) {
for i := range allNodes { for i := range allNodes {
node := &allNodes[i] 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 // Issue full update after each join to ensure connectivity
batcher.AddWork(change.FullUpdate()) batcher.AddWork(change.FullUpdate())
@ -827,7 +827,7 @@ func TestBatcherBasicOperations(t *testing.T) {
tn2 := testData.Nodes[1] tn2 := testData.Nodes[1]
// Test AddNode with real node ID // 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) { if !batcher.IsConnected(tn.n.ID) {
t.Error("Node should be connected after AddNode") 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) drainChannelTimeout(tn.ch, "first node before second", 100*time.Millisecond)
// Add the second node and verify update message // 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)) assert.True(t, batcher.IsConnected(tn2.n.ID))
// First node should get an update that second node has connected. // First node should get an update that second node has connected.
@ -1053,7 +1053,7 @@ func TestBatcherWorkQueueBatching(t *testing.T) {
testNodes := testData.Nodes testNodes := testData.Nodes
ch := make(chan *tailcfg.MapResponse, 10) 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 // Track update content for validation
var receivedUpdates []*tailcfg.MapResponse var receivedUpdates []*tailcfg.MapResponse
@ -1157,7 +1157,7 @@ func XTestBatcherChannelClosingRace(t *testing.T) {
ch1 := make(chan *tailcfg.MapResponse, 1) ch1 := make(chan *tailcfg.MapResponse, 1)
wg.Go(func() { 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 // Add real work during connection chaos
@ -1170,7 +1170,7 @@ func XTestBatcherChannelClosingRace(t *testing.T) {
wg.Go(func() { wg.Go(func() {
runtime.Gosched() // Yield to introduce timing variability 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 // Remove second connection
@ -1261,7 +1261,7 @@ func TestBatcherWorkerChannelSafety(t *testing.T) {
ch := make(chan *tailcfg.MapResponse, 5) ch := make(chan *tailcfg.MapResponse, 5)
// Add node and immediately queue real work // 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()) batcher.AddWork(change.DERPMap())
// Consumer goroutine to validate data and detect channel issues // Consumer goroutine to validate data and detect channel issues
@ -1384,7 +1384,7 @@ func TestBatcherConcurrentClients(t *testing.T) {
for _, node := range stableNodes { for _, node := range stableNodes {
ch := make(chan *tailcfg.MapResponse, NORMAL_BUFFER_SIZE) ch := make(chan *tailcfg.MapResponse, NORMAL_BUFFER_SIZE)
stableChannels[node.n.ID] = ch 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 // Monitor updates for each stable client
go func(nodeID types.NodeID, channel chan *tailcfg.MapResponse) { go func(nodeID types.NodeID, channel chan *tailcfg.MapResponse) {
@ -1458,7 +1458,7 @@ func TestBatcherConcurrentClients(t *testing.T) {
churningChannelsMutex.Unlock() churningChannelsMutex.Unlock()
batcher.AddNode(nodeID, ch, tailcfg.CapabilityVersion(100)) _ = batcher.AddNode(nodeID, ch, tailcfg.CapabilityVersion(100))
// Consume updates to prevent blocking // Consume updates to prevent blocking
go func() { go func() {
@ -1771,7 +1771,7 @@ func XTestBatcherScalability(t *testing.T) {
for i := range testNodes { for i := range testNodes {
node := &testNodes[i] 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() connectedNodesMutex.Lock()
connectedNodes[node.n.ID] = true 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 // Connect nodes one at a time and wait for each to be connected
for i, node := range allNodes { 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) t.Logf("Connected node %d (ID: %d)", i, node.n.ID)
// Wait for node to be connected // Wait for node to be connected

View file

@ -19,7 +19,7 @@ func (h *Headscale) WindowsConfigMessage(
) { ) {
writer.Header().Set("Content-Type", "text/html; charset=utf-8") writer.Header().Set("Content-Type", "text/html; charset=utf-8")
writer.WriteHeader(http.StatusOK) 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. // 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.Header().Set("Content-Type", "text/html; charset=utf-8")
writer.WriteHeader(http.StatusOK) 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( func (h *Headscale) ApplePlatformConfig(
@ -98,7 +98,7 @@ func (h *Headscale) ApplePlatformConfig(
writer.Header(). writer.Header().
Set("Content-Type", "application/x-apple-aspen-config; charset=utf-8") Set("Content-Type", "application/x-apple-aspen-config; charset=utf-8")
writer.WriteHeader(http.StatusOK) writer.WriteHeader(http.StatusOK)
writer.Write(content.Bytes()) _, _ = writer.Write(content.Bytes())
} }
type AppleMobileConfig struct { type AppleMobileConfig struct {

View file

@ -14,7 +14,7 @@ func YesNo(msg string) bool {
fmt.Fprint(os.Stderr, msg+" [y/n] ") fmt.Fprint(os.Stderr, msg+" [y/n] ")
var resp string var resp string
fmt.Scanln(&resp) _, _ = fmt.Scanln(&resp)
resp = strings.ToLower(resp) resp = strings.ToLower(resp)
switch resp { switch resp {

View file

@ -87,7 +87,7 @@ func TestYesNo(t *testing.T) {
go func() { go func() {
defer w.Close() defer w.Close()
w.WriteString(tt.input) _, _ = w.WriteString(tt.input)
}() }()
// Call the function // Call the function
@ -106,7 +106,7 @@ func TestYesNo(t *testing.T) {
// Check that the prompt was written to stderr // Check that the prompt was written to stderr
var stderrBuf bytes.Buffer var stderrBuf bytes.Buffer
io.Copy(&stderrBuf, stderrR) _, _ = io.Copy(&stderrBuf, stderrR)
stderrR.Close() stderrR.Close()
expectedPrompt := "Test question [y/n] " expectedPrompt := "Test question [y/n] "
@ -134,7 +134,7 @@ func TestYesNoPromptMessage(t *testing.T) {
go func() { go func() {
defer w.Close() defer w.Close()
w.WriteString("n\n") _, _ = w.WriteString("n\n")
}() }()
// Call the function with a custom message // 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 // Check that the custom message was included in the prompt
var stderrBuf bytes.Buffer var stderrBuf bytes.Buffer
io.Copy(&stderrBuf, stderrR) _, _ = io.Copy(&stderrBuf, stderrR)
stderrR.Close() stderrR.Close()
expectedPrompt := customMessage + " [y/n] " expectedPrompt := customMessage + " [y/n] "
@ -193,7 +193,7 @@ func TestYesNoCaseInsensitive(t *testing.T) {
go func() { go func() {
defer w.Close() defer w.Close()
w.WriteString(tc.input) _, _ = w.WriteString(tc.input)
}() }()
// Call the function // Call the function
@ -206,7 +206,7 @@ func TestYesNoCaseInsensitive(t *testing.T) {
stderrW.Close() stderrW.Close()
// Drain stderr // Drain stderr
io.Copy(io.Discard, stderrR) _, _ = io.Copy(io.Discard, stderrR)
stderrR.Close() stderrR.Close()
if result != tc.expected { if result != tc.expected {

View file

@ -308,7 +308,10 @@ func TestAuthWebFlowLogoutAndReloginNewUser(t *testing.T) {
// Register all clients as user1 (this is where cross-user registration happens) // Register all clients as user1 (this is where cross-user registration happens)
// This simulates: headscale nodes register --user user1 --key <key> // This simulates: headscale nodes register --user user1 --key <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 // Wait for all clients to reach running state

View file

@ -93,7 +93,8 @@ func TestResolveMagicDNSExtraRecordsPath(t *testing.T) {
Value: "6.6.6.6", Value: "6.6.6.6",
}, },
} }
b, _ := json.Marshal(extraRecords) b, err := json.Marshal(extraRecords)
require.NoError(t, err)
err = scenario.CreateHeadscaleEnv([]tsic.Option{ err = scenario.CreateHeadscaleEnv([]tsic.Option{
tsic.WithPackages("python3", "curl", "bind-tools"), tsic.WithPackages("python3", "curl", "bind-tools"),
@ -133,13 +134,14 @@ func TestResolveMagicDNSExtraRecordsPath(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// Write the file directly into place from the docker API. // 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", Name: "docker.myvpn.example.com",
Type: "A", Type: "A",
Value: "2.2.2.2", Value: "2.2.2.2",
}, },
}) })
require.NoError(t, err)
err = hs.WriteFile(erPath, b0) err = hs.WriteFile(erPath, b0)
require.NoError(t, err) require.NoError(t, err)
@ -155,7 +157,8 @@ func TestResolveMagicDNSExtraRecordsPath(t *testing.T) {
Type: "A", Type: "A",
Value: "7.7.7.7", Value: "7.7.7.7",
}) })
b2, _ := json.Marshal(extraRecords) b2, err := json.Marshal(extraRecords)
require.NoError(t, err)
err = hs.WriteFile(erPath+"2", b2) err = hs.WriteFile(erPath+"2", b2)
require.NoError(t, err) 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 // Write a new file and copy it to the path to ensure the reload
// works when a file is copied into place. // works when a file is copied into place.
b3, _ := json.Marshal([]tailcfg.DNSRecord{ b3, err := json.Marshal([]tailcfg.DNSRecord{
{ {
Name: "copy.myvpn.example.com", Name: "copy.myvpn.example.com",
Type: "A", Type: "A",
Value: "8.8.8.8", Value: "8.8.8.8",
}, },
}) })
require.NoError(t, err)
err = hs.WriteFile(erPath+"3", b3) err = hs.WriteFile(erPath+"3", b3)
require.NoError(t, err) require.NoError(t, err)
@ -187,13 +191,14 @@ func TestResolveMagicDNSExtraRecordsPath(t *testing.T) {
} }
// Write in place to ensure pipe like behaviour works // 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", Name: "docker.myvpn.example.com",
Type: "A", Type: "A",
Value: "9.9.9.9", Value: "9.9.9.9",
}, },
}) })
require.NoError(t, err)
command := []string{"echo", fmt.Sprintf("'%s'", string(b4)), ">", erPath} command := []string{"echo", fmt.Sprintf("'%s'", string(b4)), ">", erPath}
_, err = hs.Execute([]string{"bash", "-c", strings.Join(command, " ")}) _, err = hs.Execute([]string{"bash", "-c", strings.Join(command, " ")})
require.NoError(t, err) require.NoError(t, err)

View file

@ -169,8 +169,8 @@ func NewScenario(spec ScenarioSpec) (*Scenario, error) {
// Opportunity to clean up unreferenced networks. // Opportunity to clean up unreferenced networks.
// This might be a no op, but it is worth a try as we sometime // This might be a no op, but it is worth a try as we sometime
// dont clean up nicely after ourselves. // dont clean up nicely after ourselves.
dockertestutil.CleanUnreferencedNetworks(pool) _ = dockertestutil.CleanUnreferencedNetworks(pool)
dockertestutil.CleanImagesInCI(pool) _ = dockertestutil.CleanImagesInCI(pool)
if spec.MaxWait == 0 { if spec.MaxWait == 0 {
pool.MaxWait = dockertestMaxWait() pool.MaxWait = dockertestMaxWait()
@ -314,8 +314,8 @@ func (s *Scenario) Services(name string) ([]*dockertest.Resource, error) {
} }
func (s *Scenario) ShutdownAssertNoPanics(t *testing.T) { func (s *Scenario) ShutdownAssertNoPanics(t *testing.T) {
defer dockertestutil.CleanUnreferencedNetworks(s.pool) defer func() { _ = dockertestutil.CleanUnreferencedNetworks(s.pool) }()
defer dockertestutil.CleanImagesInCI(s.pool) defer func() { _ = dockertestutil.CleanImagesInCI(s.pool) }()
s.controlServers.Range(func(_ string, control ControlServer) bool { s.controlServers.Range(func(_ string, control ControlServer) bool {
stdoutPath, stderrPath, err := control.Shutdown() 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 // If the URL is not a OIDC URL, then we need to
// run the register command to fully log in the client. // run the register command to fully log in the client.
if !strings.Contains(loginURL.String(), "/oidc/") { if !strings.Contains(loginURL.String(), "/oidc/") {
s.runHeadscaleRegister(userStr, body) _ = s.runHeadscaleRegister(userStr, body)
} }
return nil return nil