mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-28 12:03:33 +00:00
add netbird embed experiment
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
// netbird-load launches N userspace NetBird nodes and has each one periodically
|
||||
// TCP-dial a peer to measure connect latency through the wireguard overlay.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// NB_SETUP_KEY=nb-... go run . -count 10 -interval 5s -target 100.x.y.z:22
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand/v2"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/netbirdio/netbird/client/embed"
|
||||
)
|
||||
|
||||
func main() {
|
||||
setupKey := flag.String("setupkey", os.Getenv("NB_SETUP_KEY"),
|
||||
"NetBird reusable setup key (or set NB_SETUP_KEY).")
|
||||
mgmt := flag.String("mgmt", os.Getenv("NB_MGMT_URL"),
|
||||
"NetBird management URL (empty = embed default, i.e. NetBird Cloud).")
|
||||
count := flag.Int("count", 10, "Number of embedded netbird nodes to launch.")
|
||||
interval := flag.Duration("interval", 5*time.Second, "Interval between actions per node.")
|
||||
hostnamePrefix := flag.String("hostname-prefix", "uncloud", "Hostname prefix for netbird nodes.")
|
||||
stateRoot := flag.String("state-dir", "./state", "Directory to keep netbird state (empty = a fresh temp dir).")
|
||||
target := flag.String("target", "", "TCP host:port to dial each tick (required).")
|
||||
flag.Parse()
|
||||
|
||||
if *setupKey == "" {
|
||||
log.Fatal("missing NetBird setup key: pass -setupkey or set NB_SETUP_KEY.")
|
||||
}
|
||||
if *target == "" {
|
||||
log.Fatal("missing -target: provide a TCP host:port reachable through the netbird network.")
|
||||
}
|
||||
|
||||
root := *stateRoot
|
||||
if root == "" {
|
||||
var err error
|
||||
root, err = os.MkdirTemp("", "netbird-load-*")
|
||||
if err != nil {
|
||||
log.Fatalf("mktemp: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(root)
|
||||
} else if err := os.MkdirAll(root, 0o700); err != nil {
|
||||
log.Fatalf("mkdir state dir: %v", err)
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < *count; i++ {
|
||||
wg.Add(1)
|
||||
go func(id int) {
|
||||
defer wg.Done()
|
||||
runNode(ctx, id, *setupKey, *mgmt, *hostnamePrefix, root, *target, *interval)
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func runNode(ctx context.Context, id int, setupKey, mgmt, prefix, stateRoot, target string, interval time.Duration) {
|
||||
logPrefix := fmt.Sprintf("[node-%02d] ", id)
|
||||
logger := log.New(os.Stderr, logPrefix, log.LstdFlags|log.Lmsgprefix)
|
||||
|
||||
nodeDir := filepath.Join(stateRoot, fmt.Sprintf("node-%02d", id))
|
||||
deviceName := fmt.Sprintf("%s-%02d", prefix, id)
|
||||
|
||||
client, err := embed.New(embed.Options{
|
||||
DeviceName: deviceName,
|
||||
SetupKey: setupKey,
|
||||
ManagementURL: mgmt,
|
||||
StatePath: nodeDir,
|
||||
ConfigPath: filepath.Join(nodeDir, "config"),
|
||||
// Silence per-node netbird chatter. Swap to os.Stderr to debug.
|
||||
LogOutput: io.Discard,
|
||||
//NoUserspace: true,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Printf("netbird new: %v", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
stopCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
_ = client.Stop(stopCtx)
|
||||
}()
|
||||
|
||||
upCtx, cancel := context.WithTimeout(ctx, 60*time.Second)
|
||||
defer cancel()
|
||||
if err := client.Start(upCtx); err != nil {
|
||||
logger.Printf("netbird start: %v", err)
|
||||
return
|
||||
}
|
||||
logger.Printf("up as %s", deviceName)
|
||||
|
||||
// Stagger ticks so all nodes don't fire in lockstep.
|
||||
time.Sleep(time.Duration(rand.Int64N(int64(interval))))
|
||||
|
||||
tick := time.NewTicker(interval)
|
||||
defer tick.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-tick.C:
|
||||
doDial(ctx, client, target, logger)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func doDial(ctx context.Context, c *embed.Client, target string, logger *log.Logger) {
|
||||
dctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
start := time.Now()
|
||||
conn, err := c.Dial(dctx, "tcp", target)
|
||||
if err != nil {
|
||||
logger.Printf("dial %s: %v", target, err)
|
||||
return
|
||||
}
|
||||
latency := time.Since(start)
|
||||
_ = conn.Close()
|
||||
logger.Printf("dial %s ok in %.1fms", target, float64(latency.Microseconds())/1000)
|
||||
}
|
||||
Reference in New Issue
Block a user