db, types: add a column for auto-approved routes

It starts empty: the current policy cannot tell which earlier approvals
were automatic, so all of them stay as an admin's.
This commit is contained in:
Kristoffer Dalby committed 2026-10-30 14:55:36 +00:00
1 parent 177920d84e
commit 20117e7060
7 files changed
+89 -55

No files matched your search

+12
View File
@@ -282,6 +282,18 @@ WHERE tags IS NOT NULL AND tags != '[]' AND tags != '' AND tags != 'null'
},
Rollback: func(db *gorm.DB) error { return nil },
},
{
// Records which approved routes only auto-approval granted. It
// starts empty: the current policy cannot tell which earlier
// approvals were automatic, so all of them stay as an admin's.
ID: "202610211200-node-auto-approved-routes",
Migrate: func(tx *gorm.DB) error {
return tx.Transaction(func(tx *gorm.DB) error {
return tx.Exec(`ALTER TABLE nodes ADD COLUMN auto_approved_routes text`).Error
})
},
Rollback: func(db *gorm.DB) error { return nil },
},
},
)
+5
View File
@@ -117,6 +117,11 @@ func TestSQLiteMigrationAndDataValidation(t *testing.T) {
require.NoError(t, err)
require.Len(t, nodes, 5)
// Approvals from before the upgrade carry no auto-approval mark.
for _, n := range nodes {
assert.Empty(t, n.AutoApprovedRoutes, "node %d", n.ID)
}
// Pre-auth key ids are preserved, so every node keeps its key.
for _, n := range nodes {
require.NotNil(t, n.AuthKeyID, "node %d", n.ID)
+1
View File
@@ -89,6 +89,7 @@ CREATE TABLE nodes(
last_seen datetime,
expiry datetime,
approved_routes text,
auto_approved_routes text,
created_at datetime,
updated_at datetime,
+2 -1
View File
@@ -1960,7 +1960,8 @@ var nodeFieldImpact = map[string]string{
"ID": "relation", "IPv4": "relation", "IPv6": "relation",
"UserID": "relation", "User": "relation", "Tags": "relation",
"ApprovedRoutes": "relation", "Hostinfo": "relation",
"IsOnline": "election", "Unhealthy": "election",
"AutoApprovedRoutes": "payload",
"IsOnline": "election", "Unhealthy": "election",
"MachineKey": "payload", "NodeKey": "payload", "DiscoKey": "payload",
"Endpoints": "payload", "Hostname": "payload", "GivenName": "payload",
"RegisterMethod": "payload", "AuthKeyID": "payload", "AuthKey": "payload",
+5
View File
@@ -184,6 +184,11 @@ type Node struct {
// See [Node.Hostinfo]
ApprovedRoutes Prefixes `gorm:"column:approved_routes;serializer:json"`
// AutoApprovedRoutes are the approved routes only auto-approval granted.
// The node loses one when it stops announcing it; a route an admin
// approved stays.
AutoApprovedRoutes Prefixes `gorm:"column:auto_approved_routes;serializer:json"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
+29 -27
View File
@@ -71,6 +71,7 @@ func (src *Node) Clone() *Node {
dst.LastSeen = new(*src.LastSeen)
}
dst.ApprovedRoutes = append(src.ApprovedRoutes[:0:0], src.ApprovedRoutes...)
dst.AutoApprovedRoutes = append(src.AutoApprovedRoutes[:0:0], src.AutoApprovedRoutes...)
if dst.DeletedAt != nil {
dst.DeletedAt = new(*src.DeletedAt)
}
@@ -82,33 +83,34 @@ func (src *Node) Clone() *Node {
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _NodeCloneNeedsRegeneration = Node(struct {
ID NodeID
MachineKey key.MachinePublic
NodeKey key.NodePublic
DiscoKey key.DiscoPublic
Endpoints AddrPorts
Hostinfo *tailcfg.Hostinfo
IPv4 *netip.Addr
IPv6 *netip.Addr
Hostname string
GivenName string
UserID *uint
User *User
RegisterMethod string
Tags Strings
AuthKeyID *uint64
AuthKey *Credential
Expiry *time.Time
LastSeen *time.Time
ApprovedRoutes Prefixes
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
IsOnline *bool
Unhealthy bool
ActiveSessions int
SessionEpoch uint64
SessionStart time.Time
ID NodeID
MachineKey key.MachinePublic
NodeKey key.NodePublic
DiscoKey key.DiscoPublic
Endpoints AddrPorts
Hostinfo *tailcfg.Hostinfo
IPv4 *netip.Addr
IPv6 *netip.Addr
Hostname string
GivenName string
UserID *uint
User *User
RegisterMethod string
Tags Strings
AuthKeyID *uint64
AuthKey *Credential
Expiry *time.Time
LastSeen *time.Time
ApprovedRoutes Prefixes
AutoApprovedRoutes Prefixes
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
IsOnline *bool
Unhealthy bool
ActiveSessions int
SessionEpoch uint64
SessionStart time.Time
}{})
// Clone makes a deep copy of PreAuthKey.
+35 -27
View File
@@ -252,6 +252,13 @@ func (v NodeView) LastSeen() views.ValuePointer[time.Time] {
func (v NodeView) ApprovedRoutes() views.Slice[netip.Prefix] {
return views.SliceOf(v.ж.ApprovedRoutes)
}
// AutoApprovedRoutes are the approved routes only auto-approval granted.
// The node loses one when it stops announcing it; a route an admin
// approved stays.
func (v NodeView) AutoApprovedRoutes() views.Slice[netip.Prefix] {
return views.SliceOf(v.ж.AutoApprovedRoutes)
}
func (v NodeView) CreatedAt() time.Time { return v.ж.CreatedAt }
func (v NodeView) UpdatedAt() time.Time { return v.ж.UpdatedAt }
func (v NodeView) DeletedAt() views.ValuePointer[time.Time] {
@@ -291,33 +298,34 @@ func (v NodeView) String() string { return v.ж.String() }
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _NodeViewNeedsRegeneration = Node(struct {
ID NodeID
MachineKey key.MachinePublic
NodeKey key.NodePublic
DiscoKey key.DiscoPublic
Endpoints AddrPorts
Hostinfo *tailcfg.Hostinfo
IPv4 *netip.Addr
IPv6 *netip.Addr
Hostname string
GivenName string
UserID *uint
User *User
RegisterMethod string
Tags Strings
AuthKeyID *uint64
AuthKey *Credential
Expiry *time.Time
LastSeen *time.Time
ApprovedRoutes Prefixes
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
IsOnline *bool
Unhealthy bool
ActiveSessions int
SessionEpoch uint64
SessionStart time.Time
ID NodeID
MachineKey key.MachinePublic
NodeKey key.NodePublic
DiscoKey key.DiscoPublic
Endpoints AddrPorts
Hostinfo *tailcfg.Hostinfo
IPv4 *netip.Addr
IPv6 *netip.Addr
Hostname string
GivenName string
UserID *uint
User *User
RegisterMethod string
Tags Strings
AuthKeyID *uint64
AuthKey *Credential
Expiry *time.Time
LastSeen *time.Time
ApprovedRoutes Prefixes
AutoApprovedRoutes Prefixes
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
IsOnline *bool
Unhealthy bool
ActiveSessions int
SessionEpoch uint64
SessionStart time.Time
}{})
// View returns a read-only view of PreAuthKey.