This commit is contained in:
Maycon Santos
2025-10-10 09:58:22 +02:00
parent e67829d1d7
commit 213043fe7a
4 changed files with 79 additions and 30 deletions

View File

@@ -8,6 +8,7 @@ Load testing tool for the NetBird signal server.
- **Two exchange modes**:
- **Single message**: Each pair exchanges one message for validation
- **Continuous exchange**: Pairs continuously exchange messages for a specified duration (e.g., 30 seconds, 10 minutes)
- **TLS/HTTPS support**: Connect to TLS-enabled signal servers with optional certificate verification
- **Configurable message interval**: Control message send rate in continuous mode
- **Message exchange validation**: Validates encrypted body size > 0
- **Comprehensive metrics**: Tracks throughput, success/failure rates, and latency statistics
@@ -53,18 +54,34 @@ go build -o signal-loadtest
-test-duration 15m \
-log-level debug
# TLS server with valid certificate
./signal-loadtest \
-server https://signal.example.com:443 \
-pairs-per-sec 10 \
-total-pairs 50 \
-message-size 100
# TLS server with self-signed certificate
./signal-loadtest \
-server https://localhost:443 \
-pairs-per-sec 5 \
-total-pairs 10 \
-insecure-skip-verify \
-log-level debug
# Show help
./signal-loadtest -h
```
**Available Flags:**
- `-server`: Signal server URL (default: `http://localhost:10000`)
- `-server`: Signal server URL (http:// or https://) (default: `http://localhost:10000`)
- `-pairs-per-sec`: Peer pairs created per second (default: 10)
- `-total-pairs`: Total number of peer pairs (default: 100)
- `-message-size`: Message size in bytes (default: 100)
- `-test-duration`: Maximum test duration, 0 = unlimited (default: 0)
- `-exchange-duration`: Continuous exchange duration per pair, 0 = single message (default: 0)
- `-message-interval`: Interval between messages in continuous mode (default: 100ms)
- `-insecure-skip-verify`: Skip TLS certificate verification for self-signed certificates (default: false)
- `-log-level`: Log level: trace, debug, info, warn, error (default: info)
### Running Tests
@@ -156,6 +173,7 @@ func main() {
- **TestDuration**: Maximum test duration (optional, 0 = no limit)
- **ExchangeDuration**: Duration for continuous message exchange per pair (0 = single message)
- **MessageInterval**: Interval between messages in continuous mode (default: 100ms)
- **InsecureSkipVerify**: Skip TLS certificate verification (for self-signed certificates)
- **RampUpDuration**: Gradual ramp-up period (not yet implemented)
## Metrics

View File

@@ -2,11 +2,13 @@ package loadtest
import (
"context"
"crypto/tls"
"fmt"
"strings"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
@@ -24,9 +26,23 @@ type Client struct {
msgChannel chan *proto.EncryptedMessage
}
// ClientConfig holds optional configuration for the client
type ClientConfig struct {
InsecureSkipVerify bool
}
// NewClient creates a new signal client for load testing
func NewClient(serverURL, peerID string) (*Client, error) {
addr, opts, err := parseServerURL(serverURL)
return NewClientWithConfig(serverURL, peerID, nil)
}
// NewClientWithConfig creates a new signal client with custom TLS configuration
func NewClientWithConfig(serverURL, peerID string, config *ClientConfig) (*Client, error) {
if config == nil {
config = &ClientConfig{}
}
addr, opts, err := parseServerURL(serverURL, config.InsecureSkipVerify)
if err != nil {
return nil, fmt.Errorf("parse server URL: %w", err)
}
@@ -124,7 +140,7 @@ func (c *Client) receiveMessages() {
}
}
func parseServerURL(serverURL string) (string, []grpc.DialOption, error) {
func parseServerURL(serverURL string, insecureSkipVerify bool) (string, []grpc.DialOption, error) {
serverURL = strings.TrimSpace(serverURL)
if serverURL == "" {
return "", nil, fmt.Errorf("server URL is empty")
@@ -135,7 +151,11 @@ func parseServerURL(serverURL string) (string, []grpc.DialOption, error) {
if strings.HasPrefix(serverURL, "https://") {
addr = strings.TrimPrefix(serverURL, "https://")
return "", nil, fmt.Errorf("TLS support not yet implemented")
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: insecureSkipVerify,
}
opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
} else if strings.HasPrefix(serverURL, "http://") {
addr = strings.TrimPrefix(serverURL, "http://")
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))

View File

@@ -19,17 +19,19 @@ var (
testDuration time.Duration
exchangeDuration time.Duration
messageInterval time.Duration
insecureSkipVerify bool
logLevel string
)
func init() {
flag.StringVar(&serverURL, "server", "http://localhost:10000", "Signal server URL")
flag.StringVar(&serverURL, "server", "http://localhost:10000", "Signal server URL (http:// or https://)")
flag.IntVar(&pairsPerSecond, "pairs-per-sec", 10, "Number of peer pairs to create per second")
flag.IntVar(&totalPairs, "total-pairs", 100, "Total number of peer pairs to create")
flag.IntVar(&messageSize, "message-size", 100, "Size of test message in bytes")
flag.DurationVar(&testDuration, "test-duration", 0, "Maximum test duration (0 = unlimited)")
flag.DurationVar(&exchangeDuration, "exchange-duration", 0, "Duration for continuous message exchange per pair (0 = single message)")
flag.DurationVar(&messageInterval, "message-interval", 100*time.Millisecond, "Interval between messages in continuous mode")
flag.BoolVar(&insecureSkipVerify, "insecure-skip-verify", false, "Skip TLS certificate verification (use with self-signed certificates)")
flag.StringVar(&logLevel, "log-level", "info", "Log level (trace, debug, info, warn, error)")
}
@@ -51,6 +53,7 @@ func main() {
TestDuration: testDuration,
ExchangeDuration: exchangeDuration,
MessageInterval: messageInterval,
InsecureSkipVerify: insecureSkipVerify,
}
if err := validateConfig(config); err != nil {
@@ -64,6 +67,9 @@ func main() {
log.Infof(" Pairs per second: %d", config.PairsPerSecond)
log.Infof(" Total pairs: %d", config.TotalPairs)
log.Infof(" Message size: %d bytes", config.MessageSize)
if config.InsecureSkipVerify {
log.Warnf(" TLS certificate verification: DISABLED (insecure)")
}
if config.TestDuration > 0 {
log.Infof(" Test duration: %v", config.TestDuration)
}

View File

@@ -20,6 +20,7 @@ type LoadTestConfig struct {
ExchangeDuration time.Duration
MessageInterval time.Duration
RampUpDuration time.Duration
InsecureSkipVerify bool
}
// LoadTestMetrics metrics collected during the load test
@@ -138,13 +139,17 @@ func (lt *LoadTest) executePairExchange(pairID int) error {
senderID := fmt.Sprintf("sender-%d", pairID)
receiverID := fmt.Sprintf("receiver-%d", pairID)
sender, err := NewClient(lt.config.ServerURL, senderID)
clientConfig := &ClientConfig{
InsecureSkipVerify: lt.config.InsecureSkipVerify,
}
sender, err := NewClientWithConfig(lt.config.ServerURL, senderID, clientConfig)
if err != nil {
return fmt.Errorf("create sender: %w", err)
}
defer sender.Close()
receiver, err := NewClient(lt.config.ServerURL, receiverID)
receiver, err := NewClientWithConfig(lt.config.ServerURL, receiverID, clientConfig)
if err != nil {
return fmt.Errorf("create receiver: %w", err)
}