slackhq_nebula/e2e/router/hostmap.go

146 lines
3.6 KiB
Go

//go:build e2e_testing
// +build e2e_testing
package router
import (
"fmt"
"sort"
"strings"
"github.com/slackhq/nebula"
"github.com/slackhq/nebula/iputil"
)
type edge struct {
from string
to string
dual bool
}
func renderHostmaps(controls ...*nebula.Control) string {
var lines []*edge
r := "graph TB\n"
for _, c := range controls {
sr, se := renderHostmap(c)
r += sr
for _, e := range se {
add := true
// Collapse duplicate edges into a bi-directionally connected edge
for _, ge := range lines {
if e.to == ge.from && e.from == ge.to {
add = false
ge.dual = true
break
}
}
if add {
lines = append(lines, e)
}
}
}
for _, line := range lines {
if line.dual {
r += fmt.Sprintf("\t%v <--> %v\n", line.from, line.to)
} else {
r += fmt.Sprintf("\t%v --> %v\n", line.from, line.to)
}
}
return r
}
func renderHostmap(c *nebula.Control) (string, []*edge) {
var lines []string
var globalLines []*edge
clusterName := strings.Trim(c.GetCert().Details.Name, " ")
clusterVpnIp := c.GetCert().Details.Ips[0].IP
r := fmt.Sprintf("\tsubgraph %s[\"%s (%s)\"]\n", clusterName, clusterName, clusterVpnIp)
hm := c.GetHostmap()
hm.RLock()
defer hm.RUnlock()
// Draw the vpn to index nodes
r += fmt.Sprintf("\t\tsubgraph %s.hosts[\"Hosts (vpn ip to index)\"]\n", clusterName)
hosts := sortedHosts(hm.Hosts)
for _, vpnIp := range hosts {
hi := hm.Hosts[vpnIp]
r += fmt.Sprintf("\t\t\t%v.%v[\"%v\"]\n", clusterName, vpnIp, vpnIp)
lines = append(lines, fmt.Sprintf("%v.%v --> %v.%v", clusterName, vpnIp, clusterName, hi.GetLocalIndex()))
rs := hi.GetRelayState()
for _, relayIp := range rs.CopyRelayIps() {
lines = append(lines, fmt.Sprintf("%v.%v --> %v.%v", clusterName, vpnIp, clusterName, relayIp))
}
for _, relayIp := range rs.CopyRelayForIdxs() {
lines = append(lines, fmt.Sprintf("%v.%v --> %v.%v", clusterName, vpnIp, clusterName, relayIp))
}
}
r += "\t\tend\n"
// Draw the relay hostinfos
if len(hm.Relays) > 0 {
r += fmt.Sprintf("\t\tsubgraph %s.relays[\"Relays (relay index to hostinfo)\"]\n", clusterName)
for relayIndex, hi := range hm.Relays {
r += fmt.Sprintf("\t\t\t%v.%v[\"%v\"]\n", clusterName, relayIndex, relayIndex)
lines = append(lines, fmt.Sprintf("%v.%v --> %v.%v", clusterName, relayIndex, clusterName, hi.GetLocalIndex()))
}
r += "\t\tend\n"
}
// Draw the local index to relay or remote index nodes
r += fmt.Sprintf("\t\tsubgraph indexes.%s[\"Indexes (index to hostinfo)\"]\n", clusterName)
indexes := sortedIndexes(hm.Indexes)
for _, idx := range indexes {
hi, ok := hm.Indexes[idx]
if ok {
r += fmt.Sprintf("\t\t\t%v.%v[\"%v (%v)\"]\n", clusterName, idx, idx, hi.GetVpnIp())
remoteClusterName := strings.Trim(hi.GetCert().Details.Name, " ")
globalLines = append(globalLines, &edge{from: fmt.Sprintf("%v.%v", clusterName, idx), to: fmt.Sprintf("%v.%v", remoteClusterName, hi.GetRemoteIndex())})
_ = hi
}
}
r += "\t\tend\n"
// Add the edges inside this host
for _, line := range lines {
r += fmt.Sprintf("\t\t%v\n", line)
}
r += "\tend\n"
return r, globalLines
}
func sortedHosts(hosts map[iputil.VpnIp]*nebula.HostInfo) []iputil.VpnIp {
keys := make([]iputil.VpnIp, 0, len(hosts))
for key := range hosts {
keys = append(keys, key)
}
sort.SliceStable(keys, func(i, j int) bool {
return keys[i] > keys[j]
})
return keys
}
func sortedIndexes(indexes map[uint32]*nebula.HostInfo) []uint32 {
keys := make([]uint32, 0, len(indexes))
for key := range indexes {
keys = append(keys, key)
}
sort.SliceStable(keys, func(i, j int) bool {
return keys[i] > keys[j]
})
return keys
}