all: use new() builtin and slices utilities

Modernize code to use Go 1.26 features:

1. Replace ptr.To with the new() builtin:
   Before: ptr.To(value)
   After:  new(value)

2. Replace append clone pattern with slices.Clone:
   Before: copy := append([]T{}, slice...)
   After:  copy := slices.Clone(slice)

3. Replace manual contains loops with slices.Contains:
   Before: for _, v := range slice { if v == target { return true } }
   After:  slices.Contains(slice, target)

The ptr.To function from tailscale.com/types/ptr is no longer
needed as Go 1.26's enhanced new() builtin accepts a value
argument and returns a pointer to a copy of that value.

Note: Auto-generated files (types_clone.go) are not modified
as they are generated by tailscale.com/cmd/cloner.
This commit is contained in:
Kristoffer Dalby 2026-01-20 14:23:21 +00:00
parent 094faf7a6a
commit 3675b65504
22 changed files with 324 additions and 341 deletions

View file

@ -13,7 +13,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"tailscale.com/net/tsaddr"
"tailscale.com/types/ptr"
)
var mpp = func(pref string) *netip.Prefix {
@ -488,8 +487,8 @@ func TestIPAllocatorNextNoReservedIPs(t *testing.T) {
alloc, err := NewIPAllocator(
db,
ptr.To(tsaddr.CGNATRange()),
ptr.To(tsaddr.TailscaleULARange()),
new(tsaddr.CGNATRange()),
new(tsaddr.TailscaleULARange()),
types.IPAllocationStrategySequential,
)
if err != nil {
@ -497,17 +496,17 @@ func TestIPAllocatorNextNoReservedIPs(t *testing.T) {
}
// Validate that we do not give out 100.100.100.100
nextQuad100, err := alloc.next(na("100.100.100.99"), ptr.To(tsaddr.CGNATRange()))
nextQuad100, err := alloc.next(na("100.100.100.99"), new(tsaddr.CGNATRange()))
require.NoError(t, err)
assert.Equal(t, na("100.100.100.101"), *nextQuad100)
// Validate that we do not give out fd7a:115c:a1e0::53
nextQuad100v6, err := alloc.next(na("fd7a:115c:a1e0::52"), ptr.To(tsaddr.TailscaleULARange()))
nextQuad100v6, err := alloc.next(na("fd7a:115c:a1e0::52"), new(tsaddr.TailscaleULARange()))
require.NoError(t, err)
assert.Equal(t, na("fd7a:115c:a1e0::54"), *nextQuad100v6)
// Validate that we do not give out fd7a:115c:a1e0::53
nextChrome, err := alloc.next(na("100.115.91.255"), ptr.To(tsaddr.CGNATRange()))
nextChrome, err := alloc.next(na("100.115.91.255"), new(tsaddr.CGNATRange()))
t.Logf("chrome: %s", nextChrome.String())
require.NoError(t, err)
assert.Equal(t, na("100.115.94.0"), *nextChrome)

View file

@ -22,7 +22,6 @@ import (
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/types/ptr"
)
func TestGetNode(t *testing.T) {
@ -115,7 +114,7 @@ func TestExpireNode(t *testing.T) {
Hostname: "testnode",
UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID),
AuthKeyID: new(pak.ID),
Expiry: &time.Time{},
}
db.DB.Save(node)
@ -159,7 +158,7 @@ func TestSetTags(t *testing.T) {
Hostname: "testnode",
UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID),
AuthKeyID: new(pak.ID),
}
trx := db.DB.Save(node)
@ -443,7 +442,7 @@ func TestAutoApproveRoutes(t *testing.T) {
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tt.routes,
},
IPv4: ptr.To(netip.MustParseAddr("100.64.0.1")),
IPv4: new(netip.MustParseAddr("100.64.0.1")),
}
err = adb.DB.Save(&node).Error
@ -460,7 +459,7 @@ func TestAutoApproveRoutes(t *testing.T) {
RoutableIPs: tt.routes,
},
Tags: []string{"tag:exit"},
IPv4: ptr.To(netip.MustParseAddr("100.64.0.2")),
IPv4: new(netip.MustParseAddr("100.64.0.2")),
}
err = adb.DB.Save(&nodeTagged).Error
@ -649,7 +648,7 @@ func TestListEphemeralNodes(t *testing.T) {
Hostname: "test",
UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID),
AuthKeyID: new(pak.ID),
}
nodeEph := types.Node{
@ -659,7 +658,7 @@ func TestListEphemeralNodes(t *testing.T) {
Hostname: "ephemeral",
UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pakEph.ID),
AuthKeyID: new(pakEph.ID),
}
err = db.DB.Save(&node).Error
@ -750,8 +749,8 @@ func TestNodeNaming(t *testing.T) {
if err != nil {
return err
}
_, err = RegisterNodeForTest(tx, nodeInvalidHostname, ptr.To(mpp("100.64.0.66/32").Addr()), nil)
_, err = RegisterNodeForTest(tx, nodeShortHostname, ptr.To(mpp("100.64.0.67/32").Addr()), nil)
_, err = RegisterNodeForTest(tx, nodeInvalidHostname, new(mpp("100.64.0.66/32").Addr()), nil)
_, err = RegisterNodeForTest(tx, nodeShortHostname, new(mpp("100.64.0.67/32").Addr()), nil)
return err
})
require.NoError(t, err)

View file

