Skip to content

Commit

Permalink
Add peer groups support for network routes (#1150)
Browse files Browse the repository at this point in the history
This commit enhances the functionality of the network routes endpoint by introducing a new parameter called `peers_group`. This addition allows users to associate network routes with specific peer groups, simplifying the management and distribution of routes within a network.
  • Loading branch information
surik authored Sep 28, 2023
1 parent 1956ca1 commit 8118d60
Show file tree
Hide file tree
Showing 11 changed files with 829 additions and 101 deletions.
96 changes: 78 additions & 18 deletions management/server/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,14 @@ type AccountManager interface {
DeleteGroup(accountId, userId, groupID string) error
ListGroups(accountId string) ([]*Group, error)
GroupAddPeer(accountId, groupID, peerID string) error
GroupDeletePeer(accountId, groupID, peerKey string) error
GroupDeletePeer(accountId, groupID, peerID string) error
GroupListPeers(accountId, groupID string) ([]*Peer, error)
GetPolicy(accountID, policyID, userID string) (*Policy, error)
SavePolicy(accountID, userID string, policy *Policy) error
DeletePolicy(accountID, policyID, userID string) error
ListPolicies(accountID, userID string) ([]*Policy, error)
GetRoute(accountID, routeID, userID string) (*route.Route, error)
CreateRoute(accountID string, prefix, peerID, description, netID string, masquerade bool, metric int, groups []string, enabled bool, userID string) (*route.Route, error)
CreateRoute(accountID, prefix, peerID string, peerGroupIDs []string, description, netID string, masquerade bool, metric int, groups []string, enabled bool, userID string) (*route.Route, error)
SaveRoute(accountID, userID string, route *route.Route) error
DeleteRoute(accountID, routeID, userID string) error
ListRoutes(accountID, userID string) ([]*route.Route, error)
Expand Down Expand Up @@ -253,22 +253,39 @@ func (a *Account) filterRoutesByGroups(routes []*route.Route, groupListMap looku
func (a *Account) getEnabledAndDisabledRoutesByPeer(peerID string) ([]*route.Route, []*route.Route) {
var enabledRoutes []*route.Route
var disabledRoutes []*route.Route

takeRoute := func(r *route.Route, id string) {
peer := a.GetPeer(peerID)
if peer == nil {
log.Errorf("route %s has peer %s that doesn't exist under account %s", r.ID, peerID, a.Id)
return
}

if r.Enabled {
enabledRoutes = append(enabledRoutes, r)
return
}
disabledRoutes = append(disabledRoutes, r)
}

for _, r := range a.Routes {
if r.Peer == peerID {
// We need to set Peer.Key instead of Peer.ID because this object will be sent to agents as part of a network map.
// Ideally we should have a separate field for that, but fine for now.
peer := a.GetPeer(peerID)
if peer == nil {
log.Errorf("route %s has peer %s that doesn't exist under account %s", r.ID, peerID, a.Id)
continue
}
raut := r.Copy()
raut.Peer = peer.Key
if r.Enabled {
enabledRoutes = append(enabledRoutes, raut)
continue
if len(r.PeerGroups) != 0 {
for _, groupID := range r.PeerGroups {
group := a.GetGroup(groupID)
if group == nil {
log.Errorf("route %s has peers group %s that doesn't exist under account %s", r.ID, groupID, a.Id)
continue
}
for _, id := range group.Peers {
if id == peerID {
takeRoute(r, id)
break
}
}
}
disabledRoutes = append(disabledRoutes, raut)
}
if r.Peer == peerID {
takeRoute(r, peerID)
}
}
return enabledRoutes, disabledRoutes
Expand Down Expand Up @@ -316,8 +333,51 @@ func (a *Account) GetPeerNetworkMap(peerID, dnsDomain string) *NetworkMap {
}
peersToConnect = append(peersToConnect, p)
}
// Please mind, that the returned route.Route objects will contain Peer.Key instead of Peer.ID.
routesUpdate := a.getRoutesToSync(peerID, peersToConnect)

routes := a.getRoutesToSync(peerID, peersToConnect)

takePeer := func(id string) (*Peer, bool) {
peer := a.GetPeer(id)
if peer == nil || peer.Meta.GoOS != "linux" {
return nil, false
}
return peer, true
}

// We need to set Peer.Key instead of Peer.ID because this object will be sent to agents as part of a network map.
// Ideally we should have a separate field for that, but fine for now.
var routesUpdate []*route.Route
seenPeers := make(map[string]bool)
for _, r := range routes {
if r.Peer != "" {
peer, valid := takePeer(r.Peer)
if !valid {
continue
}
rCopy := r.Copy()
rCopy.Peer = peer.Key // client expects the key
routesUpdate = append(routesUpdate, rCopy)
continue
}
for _, groupID := range r.PeerGroups {
if group := a.GetGroup(groupID); group != nil {
for _, peerId := range group.Peers {
peer, valid := takePeer(peerId)
if !valid {
continue
}

if _, ok := seenPeers[peer.ID]; !ok {
rCopy := r.Copy()
rCopy.ID = r.ID + ":" + peer.ID // we have to provide unit route id when distribute network map
rCopy.Peer = peer.Key // client expects the key
routesUpdate = append(routesUpdate, rCopy)
}
seenPeers[peer.ID] = true
}
}
}
}

dnsManagementStatus := a.getPeerDNSManagementStatus(peerID)
dnsUpdate := nbdns.Config{
Expand Down
5 changes: 3 additions & 2 deletions management/server/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1385,8 +1385,9 @@ func TestAccount_Copy(t *testing.T) {
},
Routes: map[string]*route.Route{
"route1": {
ID: "route1",
Groups: []string{"group1"},
ID: "route1",
PeerGroups: []string{},
Groups: []string{"group1"},
},
},
NameServerGroups: map[string]*nbdns.NameServerGroup{
Expand Down
4 changes: 2 additions & 2 deletions management/server/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ func (am *DefaultAccountManager) GroupAddPeer(accountID, groupID, peerID string)
}

// GroupDeletePeer removes peer from the group
func (am *DefaultAccountManager) GroupDeletePeer(accountID, groupID, peerKey string) error {
func (am *DefaultAccountManager) GroupDeletePeer(accountID, groupID, peerID string) error {
unlock := am.Store.AcquireAccountLock(accountID)
defer unlock()

Expand All @@ -301,7 +301,7 @@ func (am *DefaultAccountManager) GroupDeletePeer(accountID, groupID, peerKey str

account.Network.IncSerial()
for i, itemID := range group.Peers {
if itemID == peerKey {
if itemID == peerID {
group.Peers = append(group.Peers[:i], group.Peers[i+1:]...)
if err := am.Store.SaveAccount(account); err != nil {
return err
Expand Down
12 changes: 10 additions & 2 deletions management/server/http/api/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -745,9 +745,15 @@ components:
type: boolean
example: true
peer:
description: Peer Identifier associated with route
description: Peer Identifier associated with route. This property can not be set together with `peer_groups`
type: string
example: chacbco6lnnbn6cg5s91
peer_groups:
description: Peers Group Identifier associated with route. This property can not be set together with `peer`
type: array
items:
type: string
example: chacbco6lnnbn6cg5s91
network:
description: Network range in CIDR format
type: string
Expand All @@ -773,7 +779,9 @@ components:
- description
- network_id
- enabled
- peer
# Only one property has to be set
#- peer
#- peer_groups
- network
- metric
- masquerade
Expand Down
16 changes: 11 additions & 5 deletions management/server/http/api/types.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 65 additions & 4 deletions management/server/http/routes_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,33 @@ func (h *RoutesHandler) CreateRoute(w http.ResponseWriter, r *http.Request) {
return
}

newRoute, err := h.accountManager.CreateRoute(account.Id, newPrefix.String(), req.Peer, req.Description, req.NetworkId, req.Masquerade, req.Metric, req.Groups, req.Enabled, user.Id)
peerId := ""
if req.Peer != nil {
peerId = *req.Peer
}

peerGroupIds := []string{}
if req.PeerGroups != nil {
peerGroupIds = *req.PeerGroups
}

if (peerId != "" && len(peerGroupIds) > 0) || (peerId == "" && len(peerGroupIds) == 0) {
util.WriteError(status.Errorf(status.InvalidArgument, "only one peer or peer_groups should be provided"), w)
return
}

// do not allow non Linux peers
if peer := account.GetPeer(peerId); peer != nil {
if peer.Meta.GoOS != "linux" {
util.WriteError(status.Errorf(status.InvalidArgument, "non-linux peers are non supported as network routes"), w)
return
}
}

newRoute, err := h.accountManager.CreateRoute(
account.Id, newPrefix.String(), peerId, peerGroupIds,
req.Description, req.NetworkId, req.Masquerade, req.Metric, req.Groups, req.Enabled, user.Id,
)
if err != nil {
util.WriteError(err, w)
return
Expand Down Expand Up @@ -135,19 +161,49 @@ func (h *RoutesHandler) UpdateRoute(w http.ResponseWriter, r *http.Request) {
return
}

if req.Peer != nil && req.PeerGroups != nil {
util.WriteError(status.Errorf(status.InvalidArgument, "only peer or peers_group should be provided"), w)
return
}

if req.Peer == nil && req.PeerGroups == nil {
util.WriteError(status.Errorf(status.InvalidArgument, "either peer or peers_group should be provided"), w)
return
}

peerID := ""
if req.Peer != nil {
peerID = *req.Peer
}

// do not allow non Linux peers
if peer := account.GetPeer(peerID); peer != nil {
if peer.Meta.GoOS != "linux" {
util.WriteError(status.Errorf(status.InvalidArgument, "non-linux peers are non supported as network routes"), w)
return
}
}

newRoute := &route.Route{
ID: routeID,
Network: newPrefix,
NetID: req.NetworkId,
NetworkType: prefixType,
Masquerade: req.Masquerade,
Peer: req.Peer,
Metric: req.Metric,
Description: req.Description,
Enabled: req.Enabled,
Groups: req.Groups,
}

if req.Peer != nil {
newRoute.Peer = peerID
}

if req.PeerGroups != nil {
newRoute.PeerGroups = *req.PeerGroups
}

err = h.accountManager.SaveRoute(account.Id, user.Id, newRoute)
if err != nil {
util.WriteError(err, w)
Expand Down Expand Up @@ -208,16 +264,21 @@ func (h *RoutesHandler) GetRoute(w http.ResponseWriter, r *http.Request) {
}

func toRouteResponse(serverRoute *route.Route) *api.Route {
return &api.Route{
route := &api.Route{
Id: serverRoute.ID,
Description: serverRoute.Description,
NetworkId: serverRoute.NetID,
Enabled: serverRoute.Enabled,
Peer: serverRoute.Peer,
Peer: &serverRoute.Peer,
Network: serverRoute.Network.String(),
NetworkType: serverRoute.NetworkType.String(),
Masquerade: serverRoute.Masquerade,
Metric: serverRoute.Metric,
Groups: serverRoute.Groups,
}

if len(serverRoute.PeerGroups) > 0 {
route.PeerGroups = &serverRoute.PeerGroups
}
return route
}
Loading

0 comments on commit 8118d60

Please sign in to comment.