Skip to content
Open
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
103 changes: 103 additions & 0 deletions cmd/prism-loadtest/cmd/serve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package cmd

import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"

"github.com/spf13/cobra"

"github.com/jrepp/prism-data-layer/cmd/prism-loadtest/server"
)

var (
servePort int
)

var serveCmd = &cobra.Command{
Use: "serve",
Short: "Run prism-loadtest as a server with HTTP API and WebSocket streaming",
Long: `Start prism-loadtest in server mode, exposing:
- HTTP API for starting/stopping load tests
- WebSocket endpoint for real-time metrics streaming
- Embedded dashboard for visualization

The server allows remote control of load tests and provides real-time
metrics via WebSocket for D3-based visualizations.

Example:
# Start server on default port (8091)
prism-loadtest serve

# Start server on custom port
prism-loadtest serve --port 9000

# Access dashboard
open http://localhost:8091/dashboard

API Endpoints:
POST /api/loadtest/start - Start a new load test
POST /api/loadtest/stop/:id - Stop a running test
GET /api/loadtest/status/:id - Get test status
GET /api/loadtest/list - List all tests
WS /ws/metrics/:id - Stream metrics for test
GET /dashboard - Embedded dashboard UI
`,
RunE: runServe,
}

func init() {
rootCmd.AddCommand(serveCmd)
serveCmd.Flags().IntVar(&servePort, "port", 8091, "HTTP server port")
}

func runServe(cmd *cobra.Command, args []string) error {
fmt.Printf("Starting prism-loadtest server on port %d...\n", servePort)
fmt.Printf("Dashboard: http://localhost:%d/dashboard\n", servePort)
fmt.Printf("API: http://localhost:%d/api/loadtest/...\n", servePort)
fmt.Printf("WebSocket: ws://localhost:%d/ws/metrics/:testId\n\n", servePort)

// Create backend configuration
backendConfig := server.BackendConfig{
RedisAddr: redisAddr,
RedisPassword: redisPassword,
RedisDB: redisDB,
NATSServers: natsServers,
}

// Create and start server
srv := server.NewServer(servePort, backendConfig)

// Start server in background
errChan := make(chan error, 1)
go func() {
if err := srv.Start(); err != nil {
errChan <- err
}
}()

// Wait for interrupt signal
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

select {
case err := <-errChan:
return fmt.Errorf("server failed to start: %w", err)
case sig := <-sigChan:
fmt.Printf("\nReceived signal %v, shutting down...\n", sig)
}

// Graceful shutdown
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

if err := srv.Shutdown(ctx); err != nil {
return fmt.Errorf("server shutdown failed: %w", err)
}

fmt.Println("Server stopped gracefully")
return nil
}
Loading