@ -17,7 +17,7 @@ func isTextUnmarshaler(rv reflect.Value) bool {
}
func maybeInstantiatePtr(rv reflect.Value) {
if rv.Kind() == reflect.Ptr && rv.IsNil() {
if rv.Kind() == reflect.Pointer && rv.IsNil() {
np := reflect.New(rv.Type().Elem())
rv.Set(np)
}
@ -36,7 +36,7 @@ func (TextSerialiser) Scan(ctx context.Context, field *schema.Field, dst reflect
// If the field is a pointer, we need to dereference it to get the actual type
// so we do not end with a second pointer.
if fieldValue.Elem().Kind() == reflect.Ptr {
if fieldValue.Elem().Kind() == reflect.Pointer {
fieldValue = fieldValue.Elem()
}
@ -65,7 +65,7 @@ func (TextSerialiser) Scan(ctx context.Context, field *schema.Field, dst reflect
// If it is not a pointer, we need to assign the value to the
// field.
dstField := field.ReflectValueOf(ctx, dst)
if dstField.Kind() == reflect.Ptr {
if dstField.Kind() == reflect.Pointer {
dstField.Set(fieldValue)
} else {
dstField.Set(fieldValue.Elem())
@ -86,7 +86,7 @@ func (TextSerialiser) Value(ctx context.Context, field *schema.Field, dst reflec
// If the value is nil, we return nil, however, go nil values are not
// always comparable, particularly when reflection is involved:
// https://dev.to/arxeiss/in-go-nil-is-not-equal-to-nil-sometimes-jn8
if v == nil || (reflect.ValueOf(v).Kind() == reflect.Ptr && reflect.ValueOf(v).IsNil()) {
if v == nil || (reflect.ValueOf(v).Kind() == reflect.Pointer && reflect.ValueOf(v).IsNil()) {
return nil, nil
}
b, err := v.MarshalText()

View file

@ -8,7 +8,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
"tailscale.com/types/ptr"
)
func TestCreateAndDestroyUser(t *testing.T) {
@ -79,7 +78,7 @@ func TestDestroyUserErrors(t *testing.T) {
Hostname: "testnode",
UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID),
AuthKeyID: new(pak.ID),
}
trx := db.DB.Save(&node)
require.NoError(t, trx.Error)

View file

@ -13,7 +13,6 @@ import (
"github.com/puzpuzpuz/xsync/v4"
"github.com/rs/zerolog/log"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
var errConnectionClosed = errors.New("connection channel already closed")
@ -136,7 +135,7 @@ func (b *LockFreeBatcher) RemoveNode(id types.NodeID, c chan<- *tailcfg.MapRespo
// No active connections - keep the node entry alive for rapid reconnections
// The node will get a fresh full map when it reconnects
log.Debug().Caller().Uint64("node.id", id.Uint64()).Msg("Node disconnected from batcher because all connections removed, keeping entry for rapid reconnection")
b.connected.Store(id, ptr.To(time.Now()))
b.connected.Store(id, new(time.Now()))
return false
}

View file

@ -5,6 +5,7 @@ import (
"fmt"
"net/netip"
"runtime"
"slices"
"strings"
"sync"
"sync/atomic"
@ -327,7 +328,7 @@ func (ut *updateTracker) getStats(nodeID types.NodeID) UpdateStats {
// Return a copy to avoid race conditions
return UpdateStats{
TotalUpdates: stats.TotalUpdates,
UpdateSizes: append([]int{}, stats.UpdateSizes...),
UpdateSizes: slices.Clone(stats.UpdateSizes),
LastUpdate: stats.LastUpdate,
}
}
@ -344,7 +345,7 @@ func (ut *updateTracker) getAllStats() map[types.NodeID]UpdateStats {
for nodeID, stats := range ut.stats {
result[nodeID] = UpdateStats{
TotalUpdates: stats.TotalUpdates,
UpdateSizes: append([]int{}, stats.UpdateSizes...),
UpdateSizes: slices.Clone(stats.UpdateSizes),
LastUpdate: stats.LastUpdate,
}
}

View file

@ -14,7 +14,6 @@ import (
"github.com/juanfont/headscale/hscontrol/types"
"tailscale.com/tailcfg"
"tailscale.com/types/dnstype"
"tailscale.com/types/ptr"
)
var iap = func(ipStr string) *netip.Addr {
@ -51,7 +50,7 @@ func TestDNSConfigMapResponse(t *testing.T) {
mach := func(hostname, username string, userid uint) *types.Node {
return &types.Node{
Hostname: hostname,
UserID: ptr.To(userid),
UserID: new(userid),
User: &types.User{
Name: username,
},

View file

@ -14,7 +14,6 @@ import (
"github.com/stretchr/testify/require"
"gorm.io/gorm"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
var ap = func(ipStr string) *netip.Addr {
@ -1074,21 +1073,21 @@ func TestSSHPolicyRules(t *testing.T) {
nodeUser1 := types.Node{
Hostname: "user1-device",
IPv4: ap("100.64.0.1"),
UserID: ptr.To(uint(1)),
User: ptr.To(users[0]),
UserID: new(uint(1)),
User: new(users[0]),
}
nodeUser2 := types.Node{
Hostname: "user2-device",
IPv4: ap("100.64.0.2"),
UserID: ptr.To(uint(2)),
User: ptr.To(users[1]),
UserID: new(uint(2)),
User: new(users[1]),
}
taggedClient := types.Node{
Hostname: "tagged-client",
IPv4: ap("100.64.0.4"),
UserID: ptr.To(uint(2)),
User: ptr.To(users[1]),
UserID: new(uint(2)),
User: new(users[1]),
Tags: []string{"tag:client"},
}

View file

@ -16,7 +16,6 @@ import (
"gorm.io/gorm"
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
"tailscale.com/util/must"
)
@ -144,13 +143,13 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0:ab12:4843:2222:6273:2221"),
User: ptr.To(users[0]),
User: new(users[0]),
},
peers: types.Nodes{
&types.Node{
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0:ab12:4843:2222:6273:2222"),
User: ptr.To(users[0]),
User: new(users[0]),
},
},
want: []tailcfg.FilterRule{},
@ -191,7 +190,7 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]),
User: new(users[1]),
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: []netip.Prefix{
netip.MustParsePrefix("10.33.0.0/16"),
@ -202,7 +201,7 @@ func TestReduceFilterRules(t *testing.T) {
&types.Node{
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[1]),
User: new(users[1]),
},
},
want: []tailcfg.FilterRule{
@ -283,19 +282,19 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]),
User: new(users[1]),
},
peers: types.Nodes{
&types.Node{
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[2]),
User: new(users[2]),
},
// "internal" exit node
&types.Node{
IPv4: ap("100.64.0.100"),
IPv6: ap("fd7a:115c:a1e0::100"),
User: ptr.To(users[3]),
User: new(users[3]),
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tsaddr.ExitRoutes(),
},
@ -344,7 +343,7 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{
IPv4: ap("100.64.0.100"),
IPv6: ap("fd7a:115c:a1e0::100"),
User: ptr.To(users[3]),
User: new(users[3]),
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tsaddr.ExitRoutes(),
},
@ -353,12 +352,12 @@ func TestReduceFilterRules(t *testing.T) {
&types.Node{
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[2]),
User: new(users[2]),
},
&types.Node{
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]),
User: new(users[1]),
},
},
want: []tailcfg.FilterRule{
@ -453,7 +452,7 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{
IPv4: ap("100.64.0.100"),
IPv6: ap("fd7a:115c:a1e0::100"),
User: ptr.To(users[3]),
User: new(users[3]),
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tsaddr.ExitRoutes(),
},
@ -462,12 +461,12 @@ func TestReduceFilterRules(t *testing.T) {
&types.Node{
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[2]),
User: new(users[2]),
},
&types.Node{
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]),
User: new(users[1]),
},
},
want: []tailcfg.FilterRule{
@ -565,7 +564,7 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{
IPv4: ap("100.64.0.100"),
IPv6: ap("fd7a:115c:a1e0::100"),
User: ptr.To(users[3]),
User: new(users[3]),
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: []netip.Prefix{netip.MustParsePrefix("8.0.0.0/16"), netip.MustParsePrefix("16.0.0.0/16")},
},
@ -574,12 +573,12 @@ func TestReduceFilterRules(t *testing.T) {
&types.Node{
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[2]),
User: new(users[2]),
},
&types.Node{
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]),
User: new(users[1]),
},
},
want: []tailcfg.FilterRule{
@ -655,7 +654,7 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{
IPv4: ap("100.64.0.100"),
IPv6: ap("fd7a:115c:a1e0::100"),
User: ptr.To(users[3]),
User: new(users[3]),
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: []netip.Prefix{netip.MustParsePrefix("8.0.0.0/8"), netip.MustParsePrefix("16.0.0.0/8")},
},
@ -664,12 +663,12 @@ func TestReduceFilterRules(t *testing.T) {
&types.Node{
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[2]),
User: new(users[2]),
},
&types.Node{
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]),
User: new(users[1]),
},
},
want: []tailcfg.FilterRule{
@ -737,7 +736,7 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{
IPv4: ap("100.64.0.100"),
IPv6: ap("fd7a:115c:a1e0::100"),
User: ptr.To(users[3]),
User: new(users[3]),
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: []netip.Prefix{netip.MustParsePrefix("172.16.0.0/24")},
},
@ -747,7 +746,7 @@ func TestReduceFilterRules(t *testing.T) {
&types.Node{
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]),
User: new(users[1]),
},
},
want: []tailcfg.FilterRule{
@ -804,13 +803,13 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[3]),
User: new(users[3]),
},
peers: types.Nodes{
&types.Node{
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]),
User: new(users[1]),
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: []netip.Prefix{p("172.16.0.0/24"), p("10.10.11.0/24"), p("10.10.12.0/24")},
},

View file

@ -10,7 +10,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
"tailscale.com/types/ptr"
)
func TestNodeCanApproveRoute(t *testing.T) {
@ -25,24 +24,24 @@ func TestNodeCanApproveRoute(t *testing.T) {
ID: 1,
Hostname: "user1-device",
IPv4: ap("100.64.0.1"),
UserID: ptr.To(uint(1)),
User: ptr.To(users[0]),
UserID: new(uint(1)),
User: new(users[0]),
}
exitNode := types.Node{
ID: 2,
Hostname: "user2-device",
IPv4: ap("100.64.0.2"),
UserID: ptr.To(uint(2)),
User: ptr.To(users[1]),
UserID: new(uint(2)),
User: new(users[1]),
}
taggedNode := types.Node{
ID: 3,
Hostname: "tagged-server",
IPv4: ap("100.64.0.3"),
UserID: ptr.To(uint(3)),
User: ptr.To(users[2]),
UserID: new(uint(3)),
User: new(users[2]),
Tags: []string{"tag:router"},
}
@ -50,8 +49,8 @@ func TestNodeCanApproveRoute(t *testing.T) {
ID: 4,
Hostname: "multi-tag-node",
IPv4: ap("100.64.0.4"),
UserID: ptr.To(uint(2)),
User: ptr.To(users[1]),
UserID: new(uint(2)),
User: new(users[1]),
Tags: []string{"tag:router", "tag:server"},
}

View file

@ -15,7 +15,6 @@ import (
"github.com/stretchr/testify/require"
"gorm.io/gorm"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
// aliasWithPorts creates an AliasWithPorts structure from an alias and ports.
@ -410,14 +409,14 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
nodeUser1 := types.Node{
Hostname: "user1-device",
IPv4: createAddr("100.64.0.1"),
UserID: ptr.To(users[0].ID),
User: ptr.To(users[0]),
UserID: new(users[0].ID),
User: new(users[0]),
}
nodeUser2 := types.Node{
Hostname: "user2-device",
IPv4: createAddr("100.64.0.2"),
UserID: ptr.To(users[1].ID),
User: ptr.To(users[1]),
UserID: new(users[1].ID),
User: new(users[1]),
}
nodes := types.Nodes{&nodeUser1, &nodeUser2}
@ -622,14 +621,14 @@ func TestCompileSSHPolicy_CheckAction(t *testing.T) {
nodeUser1 := types.Node{
Hostname: "user1-device",
IPv4: createAddr("100.64.0.1"),
UserID: ptr.To(users[0].ID),
User: ptr.To(users[0]),
UserID: new(users[0].ID),
User: new(users[0]),
}
nodeUser2 := types.Node{
Hostname: "user2-device",
IPv4: createAddr("100.64.0.2"),
UserID: ptr.To(users[1].ID),
User: ptr.To(users[1]),
UserID: new(users[1].ID),
User: new(users[1]),
}
nodes := types.Nodes{&nodeUser1, &nodeUser2}
@ -683,15 +682,15 @@ func TestSSHIntegrationReproduction(t *testing.T) {
node1 := &types.Node{
Hostname: "user1-node",
IPv4: createAddr("100.64.0.1"),
UserID: ptr.To(users[0].ID),
User: ptr.To(users[0]),
UserID: new(users[0].ID),
User: new(users[0]),
}
node2 := &types.Node{
Hostname: "user2-node",
IPv4: createAddr("100.64.0.2"),
UserID: ptr.To(users[1].ID),
User: ptr.To(users[1]),
UserID: new(users[1].ID),
User: new(users[1]),
}
nodes := types.Nodes{node1, node2}
@ -806,19 +805,19 @@ func TestCompileFilterRulesForNodeWithAutogroupSelf(t *testing.T) {
nodes := types.Nodes{
{
User: ptr.To(users[0]),
User: new(users[0]),
IPv4: ap("100.64.0.1"),
},
{
User: ptr.To(users[0]),
User: new(users[0]),
IPv4: ap("100.64.0.2"),
},
{
User: ptr.To(users[1]),
User: new(users[1]),
IPv4: ap("100.64.0.3"),
},
{
User: ptr.To(users[1]),
User: new(users[1]),
IPv4: ap("100.64.0.4"),
},
// Tagged device for user1
@ -938,11 +937,11 @@ func TestTagUserMutualExclusivity(t *testing.T) {
nodes := types.Nodes{
// User-owned nodes
{
User: ptr.To(users[0]),
User: new(users[0]),
IPv4: ap("100.64.0.1"),
},
{
User: ptr.To(users[1]),
User: new(users[1]),
IPv4: ap("100.64.0.2"),
},
// Tagged nodes
@ -960,8 +959,8 @@ func TestTagUserMutualExclusivity(t *testing.T) {
policy := &Policy{
TagOwners: TagOwners{
Tag("tag:server"): Owners{ptr.To(Username("user1@"))},
Tag("tag:database"): Owners{ptr.To(Username("user2@"))},
Tag("tag:server"): Owners{new(Username("user1@"))},
Tag("tag:database"): Owners{new(Username("user2@"))},
},
ACLs: []ACL{
// Rule 1: user1 (user-owned) should NOT be able to reach tagged nodes
@ -1056,11 +1055,11 @@ func TestAutogroupTagged(t *testing.T) {
nodes := types.Nodes{
// User-owned nodes (not tagged)
{
User: ptr.To(users[0]),
User: new(users[0]),
IPv4: ap("100.64.0.1"),
},
{
User: ptr.To(users[1]),
User: new(users[1]),
IPv4: ap("100.64.0.2"),
},
// Tagged nodes
@ -1083,10 +1082,10 @@ func TestAutogroupTagged(t *testing.T) {
policy := &Policy{
TagOwners: TagOwners{
Tag("tag:server"): Owners{ptr.To(Username("user1@"))},
Tag("tag:database"): Owners{ptr.To(Username("user2@"))},
Tag("tag:web"): Owners{ptr.To(Username("user1@"))},
Tag("tag:prod"): Owners{ptr.To(Username("user1@"))},
Tag("tag:server"): Owners{new(Username("user1@"))},
Tag("tag:database"): Owners{new(Username("user2@"))},
Tag("tag:web"): Owners{new(Username("user1@"))},
Tag("tag:prod"): Owners{new(Username("user1@"))},
},
ACLs: []ACL{
// Rule: autogroup:tagged can reach user-owned nodes
@ -1206,10 +1205,10 @@ func TestAutogroupSelfWithSpecificUserSource(t *testing.T) {
}
nodes := types.Nodes{
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1")},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4")},
{User: new(users[0]), IPv4: ap("100.64.0.1")},
{User: new(users[0]), IPv4: ap("100.64.0.2")},
{User: new(users[1]), IPv4: ap("100.64.0.3")},
{User: new(users[1]), IPv4: ap("100.64.0.4")},
}
policy := &Policy{
@ -1273,11 +1272,11 @@ func TestAutogroupSelfWithGroupSource(t *testing.T) {
}
nodes := types.Nodes{
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1")},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4")},
{User: ptr.To(users[2]), IPv4: ap("100.64.0.5")},
{User: new(users[0]), IPv4: ap("100.64.0.1")},
{User: new(users[0]), IPv4: ap("100.64.0.2")},
{User: new(users[1]), IPv4: ap("100.64.0.3")},
{User: new(users[1]), IPv4: ap("100.64.0.4")},
{User: new(users[2]), IPv4: ap("100.64.0.5")},
}
policy := &Policy{
@ -1342,13 +1341,13 @@ func TestSSHWithAutogroupSelfInDestination(t *testing.T) {
nodes := types.Nodes{
// User1's nodes
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1"), Hostname: "user1-node1"},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2"), Hostname: "user1-node2"},
{User: new(users[0]), IPv4: ap("100.64.0.1"), Hostname: "user1-node1"},
{User: new(users[0]), IPv4: ap("100.64.0.2"), Hostname: "user1-node2"},
// User2's nodes
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3"), Hostname: "user2-node1"},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4"), Hostname: "user2-node2"},
{User: new(users[1]), IPv4: ap("100.64.0.3"), Hostname: "user2-node1"},
{User: new(users[1]), IPv4: ap("100.64.0.4"), Hostname: "user2-node2"},
// Tagged node for user1 (should be excluded)
{User: ptr.To(users[0]), IPv4: ap("100.64.0.5"), Hostname: "user1-tagged", Tags: []string{"tag:server"}},
{User: new(users[0]), IPv4: ap("100.64.0.5"), Hostname: "user1-tagged", Tags: []string{"tag:server"}},
}
policy := &Policy{
@ -1420,10 +1419,10 @@ func TestSSHWithAutogroupSelfAndSpecificUser(t *testing.T) {
}
nodes := types.Nodes{
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1")},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4")},
{User: new(users[0]), IPv4: ap("100.64.0.1")},
{User: new(users[0]), IPv4: ap("100.64.0.2")},
{User: new(users[1]), IPv4: ap("100.64.0.3")},
{User: new(users[1]), IPv4: ap("100.64.0.4")},
}
policy := &Policy{
@ -1474,11 +1473,11 @@ func TestSSHWithAutogroupSelfAndGroup(t *testing.T) {
}
nodes := types.Nodes{
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1")},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4")},
{User: ptr.To(users[2]), IPv4: ap("100.64.0.5")},
{User: new(users[0]), IPv4: ap("100.64.0.1")},
{User: new(users[0]), IPv4: ap("100.64.0.2")},
{User: new(users[1]), IPv4: ap("100.64.0.3")},
{User: new(users[1]), IPv4: ap("100.64.0.4")},
{User: new(users[2]), IPv4: ap("100.64.0.5")},
}
policy := &Policy{
@ -1531,10 +1530,10 @@ func TestSSHWithAutogroupSelfExcludesTaggedDevices(t *testing.T) {
}
nodes := types.Nodes{
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1"), Hostname: "untagged1"},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2"), Hostname: "untagged2"},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.3"), Hostname: "tagged1", Tags: []string{"tag:server"}},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.4"), Hostname: "tagged2", Tags: []string{"tag:web"}},
{User: new(users[0]), IPv4: ap("100.64.0.1"), Hostname: "untagged1"},
{User: new(users[0]), IPv4: ap("100.64.0.2"), Hostname: "untagged2"},
{User: new(users[0]), IPv4: ap("100.64.0.3"), Hostname: "tagged1", Tags: []string{"tag:server"}},
{User: new(users[0]), IPv4: ap("100.64.0.4"), Hostname: "tagged2", Tags: []string{"tag:web"}},
}
policy := &Policy{
@ -1591,10 +1590,10 @@ func TestSSHWithAutogroupSelfAndMixedDestinations(t *testing.T) {
}
nodes := types.Nodes{
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1"), Hostname: "user1-device"},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2"), Hostname: "user1-device2"},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3"), Hostname: "user2-device"},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4"), Hostname: "user2-router", Tags: []string{"tag:router"}},
{User: new(users[0]), IPv4: ap("100.64.0.1"), Hostname: "user1-device"},
{User: new(users[0]), IPv4: ap("100.64.0.2"), Hostname: "user1-device2"},
{User: new(users[1]), IPv4: ap("100.64.0.3"), Hostname: "user2-device"},
{User: new(users[1]), IPv4: ap("100.64.0.4"), Hostname: "user2-router", Tags: []string{"tag:router"}},
}
policy := &Policy{

View file

@ -11,7 +11,6 @@ import (
"github.com/stretchr/testify/require"
"gorm.io/gorm"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
func node(name, ipv4, ipv6 string, user types.User, hostinfo *tailcfg.Hostinfo) *types.Node {
@ -20,8 +19,8 @@ func node(name, ipv4, ipv6 string, user types.User, hostinfo *tailcfg.Hostinfo)
Hostname: name,
IPv4: ap(ipv4),
IPv6: ap(ipv6),
User: ptr.To(user),
UserID: ptr.To(user.ID),
User: new(user),
UserID: new(user.ID),
Hostinfo: hostinfo,
}
}
@ -457,8 +456,8 @@ func TestAutogroupSelfWithOtherRules(t *testing.T) {
Hostname: "test-1-device",
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[0]),
UserID: ptr.To(users[0].ID),
User: new(users[0]),
UserID: new(users[0].ID),
Hostinfo: &tailcfg.Hostinfo{},
}
@ -468,8 +467,8 @@ func TestAutogroupSelfWithOtherRules(t *testing.T) {
Hostname: "test-2-router",
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[1]),
UserID: ptr.To(users[1].ID),
User: new(users[1]),
UserID: new(users[1].ID),
Tags: []string{"tag:node-router"},
Hostinfo: &tailcfg.Hostinfo{},
}
@ -537,8 +536,8 @@ func TestAutogroupSelfPolicyUpdateTriggersMapResponse(t *testing.T) {
Hostname: "test-1-device",
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[0]),
UserID: ptr.To(users[0].ID),
User: new(users[0]),
UserID: new(users[0].ID),
Hostinfo: &tailcfg.Hostinfo{},
}
@ -547,8 +546,8 @@ func TestAutogroupSelfPolicyUpdateTriggersMapResponse(t *testing.T) {
Hostname: "test-2-device",
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[1]),
UserID: ptr.To(users[1].ID),
User: new(users[1]),
UserID: new(users[1].ID),
Hostinfo: &tailcfg.Hostinfo{},
}
@ -647,8 +646,8 @@ func TestTagPropagationToPeerMap(t *testing.T) {
Hostname: "user1-node",
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[0]),
UserID: ptr.To(users[0].ID),
User: new(users[0]),
UserID: new(users[0].ID),
Tags: []string{"tag:web", "tag:internal"},
}
@ -658,8 +657,8 @@ func TestTagPropagationToPeerMap(t *testing.T) {
Hostname: "user2-node",
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[1]),
UserID: ptr.To(users[1].ID),
User: new(users[1]),
UserID: new(users[1].ID),
}
initialNodes := types.Nodes{user1Node, user2Node}
@ -686,8 +685,8 @@ func TestTagPropagationToPeerMap(t *testing.T) {
Hostname: "user1-node",
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[0]),
UserID: ptr.To(users[0].ID),
User: new(users[0]),
UserID: new(users[0].ID),
Tags: []string{"tag:internal"}, // tag:web removed!
}

