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

Add peer groups support for network routes #1150

Merged
merged 18 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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
Loading