Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

migration #2509

Merged
merged 34 commits into from
Aug 14, 2023
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
f08e5a3
create gateways during migration
mattkasun Aug 1, 2023
4112145
set version for testing
mattkasun Aug 2, 2023
7da2e9b
restruct migration
mattkasun Aug 4, 2023
06618d3
debug logging
mattkasun Aug 4, 2023
f62a786
enforce unique names for ext client names (#2476)
mattkasun Aug 1, 2023
041df1c
prune(NET-483): remove defunct host.internetgateway field (#2487)
Aceix Aug 2, 2023
2184903
don't reference host on err (#2493)
abhishek9686 Aug 2, 2023
bb7371a
deprecrate netclient install scripts (#2490)
abhishek9686 Aug 2, 2023
fc7c20c
Net 500: validate network parameter passed to node endpoints (#2480)
mattkasun Aug 2, 2023
c22edbe
NET-513 (#2492)
uGiFarukh Aug 3, 2023
63cad79
[NET-404] Run in limited mode when ee checks fail (#2474)
gabrielseibel1 Aug 3, 2023
5be9fb4
feat(NET-449): add sync feature to request a host pull from server (#…
Aceix Aug 4, 2023
ec73e01
fix(NET-486): change client name length validation (#2498)
Aceix Aug 8, 2023
2fdd597
[NET-477] Pick AMB URL dynamically (#2489)
gabrielseibel1 Aug 8, 2023
91f2e45
[Feature]: nm-quick script tackling arm TODO support (#2488)
bornav Aug 8, 2023
db752d0
rebase conflict
mattkasun Aug 8, 2023
8c7ca8b
include pass and os in mirgration data
mattkasun Aug 8, 2023
d9ac019
node network ranges
mattkasun Aug 8, 2023
e3e3657
remove debugging logs
mattkasun Aug 8, 2023
4ad088f
add gateways
mattkasun Aug 8, 2023
e6ca146
use sent node
mattkasun Aug 9, 2023
36b444e
Merge branch 'develop' into NET-406
mattkasun Aug 9, 2023
2e0aa37
upgrade shell script
mattkasun Aug 9, 2023
c834071
Merge branch 'develop' into NET-406
mattkasun Aug 10, 2023
0ff1f04
associate node to host during migration
mattkasun Aug 10, 2023
c614aa3
add node to host.Nodes and publish peer update
mattkasun Aug 10, 2023
503046a
save host outside loop
mattkasun Aug 10, 2023
a5a52ad
fix script name
mattkasun Aug 10, 2023
db460ec
simplify upgrade script
mattkasun Aug 11, 2023
ae46ec2
don't migrate relays
mattkasun Aug 11, 2023
aaf55a1
simplify upgrade script even more
mattkasun Aug 11, 2023
5eaa38e
guard against blank address or address6
mattkasun Aug 11, 2023
b876fd7
Merge branch 'develop' into NET-406
mattkasun Aug 14, 2023
56572a6
typos
mattkasun Aug 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 172 additions & 43 deletions controllers/migrate.go
mattkasun marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@ package controller

import (
"encoding/json"
"fmt"
"net"
"net/http"
"strconv"
"time"

"github.com/gravitl/netmaker/auth"
"github.com/google/uuid"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
"github.com/gravitl/netmaker/servercfg"
"golang.org/x/crypto/bcrypt"
"golang.org/x/exp/slog"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)

// swagger:route PUT /api/v1/nodes/migrate nodes migrateNode
Expand All @@ -26,63 +33,185 @@ import (
// 200: nodeJoinResponse
func migrate(w http.ResponseWriter, r *http.Request) {
data := models.MigrationData{}
host := models.Host{}
node := models.Node{}
nodes := []models.Node{}
server := models.ServerConfig{}
err := json.NewDecoder(r.Body).Decode(&data)
if err != nil {
logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}

var networksToAdd = []string{}
for i := range data.LegacyNodes {
legacyNode := data.LegacyNodes[i]
record, err := database.FetchRecord(database.NODES_TABLE_NAME, legacyNode.ID)
for i, legacy := range data.LegacyNodes {
record, err := database.FetchRecord(database.NODES_TABLE_NAME, legacy.ID)
if err != nil {
logger.Log(0, "no record for legacy node", legacyNode.ID, err.Error())
continue
} else {
var oldLegacyNode models.LegacyNode
if err = json.Unmarshal([]byte(record), &oldLegacyNode); err != nil {
logger.Log(0, "error decoding legacy node", err.Error())
slog.Error("legacy node not found", "error", err)
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("legacy node not found %w", err), "badrequest"))
return
}
var legacyNode models.LegacyNode
if err = json.Unmarshal([]byte(record), &legacyNode); err != nil {
slog.Error("decoding legacy node", "errror", err)
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("decode legacy node %w", err), "badrequest"))
return
}
if err := bcrypt.CompareHashAndPassword([]byte(legacyNode.Password), []byte(legacy.Password)); err != nil {
slog.Error("legacy node invalid password", "error", err)
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("invalid password %w", err), "unauthorized"))
return
}
if i == 0 {
host, node = convertLegacyHostNode(legacy)
host.Name = data.HostName
host.HostPass = data.Password
host.OS = data.OS
if err := logic.CreateHost(&host); err != nil {
slog.Error("create host", "error", err)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
continue
return
}
server = servercfg.GetServerInfo()
if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
server.MQUserName = host.ID.String()
}
if err := bcrypt.CompareHashAndPassword([]byte(oldLegacyNode.Password), []byte(legacyNode.Password)); err != nil {
logger.Log(0, "error decoding legacy password", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "unauthorized"))
continue
key, keyErr := logic.RetrievePublicTrafficKey()
if keyErr != nil {
slog.Error("retrieving traffickey", "error", err)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
networksToAdd = append(networksToAdd, oldLegacyNode.Network)
_ = database.DeleteRecord(database.NODES_TABLE_NAME, oldLegacyNode.ID)
server.TrafficKey = key
} else {
node = convertLegacyNode(legacyNode, host.ID)
}
}
if len(networksToAdd) == 0 {
logger.Log(0, "no valid networks to migrate for host", data.NewHost.Name)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "unauthorized"))
return
}
if !logic.HostExists(&data.NewHost) {
logic.CheckHostPorts(&data.NewHost)
if err = logic.CreateHost(&data.NewHost); err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
if err := logic.UpsertNode(&node); err != nil {
mattkasun marked this conversation as resolved.
Show resolved Hide resolved
slog.Error("update node", "error", err)
continue
}
host.Nodes = append(host.Nodes, node.ID.String())

nodes = append(nodes, node)
}
key, keyErr := logic.RetrievePublicTrafficKey()
if keyErr != nil {
logger.Log(0, "error retrieving key:", keyErr.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
if err := logic.UpsertHost(&host); err != nil {
slog.Error("save host", "error", err)
}
server := servercfg.GetServerInfo()
server.TrafficKey = key
response := models.RegisterResponse{
ServerConf: server,
RequestedHost: data.NewHost,
go mq.PublishPeerUpdate()
response := models.HostPull{
Host: host,
Nodes: nodes,
ServerConfig: server,
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(&response)
logger.Log(0, "successfully migrated host", data.NewHost.Name, data.NewHost.ID.String())
// notify host of changes, peer and node updates
go auth.CheckNetRegAndHostUpdate(networksToAdd, &data.NewHost)

slog.Info("migrated nodes")
// check for gateways
for _, node := range data.LegacyNodes {
if node.IsEgressGateway == "yes" {
egressGateway := models.EgressGatewayRequest{
NodeID: node.ID,
Ranges: node.EgressGatewayRanges,
NatEnabled: node.EgressGatewayNatEnabled,
}
if _, err := logic.CreateEgressGateway(egressGateway); err != nil {
logger.Log(0, "error creating egress gateway for node", node.ID, err.Error())
}
}
if node.IsIngressGateway == "yes" {
ingressGateway := models.IngressRequest{}
ingressNode, err := logic.CreateIngressGateway(node.Network, node.ID, ingressGateway)
if err != nil {
logger.Log(0, "error creating ingress gateway for node", node.ID, err.Error())
}
runUpdates(&ingressNode, true)
}
if node.IsRelay == "yes" && servercfg.Is_EE {
_, relayNode, err := logic.CreateRelay(models.RelayRequest{
NodeID: node.ID,
NetID: node.Network,
RelayedNodes: node.RelayAddrs,
})
if err != nil {
logger.Log(0, "error creating relay for node", node.ID, err.Error())
}
runUpdates(&relayNode, true)
}
}
}

func convertLegacyHostNode(legacy models.LegacyNode) (models.Host, models.Node) {
//convert host
host := models.Host{}
host.ID = uuid.New()
host.IPForwarding = models.ParseBool(legacy.IPForwarding)
host.AutoUpdate = servercfg.AutoUpdateEnabled()
host.Interface = "netmaker"
host.ListenPort = int(legacy.ListenPort)
host.MTU = int(legacy.MTU)
host.PublicKey, _ = wgtypes.ParseKey(legacy.PublicKey)
host.MacAddress = net.HardwareAddr(legacy.MacAddress)
host.TrafficKeyPublic = legacy.TrafficKeys.Mine
host.Nodes = append([]string{}, legacy.ID)
host.Interfaces = legacy.Interfaces
//host.DefaultInterface = legacy.Defaul
host.EndpointIP = net.ParseIP(legacy.Endpoint)
host.IsDocker = models.ParseBool(legacy.IsDocker)
host.IsK8S = models.ParseBool(legacy.IsK8S)
host.IsStatic = models.ParseBool(legacy.IsStatic)
node := convertLegacyNode(legacy, host.ID)
return host, node
}

func convertLegacyNode(legacy models.LegacyNode, hostID uuid.UUID) models.Node {
//convert node
node := models.Node{}
node.ID, _ = uuid.Parse(legacy.ID)
node.HostID = hostID
node.Network = legacy.Network
_, cidr4, err := net.ParseCIDR(legacy.NetworkSettings.AddressRange)
if err != nil {
slog.Warn("parsing address range", "error", err)
} else {
node.NetworkRange = *cidr4
}
_, cidr6, err := net.ParseCIDR(legacy.NetworkSettings.AddressRange6)
if err != nil {
slog.Warn("paring address range6", "error", err)
} else {
node.NetworkRange6 = *cidr6
}
node.Server = servercfg.GetServer()
node.Connected = models.ParseBool(legacy.Connected)
node.Address = net.IPNet{
IP: net.ParseIP(legacy.Address),
Mask: cidr4.Mask,
}
node.Address6 = net.IPNet{
IP: net.ParseIP(legacy.Address6),
Mask: cidr6.Mask,
}
node.Action = models.NODE_NOOP
node.LocalAddress = net.IPNet{
IP: net.ParseIP(legacy.LocalAddress),
}
node.IsEgressGateway = models.ParseBool(legacy.IsEgressGateway)
node.EgressGatewayRanges = legacy.EgressGatewayRanges
node.IsIngressGateway = models.ParseBool(legacy.IsIngressGateway)
node.IsRelayed = models.ParseBool(legacy.IsRelayed)
node.IsRelay = models.ParseBool(legacy.IsRelay)
node.RelayedNodes = legacy.RelayAddrs
node.DNSOn = models.ParseBool(legacy.DNSOn)
node.PersistentKeepalive = time.Duration(legacy.PersistentKeepalive)
node.LastModified = time.Now()
node.ExpirationDateTime, _ = time.Parse(strconv.Itoa(int(legacy.ExpirationDateTime)), "0")
node.EgressGatewayNatEnabled = models.ParseBool(legacy.EgressGatewayNatEnabled)
node.EgressGatewayRequest = legacy.EgressGatewayRequest
node.IngressGatewayRange = legacy.IngressGatewayRange
node.IngressGatewayRange6 = legacy.IngressGatewayRange6
node.DefaultACL = legacy.DefaultACL
node.OwnerID = legacy.OwnerID
node.FailoverNode, _ = uuid.Parse(legacy.FailoverNode)
node.Failover = models.ParseBool(legacy.Failover)
return node
}
4 changes: 3 additions & 1 deletion models/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package models

// MigrationData struct needed to create new v0.18.0 node from v.0.17.X node
type MigrationData struct {
NewHost Host
HostName string
Password string
OS string
LegacyNodes []LegacyNode
}
6 changes: 1 addition & 5 deletions models/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ type Node struct {
// LegacyNode - legacy struct for node model
type LegacyNode struct {
ID string `json:"id,omitempty" bson:"id,omitempty" yaml:"id,omitempty" validate:"required,min=5,id_unique"`
HostID string `json:"hostid,omitempty" bson:"id,omitempty" yaml:"hostid,omitempty" validate:"required,min=5,id_unique"`
Address string `json:"address" bson:"address" yaml:"address" validate:"omitempty,ipv4"`
Address6 string `json:"address6" bson:"address6" yaml:"address6" validate:"omitempty,ipv6"`
LocalAddress string `json:"localaddress" bson:"localaddress" yaml:"localaddress" validate:"omitempty"`
Expand All @@ -109,7 +108,6 @@ type LegacyNode struct {
NetworkSettings Network `json:"networksettings" bson:"networksettings" yaml:"networksettings" validate:"-"`
ListenPort int32 `json:"listenport" bson:"listenport" yaml:"listenport" validate:"omitempty,numeric,min=1024,max=65535"`
LocalListenPort int32 `json:"locallistenport" bson:"locallistenport" yaml:"locallistenport" validate:"numeric,min=0,max=65535"`
ProxyListenPort int32 `json:"proxy_listen_port" bson:"proxy_listen_port" yaml:"proxy_listen_port" validate:"numeric,min=0,max=65535"`
PublicKey string `json:"publickey" bson:"publickey" yaml:"publickey" validate:"required,base64"`
Endpoint string `json:"endpoint" bson:"endpoint" yaml:"endpoint" validate:"required,ip"`
AllowedIPs []string `json:"allowedips" bson:"allowedips" yaml:"allowedips"`
Expand Down Expand Up @@ -153,8 +151,6 @@ type LegacyNode struct {
FirewallInUse string `json:"firewallinuse" bson:"firewallinuse" yaml:"firewallinuse"`
InternetGateway string `json:"internetgateway" bson:"internetgateway" yaml:"internetgateway"`
Connected string `json:"connected" bson:"connected" yaml:"connected" validate:"checkyesorno"`
PendingDelete bool `json:"pendingdelete" bson:"pendingdelete" yaml:"pendingdelete"`
Proxy bool `json:"proxy" bson:"proxy" yaml:"proxy"`
// == PRO ==
DefaultACL string `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"`
OwnerID string `json:"ownerid,omitempty" bson:"ownerid,omitempty" yaml:"ownerid,omitempty"`
Expand Down Expand Up @@ -527,7 +523,7 @@ func (ln *LegacyNode) ConvertToNewNode() (*Host, *Node) {
func (n *Node) Legacy(h *Host, s *ServerConfig, net *Network) *LegacyNode {
l := LegacyNode{}
l.ID = n.ID.String()
l.HostID = h.ID.String()
//l.HostID = h.ID.String()
l.Address = n.Address.String()
l.Address6 = n.Address6.String()
l.Interfaces = h.Interfaces
Expand Down
Loading