View file

@ -19,7 +19,6 @@ import (
"gorm.io/gorm"
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
// TestUnmarshalPolicy tests the unmarshalling of JSON into Policy objects and the marshalling
@ -53,11 +52,11 @@ func TestMarshalJSON(t *testing.T) {
Action: "accept",
Protocol: "tcp",
Sources: Aliases{
ptr.To(Username("user@example.com")),
new(Username("user@example.com")),
},
Destinations: []AliasWithPorts{
{
Alias: ptr.To(Username("other@example.com")),
Alias: new(Username("other@example.com")),
Ports: []tailcfg.PortRange{{First: 80, Last: 80}},
},
},
@ -253,11 +252,11 @@ func TestUnmarshalPolicy(t *testing.T) {
Action: "accept",
Protocol: "tcp",
Sources: Aliases{
ptr.To(Username("testuser@headscale.net")),
new(Username("testuser@headscale.net")),
},
Destinations: []AliasWithPorts{
{
Alias: ptr.To(Username("otheruser@headscale.net")),
Alias: new(Username("otheruser@headscale.net")),
Ports: []tailcfg.PortRange{{First: 80, Last: 80}},
},
},
@ -546,7 +545,7 @@ func TestUnmarshalPolicy(t *testing.T) {
},
Destinations: []AliasWithPorts{
{
Alias: ptr.To(AutoGroup("autogroup:internet")),
Alias: new(AutoGroup("autogroup:internet")),
Ports: []tailcfg.PortRange{tailcfg.PortRangeAny},
},
},
@ -682,7 +681,7 @@ func TestUnmarshalPolicy(t *testing.T) {
`,
want: &Policy{
TagOwners: TagOwners{
Tag("tag:web"): Owners{ptr.To(Username("admin@example.com"))},
Tag("tag:web"): Owners{new(Username("admin@example.com"))},
},
SSHs: []SSH{
{
@ -691,7 +690,7 @@ func TestUnmarshalPolicy(t *testing.T) {
tp("tag:web"),
},
Destinations: SSHDstAliases{
ptr.To(Username("admin@example.com")),
new(Username("admin@example.com")),
},
Users: []SSHUser{
SSHUser("*"),
@ -733,7 +732,7 @@ func TestUnmarshalPolicy(t *testing.T) {
gp("group:admins"),
},
Destinations: SSHDstAliases{
ptr.To(Username("admin@example.com")),
new(Username("admin@example.com")),
},
Users: []SSHUser{
SSHUser("root"),
@ -1154,7 +1153,7 @@ func TestUnmarshalPolicy(t *testing.T) {
},
Destinations: []AliasWithPorts{
{
Alias: ptr.To(AutoGroup("autogroup:internet")),
Alias: new(AutoGroup("autogroup:internet")),
Ports: []tailcfg.PortRange{tailcfg.PortRangeAny},
},
},
@ -1491,7 +1490,7 @@ func TestUnmarshalPolicy(t *testing.T) {
want: &Policy{
TagOwners: TagOwners{
Tag("tag:bigbrother"): {},
Tag("tag:smallbrother"): {ptr.To(Tag("tag:bigbrother"))},
Tag("tag:smallbrother"): {new(Tag("tag:bigbrother"))},
},
ACLs: []ACL{
{
@ -1502,7 +1501,7 @@ func TestUnmarshalPolicy(t *testing.T) {
},
Destinations: []AliasWithPorts{
{
Alias: ptr.To(Tag("tag:smallbrother")),
Alias: new(Tag("tag:smallbrother")),
Ports: []tailcfg.PortRange{{First: 9000, Last: 9000}},
},
},
@ -1583,14 +1582,14 @@ func TestUnmarshalPolicy(t *testing.T) {
}
}
func gp(s string) *Group { return ptr.To(Group(s)) }
func up(s string) *Username { return ptr.To(Username(s)) }
func hp(s string) *Host { return ptr.To(Host(s)) }
func tp(s string) *Tag { return ptr.To(Tag(s)) }
func agp(s string) *AutoGroup { return ptr.To(AutoGroup(s)) }
func gp(s string) *Group { return new(Group(s)) }
func up(s string) *Username { return new(Username(s)) }
func hp(s string) *Host { return new(Host(s)) }
func tp(s string) *Tag { return new(Tag(s)) }
func agp(s string) *AutoGroup { return new(AutoGroup(s)) }
func mp(pref string) netip.Prefix { return netip.MustParsePrefix(pref) }
func ap(addr string) *netip.Addr { return ptr.To(netip.MustParseAddr(addr)) }
func pp(pref string) *Prefix { return ptr.To(Prefix(mp(pref))) }
func ap(addr string) *netip.Addr { return new(netip.MustParseAddr(addr)) }
func pp(pref string) *Prefix { return new(Prefix(mp(pref))) }
func p(pref string) Prefix { return Prefix(mp(pref)) }
func TestResolvePolicy(t *testing.T) {
@ -1636,31 +1635,31 @@ func TestResolvePolicy(t *testing.T) {
},
{
name: "username",
toResolve: ptr.To(Username("testuser@")),
toResolve: new(Username("testuser@")),
nodes: types.Nodes{
// Not matching other user
{
User: ptr.To(notme),
User: new(notme),
IPv4: ap("100.100.101.1"),
},
// Not matching forced tags
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:anything"},
IPv4: ap("100.100.101.2"),
},
// not matching because it's tagged (tags copied from AuthKey)
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"alsotagged"},
IPv4: ap("100.100.101.3"),
},
{
User: ptr.To(testuser),
User: new(testuser),
IPv4: ap("100.100.101.103"),
},
{
User: ptr.To(testuser),
User: new(testuser),
IPv4: ap("100.100.101.104"),
},
},
@ -1668,31 +1667,31 @@ func TestResolvePolicy(t *testing.T) {
},
{
name: "group",
toResolve: ptr.To(Group("group:testgroup")),
toResolve: new(Group("group:testgroup")),
nodes: types.Nodes{
// Not matching other user
{
User: ptr.To(notme),
User: new(notme),
IPv4: ap("100.100.101.4"),
},
// Not matching forced tags
{
User: ptr.To(groupuser),
User: new(groupuser),
Tags: []string{"tag:anything"},
IPv4: ap("100.100.101.5"),
},
// not matching because it's tagged (tags copied from AuthKey)
{
User: ptr.To(groupuser),
User: new(groupuser),
Tags: []string{"tag:alsotagged"},
IPv4: ap("100.100.101.6"),
},
{
User: ptr.To(groupuser),
User: new(groupuser),
IPv4: ap("100.100.101.203"),
},
{
User: ptr.To(groupuser),
User: new(groupuser),
IPv4: ap("100.100.101.204"),
},
},
@ -1710,7 +1709,7 @@ func TestResolvePolicy(t *testing.T) {
nodes: types.Nodes{
// Not matching other user
{
User: ptr.To(notme),
User: new(notme),
IPv4: ap("100.100.101.9"),
},
// Not matching forced tags
@ -1746,7 +1745,7 @@ func TestResolvePolicy(t *testing.T) {
pol: &Policy{
TagOwners: TagOwners{
Tag("tag:bigbrother"): {},
Tag("tag:smallbrother"): {ptr.To(Tag("tag:bigbrother"))},
Tag("tag:smallbrother"): {new(Tag("tag:bigbrother"))},
},
},
nodes: types.Nodes{
@ -1769,7 +1768,7 @@ func TestResolvePolicy(t *testing.T) {
pol: &Policy{
TagOwners: TagOwners{
Tag("tag:bigbrother"): {},
Tag("tag:smallbrother"): {ptr.To(Tag("tag:bigbrother"))},
Tag("tag:smallbrother"): {new(Tag("tag:bigbrother"))},
},
},
nodes: types.Nodes{
@ -1804,14 +1803,14 @@ func TestResolvePolicy(t *testing.T) {
},
{
name: "multiple-groups",
toResolve: ptr.To(Group("group:testgroup")),
toResolve: new(Group("group:testgroup")),
nodes: types.Nodes{
{
User: ptr.To(groupuser1),
User: new(groupuser1),
IPv4: ap("100.100.101.203"),
},
{
User: ptr.To(groupuser2),
User: new(groupuser2),
IPv4: ap("100.100.101.204"),
},
},
@ -1829,10 +1828,10 @@ func TestResolvePolicy(t *testing.T) {
},
{
name: "invalid-username",
toResolve: ptr.To(Username("invaliduser@")),
toResolve: new(Username("invaliduser@")),
nodes: types.Nodes{
{
User: ptr.To(testuser),
User: new(testuser),
IPv4: ap("100.100.101.103"),
},
},
@ -1860,47 +1859,47 @@ func TestResolvePolicy(t *testing.T) {
},
{
name: "autogroup-member-comprehensive",
toResolve: ptr.To(AutoGroup(AutoGroupMember)),
toResolve: new(AutoGroup(AutoGroupMember)),
nodes: types.Nodes{
// Node with no tags (should be included - is a member)
{
User: ptr.To(testuser),
User: new(testuser),
IPv4: ap("100.100.101.1"),
},
// Node with single tag (should be excluded - tagged nodes are not members)
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:test"},
IPv4: ap("100.100.101.2"),
},
// Node with multiple tags, all defined in policy (should be excluded)
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:test", "tag:other"},
IPv4: ap("100.100.101.3"),
},
// Node with tag not defined in policy (should be excluded - still tagged)
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:undefined"},
IPv4: ap("100.100.101.4"),
},
// Node with mixed tags - some defined, some not (should be excluded)
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:test", "tag:undefined"},
IPv4: ap("100.100.101.5"),
},
// Another untagged node from different user (should be included)
{
User: ptr.To(testuser2),
User: new(testuser2),
IPv4: ap("100.100.101.6"),
},
},
pol: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("testuser@"))},
Tag("tag:other"): Owners{ptr.To(Username("testuser@"))},
Tag("tag:test"): Owners{new(Username("testuser@"))},
Tag("tag:other"): Owners{new(Username("testuser@"))},
},
},
want: []netip.Prefix{
@ -1910,54 +1909,54 @@ func TestResolvePolicy(t *testing.T) {
},
{
name: "autogroup-tagged",
toResolve: ptr.To(AutoGroup(AutoGroupTagged)),
toResolve: new(AutoGroup(AutoGroupTagged)),
nodes: types.Nodes{
// Node with no tags (should be excluded - not tagged)
{
User: ptr.To(testuser),
User: new(testuser),
IPv4: ap("100.100.101.1"),
},
// Node with single tag defined in policy (should be included)
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:test"},
IPv4: ap("100.100.101.2"),
},
// Node with multiple tags, all defined in policy (should be included)
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:test", "tag:other"},
IPv4: ap("100.100.101.3"),
},
// Node with tag not defined in policy (should be included - still tagged)
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:undefined"},
IPv4: ap("100.100.101.4"),
},
// Node with mixed tags - some defined, some not (should be included)
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:test", "tag:undefined"},
IPv4: ap("100.100.101.5"),
},
// Another untagged node from different user (should be excluded)
{
User: ptr.To(testuser2),
User: new(testuser2),
IPv4: ap("100.100.101.6"),
},
// Tagged node from different user (should be included)
{
User: ptr.To(testuser2),
User: new(testuser2),
Tags: []string{"tag:server"},
IPv4: ap("100.100.101.7"),
},
},
pol: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("testuser@"))},
Tag("tag:other"): Owners{ptr.To(Username("testuser@"))},
Tag("tag:server"): Owners{ptr.To(Username("testuser2@"))},
Tag("tag:test"): Owners{new(Username("testuser@"))},
Tag("tag:other"): Owners{new(Username("testuser@"))},
Tag("tag:server"): Owners{new(Username("testuser2@"))},
},
},
want: []netip.Prefix{
@ -1968,37 +1967,37 @@ func TestResolvePolicy(t *testing.T) {
},
{
name: "autogroup-self",
toResolve: ptr.To(AutoGroupSelf),
toResolve: new(AutoGroupSelf),
nodes: types.Nodes{
{
User: ptr.To(testuser),
User: new(testuser),
IPv4: ap("100.100.101.1"),
},
{
User: ptr.To(testuser2),
User: new(testuser2),
IPv4: ap("100.100.101.2"),
},
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:test"},
IPv4: ap("100.100.101.3"),
},
{
User: ptr.To(testuser2),
User: new(testuser2),
Tags: []string{"tag:test"},
IPv4: ap("100.100.101.4"),
},
},
pol: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("testuser@"))},
Tag("tag:test"): Owners{new(Username("testuser@"))},
},
},
wantErr: "autogroup:self requires per-node resolution",
},
{
name: "autogroup-invalid",
toResolve: ptr.To(AutoGroup("autogroup:invalid")),
toResolve: new(AutoGroup("autogroup:invalid")),
wantErr: "unknown autogroup",
},
}
@ -2076,7 +2075,7 @@ func TestResolveAutoApprovers(t *testing.T) {
policy: &Policy{
AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Username("user1@"))},
mp("10.0.0.0/24"): {new(Username("user1@"))},
},
},
},
@ -2091,8 +2090,8 @@ func TestResolveAutoApprovers(t *testing.T) {
policy: &Policy{
AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Username("user1@"))},
mp("10.0.1.0/24"): {ptr.To(Username("user2@"))},
mp("10.0.0.0/24"): {new(Username("user1@"))},
mp("10.0.1.0/24"): {new(Username("user2@"))},
},
},
},
@ -2107,7 +2106,7 @@ func TestResolveAutoApprovers(t *testing.T) {
name: "exit-node",
policy: &Policy{
AutoApprovers: AutoApproverPolicy{
ExitNode: AutoApprovers{ptr.To(Username("user1@"))},
ExitNode: AutoApprovers{new(Username("user1@"))},
},
},
want: map[netip.Prefix]*netipx.IPSet{},
@ -2122,7 +2121,7 @@ func TestResolveAutoApprovers(t *testing.T) {
},
AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Group("group:testgroup"))},
mp("10.0.0.0/24"): {new(Group("group:testgroup"))},
},
},
},
@ -2137,20 +2136,20 @@ func TestResolveAutoApprovers(t *testing.T) {
policy: &Policy{
TagOwners: TagOwners{
"tag:testtag": Owners{
ptr.To(Username("user1@")),
ptr.To(Username("user2@")),
new(Username("user1@")),
new(Username("user2@")),
},
"tag:exittest": Owners{
ptr.To(Group("group:exitgroup")),
new(Group("group:exitgroup")),
},
},
Groups: Groups{
"group:exitgroup": Usernames{"user2@"},
},
AutoApprovers: AutoApproverPolicy{
ExitNode: AutoApprovers{ptr.To(Tag("tag:exittest"))},
ExitNode: AutoApprovers{new(Tag("tag:exittest"))},
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.1.0/24"): {ptr.To(Tag("tag:testtag"))},
mp("10.0.1.0/24"): {new(Tag("tag:testtag"))},
},
},
},
@ -2168,10 +2167,10 @@ func TestResolveAutoApprovers(t *testing.T) {
},
AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Group("group:testgroup"))},
mp("10.0.1.0/24"): {ptr.To(Username("user3@"))},
mp("10.0.0.0/24"): {new(Group("group:testgroup"))},
mp("10.0.1.0/24"): {new(Username("user3@"))},
},
ExitNode: AutoApprovers{ptr.To(Username("user1@"))},
ExitNode: AutoApprovers{new(Username("user1@"))},
},
},
want: map[netip.Prefix]*netipx.IPSet{
@ -2388,7 +2387,7 @@ func TestNodeCanApproveRoute(t *testing.T) {
policy: &Policy{
AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Username("user1@"))},
mp("10.0.0.0/24"): {new(Username("user1@"))},
},
},
},
@ -2401,8 +2400,8 @@ func TestNodeCanApproveRoute(t *testing.T) {
policy: &Policy{
AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Username("user1@"))},
mp("10.0.1.0/24"): {ptr.To(Username("user2@"))},
mp("10.0.0.0/24"): {new(Username("user1@"))},
mp("10.0.1.0/24"): {new(Username("user2@"))},
},
},
},
@ -2414,7 +2413,7 @@ func TestNodeCanApproveRoute(t *testing.T) {
name: "exit-node-approval",
policy: &Policy{
AutoApprovers: AutoApproverPolicy{
ExitNode: AutoApprovers{ptr.To(Username("user1@"))},
ExitNode: AutoApprovers{new(Username("user1@"))},
},
},
node: nodes[0],
@ -2429,7 +2428,7 @@ func TestNodeCanApproveRoute(t *testing.T) {
},
AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Group("group:testgroup"))},
mp("10.0.0.0/24"): {new(Group("group:testgroup"))},
},
},
},
@ -2445,10 +2444,10 @@ func TestNodeCanApproveRoute(t *testing.T) {
},
AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Group("group:testgroup"))},
mp("10.0.1.0/24"): {ptr.To(Username("user3@"))},
mp("10.0.0.0/24"): {new(Group("group:testgroup"))},
mp("10.0.1.0/24"): {new(Username("user3@"))},
},
ExitNode: AutoApprovers{ptr.To(Username("user1@"))},
ExitNode: AutoApprovers{new(Username("user1@"))},
},
},
node: nodes[0],
@ -2460,7 +2459,7 @@ func TestNodeCanApproveRoute(t *testing.T) {
policy: &Policy{
AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Username("user2@"))},
mp("10.0.0.0/24"): {new(Username("user2@"))},
},
},
},
@ -2518,7 +2517,7 @@ func TestResolveTagOwners(t *testing.T) {
name: "single-tag-owner",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user1@"))},
Tag("tag:test"): Owners{new(Username("user1@"))},
},
},
want: map[Tag]*netipx.IPSet{
@ -2530,7 +2529,7 @@ func TestResolveTagOwners(t *testing.T) {
name: "multiple-tag-owners",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user1@")), ptr.To(Username("user2@"))},
Tag("tag:test"): Owners{new(Username("user1@")), new(Username("user2@"))},
},
},
want: map[Tag]*netipx.IPSet{
@ -2545,7 +2544,7 @@ func TestResolveTagOwners(t *testing.T) {
"group:testgroup": Usernames{"user1@", "user2@"},
},
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Group("group:testgroup"))},
Tag("tag:test"): Owners{new(Group("group:testgroup"))},
},
},
want: map[Tag]*netipx.IPSet{
@ -2557,8 +2556,8 @@ func TestResolveTagOwners(t *testing.T) {
name: "tag-owns-tag",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:bigbrother"): Owners{ptr.To(Username("user1@"))},
Tag("tag:smallbrother"): Owners{ptr.To(Tag("tag:bigbrother"))},
Tag("tag:bigbrother"): Owners{new(Username("user1@"))},
Tag("tag:smallbrother"): Owners{new(Tag("tag:bigbrother"))},
},
},
want: map[Tag]*netipx.IPSet{
@ -2619,7 +2618,7 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "single-tag-owner",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user1@"))},
Tag("tag:test"): Owners{new(Username("user1@"))},
},
},
node: nodes[0],
@ -2630,7 +2629,7 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "multiple-tag-owners",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user1@")), ptr.To(Username("user2@"))},
Tag("tag:test"): Owners{new(Username("user1@")), new(Username("user2@"))},
},
},
node: nodes[1],
@ -2644,7 +2643,7 @@ func TestNodeCanHaveTag(t *testing.T) {
"group:testgroup": Usernames{"user1@", "user2@"},
},
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Group("group:testgroup"))},
Tag("tag:test"): Owners{new(Group("group:testgroup"))},
},
},
node: nodes[1],
@ -2658,7 +2657,7 @@ func TestNodeCanHaveTag(t *testing.T) {
"group:testgroup": Usernames{"invalid"},
},
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Group("group:testgroup"))},
Tag("tag:test"): Owners{new(Group("group:testgroup"))},
},
},
node: nodes[0],
@ -2670,7 +2669,7 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "node-cannot-have-tag",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user2@"))},
Tag("tag:test"): Owners{new(Username("user2@"))},
},
},
node: nodes[0],
@ -2681,7 +2680,7 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "node-with-unauthorized-tag-different-user",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:prod"): Owners{ptr.To(Username("user1@"))},
Tag("tag:prod"): Owners{new(Username("user1@"))},
},
},
node: nodes[2], // user3's node
@ -2692,8 +2691,8 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "node-with-multiple-tags-one-unauthorized",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:web"): Owners{ptr.To(Username("user1@"))},
Tag("tag:database"): Owners{ptr.To(Username("user2@"))},
Tag("tag:web"): Owners{new(Username("user1@"))},
Tag("tag:database"): Owners{new(Username("user2@"))},
},
},
node: nodes[0], // user1's node
@ -2713,7 +2712,7 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "tag-not-in-tagowners",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:prod"): Owners{ptr.To(Username("user1@"))},
Tag("tag:prod"): Owners{new(Username("user1@"))},
},
},
node: nodes[0],
@ -2726,13 +2725,13 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "node-without-ip-user-owns-tag",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user1@"))},
Tag("tag:test"): Owners{new(Username("user1@"))},
},
},
node: &types.Node{
// No IPv4 or IPv6 - simulates new node registration
User: &users[0],
UserID: ptr.To(users[0].ID),
UserID: new(users[0].ID),
},
tag: "tag:test",
want: true, // Should succeed via user-based fallback
@ -2741,13 +2740,13 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "node-without-ip-user-does-not-own-tag",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user2@"))},
Tag("tag:test"): Owners{new(Username("user2@"))},
},
},
node: &types.Node{
// No IPv4 or IPv6 - simulates new node registration
User: &users[0], // user1, but tag owned by user2
UserID: ptr.To(users[0].ID),
UserID: new(users[0].ID),
},
tag: "tag:test",
want: false, // user1 does not own tag:test
@ -2759,13 +2758,13 @@ func TestNodeCanHaveTag(t *testing.T) {
"group:admins": Usernames{"user1@", "user2@"},
},
TagOwners: TagOwners{
Tag("tag:admin"): Owners{ptr.To(Group("group:admins"))},
Tag("tag:admin"): Owners{new(Group("group:admins"))},
},
},
node: &types.Node{
// No IPv4 or IPv6 - simulates new node registration
User: &users[1], // user2 is in group:admins
UserID: ptr.To(users[1].ID),
UserID: new(users[1].ID),
},
tag: "tag:admin",
want: true, // Should succeed via group membership
@ -2777,13 +2776,13 @@ func TestNodeCanHaveTag(t *testing.T) {
"group:admins": Usernames{"user1@"},
},
TagOwners: TagOwners{
Tag("tag:admin"): Owners{ptr.To(Group("group:admins"))},
Tag("tag:admin"): Owners{new(Group("group:admins"))},
},
},
node: &types.Node{
// No IPv4 or IPv6 - simulates new node registration
User: &users[1], // user2 is NOT in group:admins
UserID: ptr.To(users[1].ID),
UserID: new(users[1].ID),
},
tag: "tag:admin",
want: false, // user2 is not in group:admins
@ -2792,7 +2791,7 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "node-without-ip-no-user",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user1@"))},
Tag("tag:test"): Owners{new(Username("user1@"))},
},
},
node: &types.Node{
@ -2809,14 +2808,14 @@ func TestNodeCanHaveTag(t *testing.T) {
},
TagOwners: TagOwners{
Tag("tag:server"): Owners{
ptr.To(Username("user1@")),
ptr.To(Group("group:ops")),
new(Username("user1@")),
new(Group("group:ops")),
},
},
},
node: &types.Node{
User: &users[0], // user1 directly owns the tag
UserID: ptr.To(users[0].ID),
UserID: new(users[0].ID),
},
tag: "tag:server",
want: true,
@ -2829,14 +2828,14 @@ func TestNodeCanHaveTag(t *testing.T) {
},
TagOwners: TagOwners{
Tag("tag:server"): Owners{
ptr.To(Username("user1@")),
ptr.To(Group("group:ops")),
new(Username("user1@")),
new(Group("group:ops")),
},
},
},
node: &types.Node{
User: &users[2], // user3 is in group:ops
UserID: ptr.To(users[2].ID),
UserID: new(users[2].ID),
},
tag: "tag:server",
want: true,
@ -2881,14 +2880,14 @@ func TestUserMatchesOwner(t *testing.T) {
name: "username-match",
policy: &Policy{},
user: users[0],
owner: ptr.To(Username("user1@")),
owner: new(Username("user1@")),
want: true,
},
{
name: "username-no-match",
policy: &Policy{},
user: users[0],
owner: ptr.To(Username("user2@")),
owner: new(Username("user2@")),
want: false,
},
{
@ -2899,7 +2898,7 @@ func TestUserMatchesOwner(t *testing.T) {
},
},
user: users[1], // user2 is in group:admins
owner: ptr.To(Group("group:admins")),
owner: new(Group("group:admins")),
want: true,
},
{
@ -2910,7 +2909,7 @@ func TestUserMatchesOwner(t *testing.T) {
},
},
user: users[1], // user2 is NOT in group:admins
owner: ptr.To(Group("group:admins")),
owner: new(Group("group:admins")),
want: false,
},
{
@ -2919,7 +2918,7 @@ func TestUserMatchesOwner(t *testing.T) {
Groups: Groups{},
},
user: users[0],
owner: ptr.To(Group("group:undefined")),
owner: new(Group("group:undefined")),
want: false,
},
{
@ -3261,20 +3260,20 @@ func TestFlattenTagOwners(t *testing.T) {
{
name: "tag-owns-tag",
input: TagOwners{
Tag("tag:bigbrother"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:smallbrother"): Owners{ptr.To(Tag("tag:bigbrother"))},
Tag("tag:bigbrother"): Owners{new(Group("group:user1"))},
Tag("tag:smallbrother"): Owners{new(Tag("tag:bigbrother"))},
},
want: TagOwners{
Tag("tag:bigbrother"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:smallbrother"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:bigbrother"): Owners{new(Group("group:user1"))},
Tag("tag:smallbrother"): Owners{new(Group("group:user1"))},
},
wantErr: "",
},
{
name: "circular-reference",
input: TagOwners{
Tag("tag:a"): Owners{ptr.To(Tag("tag:b"))},
Tag("tag:b"): Owners{ptr.To(Tag("tag:a"))},
Tag("tag:a"): Owners{new(Tag("tag:b"))},
Tag("tag:b"): Owners{new(Tag("tag:a"))},
},
want: nil,
wantErr: "circular reference detected: tag:a -> tag:b",
@ -3282,83 +3281,83 @@ func TestFlattenTagOwners(t *testing.T) {
{
name: "mixed-owners",
input: TagOwners{
Tag("tag:x"): Owners{ptr.To(Username("user1@")), ptr.To(Tag("tag:y"))},
Tag("tag:y"): Owners{ptr.To(Username("user2@"))},
Tag("tag:x"): Owners{new(Username("user1@")), new(Tag("tag:y"))},
Tag("tag:y"): Owners{new(Username("user2@"))},
},
want: TagOwners{
Tag("tag:x"): Owners{ptr.To(Username("user1@")), ptr.To(Username("user2@"))},
Tag("tag:y"): Owners{ptr.To(Username("user2@"))},
Tag("tag:x"): Owners{new(Username("user1@")), new(Username("user2@"))},
Tag("tag:y"): Owners{new(Username("user2@"))},
},
wantErr: "",
},
{
name: "mixed-dupe-owners",
input: TagOwners{
Tag("tag:x"): Owners{ptr.To(Username("user1@")), ptr.To(Tag("tag:y"))},
Tag("tag:y"): Owners{ptr.To(Username("user1@"))},
Tag("tag:x"): Owners{new(Username("user1@")), new(Tag("tag:y"))},
Tag("tag:y"): Owners{new(Username("user1@"))},
},
want: TagOwners{
Tag("tag:x"): Owners{ptr.To(Username("user1@"))},
Tag("tag:y"): Owners{ptr.To(Username("user1@"))},
Tag("tag:x"): Owners{new(Username("user1@"))},
Tag("tag:y"): Owners{new(Username("user1@"))},
},
wantErr: "",
},
{
name: "no-tag-owners",
input: TagOwners{
Tag("tag:solo"): Owners{ptr.To(Username("user1@"))},
Tag("tag:solo"): Owners{new(Username("user1@"))},
},
want: TagOwners{
Tag("tag:solo"): Owners{ptr.To(Username("user1@"))},
Tag("tag:solo"): Owners{new(Username("user1@"))},
},
wantErr: "",
},
{
name: "tag-long-owner-chain",
input: TagOwners{
Tag("tag:a"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:b"): Owners{ptr.To(Tag("tag:a"))},
Tag("tag:c"): Owners{ptr.To(Tag("tag:b"))},
Tag("tag:d"): Owners{ptr.To(Tag("tag:c"))},
Tag("tag:e"): Owners{ptr.To(Tag("tag:d"))},
Tag("tag:f"): Owners{ptr.To(Tag("tag:e"))},
Tag("tag:g"): Owners{ptr.To(Tag("tag:f"))},
Tag("tag:a"): Owners{new(Group("group:user1"))},
Tag("tag:b"): Owners{new(Tag("tag:a"))},
Tag("tag:c"): Owners{new(Tag("tag:b"))},
Tag("tag:d"): Owners{new(Tag("tag:c"))},
Tag("tag:e"): Owners{new(Tag("tag:d"))},
Tag("tag:f"): Owners{new(Tag("tag:e"))},
Tag("tag:g"): Owners{new(Tag("tag:f"))},
},
want: TagOwners{
Tag("tag:a"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:b"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:c"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:d"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:e"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:f"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:g"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:a"): Owners{new(Group("group:user1"))},
Tag("tag:b"): Owners{new(Group("group:user1"))},
Tag("tag:c"): Owners{new(Group("group:user1"))},
Tag("tag:d"): Owners{new(Group("group:user1"))},
Tag("tag:e"): Owners{new(Group("group:user1"))},
Tag("tag:f"): Owners{new(Group("group:user1"))},
Tag("tag:g"): Owners{new(Group("group:user1"))},
},
wantErr: "",
},
{
name: "tag-long-circular-chain",
input: TagOwners{
Tag("tag:a"): Owners{ptr.To(Tag("tag:g"))},
Tag("tag:b"): Owners{ptr.To(Tag("tag:a"))},
Tag("tag:c"): Owners{ptr.To(Tag("tag:b"))},
Tag("tag:d"): Owners{ptr.To(Tag("tag:c"))},
Tag("tag:e"): Owners{ptr.To(Tag("tag:d"))},
Tag("tag:f"): Owners{ptr.To(Tag("tag:e"))},
Tag("tag:g"): Owners{ptr.To(Tag("tag:f"))},
Tag("tag:a"): Owners{new(Tag("tag:g"))},
Tag("tag:b"): Owners{new(Tag("tag:a"))},
Tag("tag:c"): Owners{new(Tag("tag:b"))},
Tag("tag:d"): Owners{new(Tag("tag:c"))},
Tag("tag:e"): Owners{new(Tag("tag:d"))},
Tag("tag:f"): Owners{new(Tag("tag:e"))},
Tag("tag:g"): Owners{new(Tag("tag:f"))},
},
wantErr: "circular reference detected: tag:a -> tag:b -> tag:c -> tag:d -> tag:e -> tag:f -> tag:g",
},
{
name: "undefined-tag-reference",
input: TagOwners{
Tag("tag:a"): Owners{ptr.To(Tag("tag:nonexistent"))},
Tag("tag:a"): Owners{new(Tag("tag:nonexistent"))},
},
wantErr: `tag "tag:a" references undefined tag "tag:nonexistent"`,
},
{
name: "tag-with-empty-owners-is-valid",
input: TagOwners{
Tag("tag:a"): Owners{ptr.To(Tag("tag:b"))},
Tag("tag:a"): Owners{new(Tag("tag:b"))},
Tag("tag:b"): Owners{}, // empty owners but exists
},
want: TagOwners{

View file

@ -8,7 +8,6 @@ import (
"github.com/juanfont/headscale/hscontrol/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"tailscale.com/types/ptr"
)
// TestEphemeralNodeDeleteWithConcurrentUpdate tests the race condition where UpdateNode and DeleteNode
@ -50,7 +49,7 @@ func TestEphemeralNodeDeleteWithConcurrentUpdate(t *testing.T) {
// Goroutine 1: UpdateNode (simulates UpdateNodeFromMapRequest)
go func() {
updatedNode, updateOk = store.UpdateNode(node.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now())
n.LastSeen = new(time.Now())
})
done <- true
}()
@ -106,7 +105,7 @@ func TestUpdateNodeReturnsInvalidWhenDeletedInSameBatch(t *testing.T) {
// Start UpdateNode in goroutine - it will queue and wait for batch
go func() {
node, ok := store.UpdateNode(node.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now())
n.LastSeen = new(time.Now())
})
resultChan <- struct {
node types.NodeView
@ -156,7 +155,7 @@ func TestPersistNodeToDBPreventsRaceCondition(t *testing.T) {
// Simulate UpdateNode being called
updatedNode, ok := store.UpdateNode(node.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now())
n.LastSeen = new(time.Now())
})
require.True(t, ok, "UpdateNode should succeed")
require.True(t, updatedNode.Valid(), "UpdateNode should return valid node")
@ -221,7 +220,7 @@ func TestEphemeralNodeLogoutRaceCondition(t *testing.T) {
// Goroutine 1: UpdateNode (simulates UpdateNodeFromMapRequest)
go func() {
updatedNode, updateOk = store.UpdateNode(ephemeralNode.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now())
n.LastSeen = new(time.Now())
})
done <- true
}()
@ -294,7 +293,7 @@ func TestUpdateNodeFromMapRequestEphemeralLogoutSequence(t *testing.T) {
go func() {
node, ok := store.UpdateNode(ephemeralNode.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now())
n.LastSeen = new(time.Now())
endpoint := netip.MustParseAddrPort("10.0.0.1:41641")
n.Endpoints = []netip.AddrPort{endpoint}
})
@ -363,7 +362,7 @@ func TestUpdateNodeDeletedInSameBatchReturnsInvalid(t *testing.T) {
go func() {
updatedNode, ok := store.UpdateNode(node.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now())
n.LastSeen = new(time.Now())
})
updateDone <- struct {
node types.NodeView
@ -417,7 +416,7 @@ func TestPersistNodeToDBChecksNodeStoreBeforePersist(t *testing.T) {
// UpdateNode returns a node
updatedNode, ok := store.UpdateNode(ephemeralNode.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now())
n.LastSeen = new(time.Now())
})
require.True(t, ok, "UpdateNode should succeed")
require.True(t, updatedNode.Valid(), "updated node should be valid")

View file

@ -9,7 +9,6 @@ import (
"github.com/stretchr/testify/require"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/types/ptr"
)
func TestNetInfoFromMapRequest(t *testing.T) {
@ -149,7 +148,7 @@ func createTestNodeSimple(id types.NodeID) *types.Node {
node := &types.Node{
ID: id,
Hostname: "test-node",
UserID: ptr.To(uint(id)),
UserID: new(uint(id)),
User: &user,
MachineKey: machineKey.Public(),
NodeKey: nodeKey.Public(),

View file

@ -13,7 +13,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"tailscale.com/types/key"
"tailscale.com/types/ptr"
)
func TestSnapshotFromNodes(t *testing.T) {
@ -174,7 +173,7 @@ func createTestNode(nodeID types.NodeID, userID uint, username, hostname string)
DiscoKey: discoKey.Public(),
Hostname: hostname,
GivenName: hostname,
UserID: ptr.To(userID),
UserID: new(userID),
User: &types.User{
Name: username,
DisplayName: username,
@ -856,7 +855,7 @@ func createConcurrentTestNode(id types.NodeID, hostname string) types.Node {
Hostname: hostname,
MachineKey: machineKey.Public(),
NodeKey: nodeKey.Public(),
UserID: ptr.To(uint(1)),
UserID: new(uint(1)),
User: &types.User{
Name: "concurrent-test-user",
},

View file

@ -6,7 +6,6 @@ import (
"github.com/juanfont/headscale/hscontrol/util"
"github.com/stretchr/testify/assert"
"gorm.io/gorm"
"tailscale.com/types/ptr"
)
// TestNodeIsTagged tests the IsTagged() method for determining if a node is tagged.
@ -69,7 +68,7 @@ func TestNodeIsTagged(t *testing.T) {
{
name: "node with user and no tags - not tagged",
node: Node{
UserID: ptr.To(uint(42)),
UserID: new(uint(42)),
Tags: []string{},
},
want: false,
@ -112,7 +111,7 @@ func TestNodeViewIsTagged(t *testing.T) {
{
name: "user-owned node",
node: Node{
UserID: ptr.To(uint(1)),
UserID: new(uint(1)),
},
want: false,
},
@ -223,7 +222,7 @@ func TestNodeTagsImmutableAfterRegistration(t *testing.T) {
// Test that a user-owned node is not tagged
userNode := Node{
ID: 2,
UserID: ptr.To(uint(42)),
UserID: new(uint(42)),
Tags: []string{},
RegisterMethod: util.RegisterMethodOIDC,
}
@ -243,7 +242,7 @@ func TestNodeOwnershipModel(t *testing.T) {
name: "tagged node has tags, UserID is informational",
node: Node{
ID: 1,
UserID: ptr.To(uint(5)), // "created by" user 5
UserID: new(uint(5)), // "created by" user 5
Tags: []string{"tag:server"},
},
wantIsTagged: true,
@ -253,7 +252,7 @@ func TestNodeOwnershipModel(t *testing.T) {
name: "user-owned node has no tags",
node: Node{
ID: 2,
UserID: ptr.To(uint(5)),
UserID: new(uint(5)),
Tags: []string{},
},
wantIsTagged: false,
@ -265,7 +264,7 @@ func TestNodeOwnershipModel(t *testing.T) {
name: "node with only authkey tags - not tagged (tags should be copied)",
node: Node{
ID: 3,
UserID: ptr.To(uint(5)), // "created by" user 5
UserID: new(uint(5)), // "created by" user 5
AuthKey: &PreAuthKey{
Tags: []string{"tag:database"},
},

View file

@ -93,7 +93,7 @@ func (u *User) StringID() string {
}
// TypedID returns a pointer to the user's ID as a UserID type.
// This is a convenience method to avoid ugly casting like ptr.To(types.UserID(user.ID)).
// This is a convenience method to avoid ugly casting like new(types.UserID(user.ID)).
func (u *User) TypedID() *UserID {
uid := UserID(u.ID)
return &uid

View file

@ -20,7 +20,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
var veryLargeDestination = []policyv2.AliasWithPorts{
@ -1284,9 +1283,9 @@ func TestACLAutogroupMember(t *testing.T) {
ACLs: []policyv2.ACL{
{
Action: "accept",
Sources: []policyv2.Alias{ptr.To(policyv2.AutoGroupMember)},
Sources: []policyv2.Alias{new(policyv2.AutoGroupMember)},
Destinations: []policyv2.AliasWithPorts{
aliasWithPorts(ptr.To(policyv2.AutoGroupMember), tailcfg.PortRangeAny),
aliasWithPorts(new(policyv2.AutoGroupMember), tailcfg.PortRangeAny),
},
},
},
@ -1372,9 +1371,9 @@ func TestACLAutogroupTagged(t *testing.T) {
ACLs: []policyv2.ACL{
{
Action: "accept",
Sources: []policyv2.Alias{ptr.To(policyv2.AutoGroupTagged)},
Sources: []policyv2.Alias{new(policyv2.AutoGroupTagged)},
Destinations: []policyv2.AliasWithPorts{
aliasWithPorts(ptr.To(policyv2.AutoGroupTagged), tailcfg.PortRangeAny),
aliasWithPorts(new(policyv2.AutoGroupTagged), tailcfg.PortRangeAny),
},
},
},
@ -1657,9 +1656,9 @@ func TestACLAutogroupSelf(t *testing.T) {
ACLs: []policyv2.ACL{
{
Action: "accept",
Sources: []policyv2.Alias{ptr.To(policyv2.AutoGroupMember)},
Sources: []policyv2.Alias{new(policyv2.AutoGroupMember)},
Destinations: []policyv2.AliasWithPorts{
aliasWithPorts(ptr.To(policyv2.AutoGroupSelf), tailcfg.PortRangeAny),
aliasWithPorts(new(policyv2.AutoGroupSelf), tailcfg.PortRangeAny),
},
},
{
@ -1956,9 +1955,9 @@ func TestACLPolicyPropagationOverTime(t *testing.T) {
ACLs: []policyv2.ACL{
{
Action: "accept",
Sources: []policyv2.Alias{ptr.To(policyv2.AutoGroupMember)},
Sources: []policyv2.Alias{new(policyv2.AutoGroupMember)},
Destinations: []policyv2.AliasWithPorts{
aliasWithPorts(ptr.To(policyv2.AutoGroupSelf), tailcfg.PortRangeAny),
aliasWithPorts(new(policyv2.AutoGroupSelf), tailcfg.PortRangeAny),
},
},
},

View file

@ -356,13 +356,13 @@ func TestAPIAuthenticationBypassCurl(t *testing.T) {
lines := strings.Split(curlOutput, "\n")
var httpCode string
var responseBody string
var responseBody strings.Builder
for _, line := range lines {
if after, ok := strings.CutPrefix(line, "HTTP_CODE:"); ok {
httpCode = after
} else {
responseBody += line
responseBody.WriteString(line)
}
}
@ -372,7 +372,7 @@ func TestAPIAuthenticationBypassCurl(t *testing.T) {
// Should contain user data
var response v1.ListUsersResponse
err = protojson.Unmarshal([]byte(responseBody), &response)
err = protojson.Unmarshal([]byte(responseBody.String()), &response)
assert.NoError(t, err, "Response should be valid protobuf JSON")
users := response.GetUsers()
assert.Len(t, users, 2, "Should have 2 users")

View file

@ -17,7 +17,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
func TestAuthKeyLogoutAndReloginSameUser(t *testing.T) {
@ -608,7 +607,7 @@ func TestAuthKeyLogoutAndReloginRoutesPreserved(t *testing.T) {
},
AutoApprovers: policyv2.AutoApproverPolicy{
Routes: map[netip.Prefix]policyv2.AutoApprovers{
netip.MustParsePrefix(advertiseRoute): {ptr.To(policyv2.Username(user + "@test.no"))},
netip.MustParsePrefix(advertiseRoute): {new(policyv2.Username(user + "@test.no"))},
},
},
},

View file

@ -13,7 +13,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
func isSSHNoAccessStdError(stderr string) bool {
@ -482,10 +481,10 @@ func TestSSHAutogroupSelf(t *testing.T) {
{
Action: "accept",
Sources: policyv2.SSHSrcAliases{
ptr.To(policyv2.AutoGroupMember),
new(policyv2.AutoGroupMember),
},
Destinations: policyv2.SSHDstAliases{
ptr.To(policyv2.AutoGroupSelf),
new(policyv2.AutoGroupSelf),
},
Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")},
},