Disable the thelper linter which triggers on inline test closures in
table-driven tests. These closures are intentionally not standalone
helpers and don't benefit from t.Helper().
Also remove explanatory comments from nolint directives throughout the
codebase as they add noise without providing significant value.
- Fix godoclint: Ensure doc comments start with symbol name
- Fix gosec: Add nolint directives for false positives (G101, G110, G115, G306, G404)
- Fix testifylint: Use require instead of assert for error checks
- Fix thelper: Add t.Helper() to test helper functions
- Auto-fix gci: Format import statements
- Add nolint:staticcheck for SA1019 deprecation warnings on types.Route
(kept for GORM migrations only, intentionally deprecated)
- Add nolint:staticcheck for SA4006 false positives where variables are
used inside new() expressions which staticcheck doesn't recognize
- Fix SA5011 potential nil pointer dereferences in util_test.go by
using t.Fatal instead of t.Error for nil checks
- Add nolint:contextcheck for functions where context propagation would
require significant architectural changes (Docker client creation,
OIDC initialization, scheduled tasks, etc.)
- Replace exp/maps and exp/slices with stdlib (Go 1.21+)
- Fix ineffective assignments by checking errors or using blank identifier
- Remove unnecessary type conversions
- Use integer range syntax for loops (Go 1.22+)
- Rename variables shadowing predeclared identifiers (min, max)
Apply additional golangci-lint auto-fixes (wsl_v5, formatting)
and update SSH policy test error message expectations to match
the new sentinel error formats introduced in the err113 fixes.
Define named constants for various timeout and configuration values:
- Connection validation and retry timeouts in helpers
- Peer sync timeouts in integrationutil
- Run ID hash length and parts in dockertestutil
- Container memory limits and directory permissions
- HTML parsing split count in scenario
- Container restart and backoff timeouts in tsic
- Stats calculation constants in cmd/hi
Apply auto-fixes from golangci-lint for the following linters:
- wsl_v5: whitespace formatting and blank line adjustments
- godot: add periods to comment sentences
- nlreturn: add newlines before return statements
- perfsprint: optimize fmt.Sprintf to more efficient alternatives
Also add missing imports (errors, encoding/hex) where auto-fix
added new code patterns that require them.
Replace errors.As with the new errors.AsType generic function
introduced in Go 1.26. This provides compile-time type safety
and approximately 3x better performance by avoiding reflection.
Before:
var target *AppError
if errors.As(err, &target) {
// use target
}
After:
if target, ok := errors.AsType[*AppError](err); ok {
// use target
}
Refactor the RequestTags migration (202601121700-migrate-hostinfo-request-tags)
to use PolicyManager.NodeCanHaveTag() instead of reimplementing tag validation.
Changes:
- NewHeadscaleDatabase now accepts *types.Config to allow migrations
access to policy configuration
- Add loadPolicyBytes helper to load policy from file or DB based on config
- Add standalone GetPolicy(tx *gorm.DB) for use during migrations
- Replace custom tag validation logic with PolicyManager
Benefits:
- Full HuJSON parsing support (not just JSON)
- Proper group expansion via PolicyManager
- Support for nested tags and autogroups
- Works with both file and database policy modes
- Single source of truth for tag validation
Co-Authored-By: Shourya Gautam <shouryamgautam@gmail.com>
Add --id flag as an alternative to --prefix for expiring and
deleting API keys. This allows users to use the ID shown in
'headscale apikeys list' output, which is more convenient than
the prefix.
Either --id or --prefix must be provided; both flags are optional
but at least one is required.
Updates #2986
Convert config loading tests from gopkg.in/check.v1 Suite-based testing
to standard Go tests with testify assert/require.
Changes:
- Remove Suite boilerplate (Test, Suite type, SetUpSuite, TearDownSuite)
- Convert TestConfigFileLoading and TestConfigLoading to standalone tests
- Replace check assertions with testify equivalents
Replace the Tags column with an Owner column that displays:
- Tags (newline-separated) if the key has ACL tags
- User name if the key is associated with a user
- Dash (-) if neither is present
This aligns the CLI output with the tags-as-identity model where
preauthkeys can be created with either tags or user ownership.
Remove the concurrent test prevention logic and update cleanup to use
run ID-based isolation, allowing multiple tests to run simultaneously.
Changes:
- cleanup: Add killTestContainersByRunID() to clean only containers
belonging to a specific run, add cleanupStaleTestContainers() to
remove only stopped/exited containers without affecting running tests
- docker: Remove RunningTestInfo, checkForRunningTests(), and related
error types, update cleanupAfterTest() to use run ID-based cleanup
- run: Remove Force flag and concurrent test prevention check
The test runner now:
- Allows multiple concurrent test runs on the same Docker daemon
- Cleans only stale containers before tests (not running ones)
- Cleans only containers with matching run ID after tests
- Prints run ID and monitoring info for operator visibility
This PR restructures the integration tests and prebuilds all common assets used in all tests:
Headscale and Tailscale HEAD image
hi binary that is used to run tests
go cache is warmed up for compilation of the test
This essentially means we spend 6-10 minutes building assets before any tests starts, when that is done, all tests can just sprint through.
It looks like we are saving 3-9 minutes per test, and since we are limited to running max 20 concurrent tests across the repo, that means we had a lot of double work.
There is currently 113 checks, so we have to do five runs of 20, and the saving should be quite noticeable! I think the "worst case" saving would be 20+min and "best case" probably towards an hour.
Fixes#2927
In v0.27.0, the list-routes command with -i flag and -o json output
was returning all nodes instead of just the specified node.
The issue was that JSON output was happening before the identifier
filtering logic. This change moves the JSON output to after both
the identifier filter and route existence filter are applied,
ensuring the correct filtered results are returned.
This restores the v0.26.1 behavior where:
headscale nodes list-routes -i 12 -o json
correctly returns only node 12's route information.
Initial work on a nodestore which stores all of the nodes
and their relations in memory with relationship for peers
precalculated.
It is a copy-on-write structure, replacing the "snapshot"
when a change to the structure occurs. It is optimised for reads,
and while batches are not fast, they are grouped together
to do less of the expensive peer calculation if there are many
changes rapidly.
Writes will block until commited, while reads are never
blocked.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>