Skip to content

Commit

Permalink
migrate ip_addresses to ipv4, ipv6
Browse files Browse the repository at this point in the history
This commit migrates the list in ip_addresses column into
two separate columns, ipv4 and ipv6 and puts them into dedicated
types.

Followup commit updates types everywhere and in tests.

Updates juanfont#1828

Signed-off-by: Kristoffer Dalby <[email protected]>
  • Loading branch information
kradalby committed Apr 15, 2024
1 parent 736bad8 commit 4bfa6f6
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 10 deletions.
61 changes: 61 additions & 0 deletions hscontrol/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"database/sql"
"errors"
"fmt"
"net/netip"
"path/filepath"
"strconv"
"strings"
Expand Down Expand Up @@ -330,6 +331,66 @@ func NewHeadscaleDatabase(
return nil
},
},
{
// Replace column with IP address list with dedicated
// IP v4 and v6 column.
// Note that previously, the list _could_ contain more
// than two addresses, which should not really happen.
// In that case, the first occurence of each type will
// be kept.
ID: "2024041121742",
Migrate: func(tx *gorm.DB) error {
_ = tx.Migrator().AddColumn(&types.Node{}, "ipv4")
_ = tx.Migrator().AddColumn(&types.Node{}, "ipv6")

type node struct {
ID uint64 `gorm:"column:id"`
Addresses string `gorm:"column:ip_addresses"`
}

var nodes []node

_ = tx.Raw("SELECT id, ip_addresses FROM nodes").Scan(&nodes).Error

for _, node := range nodes {
addrs := strings.Split(node.Addresses, ",")

if len(addrs) == 0 {
fmt.Errorf("no addresses found for node(%d)", node.ID)
}

var v4 *netip.Addr
var v6 *netip.Addr

for _, addrStr := range addrs {
addr, err := netip.ParseAddr(addrStr)
if err != nil {
return fmt.Errorf("parsing IP for node(%d) from database: %w", node.ID, err)
}

if addr.Is4() && v4 == nil {
v4 = &addr
}

if addr.Is6() && v6 == nil {
v6 = &addr
}
}

err = tx.Save(&types.Node{ID: types.NodeID(node.ID), IPv4: v4, IPv6: v6}).Error
if err != nil {
return fmt.Errorf("saving ip addresses to new columns: %w", err)
}
}

_ = tx.Migrator().DropColumn(&types.Node{}, "ip_addresses")

return nil
},
Rollback: func(tx *gorm.DB) error {
return nil
},
},
},
)

Expand Down
20 changes: 10 additions & 10 deletions hscontrol/db/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"net/netip"
"sort"
"strings"
"time"

"github.com/juanfont/headscale/hscontrol/types"
Expand Down Expand Up @@ -294,7 +293,8 @@ func RegisterNodeFromAuthCallback(
userName string,
nodeExpiry *time.Time,
registrationMethod string,
addrs types.NodeAddresses,
ipv4 *netip.Addr,
ipv6 *netip.Addr,
) (*types.Node, error) {
log.Debug().
Str("machine_key", mkey.ShortString()).
Expand Down Expand Up @@ -330,7 +330,7 @@ func RegisterNodeFromAuthCallback(
node, err := RegisterNode(
tx,
registrationNode,
addrs,
ipv4, ipv6,
)

if err == nil {
Expand All @@ -346,25 +346,25 @@ func RegisterNodeFromAuthCallback(
return nil, ErrNodeNotFoundRegistrationCache
}

func (hsdb *HSDatabase) RegisterNode(node types.Node, addrs types.NodeAddresses) (*types.Node, error) {
func (hsdb *HSDatabase) RegisterNode(node types.Node, ipv4 *netip.Addr, ipv6 *netip.Addr) (*types.Node, error) {
return Write(hsdb.DB, func(tx *gorm.DB) (*types.Node, error) {
return RegisterNode(tx, node, addrs)
return RegisterNode(tx, node, ipv4, ipv6)
})
}

// RegisterNode is executed from the CLI to register a new Node using its MachineKey.
func RegisterNode(tx *gorm.DB, node types.Node, addrs types.NodeAddresses) (*types.Node, error) {
func RegisterNode(tx *gorm.DB, node types.Node, ipv4 *netip.Addr, ipv6 *netip.Addr) (*types.Node, error) {
log.Debug().
Str("node", node.Hostname).
Str("machine_key", node.MachineKey.ShortString()).
Str("node_key", node.NodeKey.ShortString()).
Str("user", node.User.Name).
Msg("Registering node")

// If the node exists and we had already IPs for it, we just save it
// If the node exists and it already has IP(s), we just save it
// so we store the node.Expire and node.Nodekey that has been set when
// adding it to the registrationCache
if len(node.IPAddresses) > 0 {
if node.IPv4 != nil || node.IPv6 != nil {
if err := tx.Save(&node).Error; err != nil {
return nil, fmt.Errorf("failed register existing node in the database: %w", err)
}
Expand All @@ -380,7 +380,8 @@ func RegisterNode(tx *gorm.DB, node types.Node, addrs types.NodeAddresses) (*typ
return &node, nil
}

node.IPAddresses = addrs
node.IPv4 = ipv4
node.IPv6 = ipv6

if err := tx.Save(&node).Error; err != nil {
return nil, fmt.Errorf("failed register(save) node in the database: %w", err)
Expand All @@ -389,7 +390,6 @@ func RegisterNode(tx *gorm.DB, node types.Node, addrs types.NodeAddresses) (*typ
log.Trace().
Caller().
Str("node", node.Hostname).
Str("ip", strings.Join(addrs.StringSlice(), ",")).
Msg("Node registered with the database")

return &node, nil
Expand Down

0 comments on commit 4bfa6f6

Please sign in to comment.