1
0
mirror of https://github.com/juanfont/headscale.git synced 2025-09-06 17:54:31 +02:00

cmd/hi: lint and format

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
Kristoffer Dalby 2025-08-06 08:36:17 +02:00
parent dc28698551
commit f6c1348835
No known key found for this signature in database
3 changed files with 42 additions and 38 deletions

View File

@ -141,6 +141,7 @@ func runTestContainer(ctx context.Context, config *RunConfig) error {
log.Printf("Container %s exceeded memory limit: %.1f MB > %.1f MB", log.Printf("Container %s exceeded memory limit: %.1f MB > %.1f MB",
violation.ContainerName, violation.MaxMemoryMB, violation.LimitMB) violation.ContainerName, violation.MaxMemoryMB, violation.LimitMB)
} }
return fmt.Errorf("test failed: %d container(s) exceeded memory limits", len(violations)) return fmt.Errorf("test failed: %d container(s) exceeded memory limits", len(violations))
} }
} }

View File

@ -24,9 +24,9 @@ type RunConfig struct {
KeepOnFailure bool `flag:"keep-on-failure,default=false,Keep containers on test failure"` KeepOnFailure bool `flag:"keep-on-failure,default=false,Keep containers on test failure"`
LogsDir string `flag:"logs-dir,default=control_logs,Control logs directory"` LogsDir string `flag:"logs-dir,default=control_logs,Control logs directory"`
Verbose bool `flag:"verbose,default=false,Verbose output"` Verbose bool `flag:"verbose,default=false,Verbose output"`
Stats bool `flag:"stats,default=false,Collect and display container resource usage statistics"` Stats bool `flag:"stats,default=false,Collect and display container resource usage statistics"`
HSMemoryLimit float64 `flag:"hs-memory-limit,default=0,Fail test if any Headscale container exceeds this memory limit in MB (0 = disabled)"` HSMemoryLimit float64 `flag:"hs-memory-limit,default=0,Fail test if any Headscale container exceeds this memory limit in MB (0 = disabled)"`
TSMemoryLimit float64 `flag:"ts-memory-limit,default=0,Fail test if any Tailscale container exceeds this memory limit in MB (0 = disabled)"` TSMemoryLimit float64 `flag:"ts-memory-limit,default=0,Fail test if any Tailscale container exceeds this memory limit in MB (0 = disabled)"`
} }
// runIntegrationTest executes the integration test workflow. // runIntegrationTest executes the integration test workflow.

View File

@ -3,6 +3,7 @@ package main
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"log" "log"
"sort" "sort"
@ -17,7 +18,7 @@ import (
"github.com/docker/docker/client" "github.com/docker/docker/client"
) )
// ContainerStats represents statistics for a single container // ContainerStats represents statistics for a single container.
type ContainerStats struct { type ContainerStats struct {
ContainerID string ContainerID string
ContainerName string ContainerName string
@ -25,14 +26,14 @@ type ContainerStats struct {
mutex sync.RWMutex mutex sync.RWMutex
} }
// StatsSample represents a single stats measurement // StatsSample represents a single stats measurement.
type StatsSample struct { type StatsSample struct {
Timestamp time.Time Timestamp time.Time
CPUUsage float64 // CPU usage percentage CPUUsage float64 // CPU usage percentage
MemoryMB float64 // Memory usage in MB MemoryMB float64 // Memory usage in MB
} }
// StatsCollector manages collection of container statistics // StatsCollector manages collection of container statistics.
type StatsCollector struct { type StatsCollector struct {
client *client.Client client *client.Client
containers map[string]*ContainerStats containers map[string]*ContainerStats
@ -42,7 +43,7 @@ type StatsCollector struct {
collectionStarted bool collectionStarted bool
} }
// NewStatsCollector creates a new stats collector instance // NewStatsCollector creates a new stats collector instance.
func NewStatsCollector() (*StatsCollector, error) { func NewStatsCollector() (*StatsCollector, error) {
cli, err := createDockerClient() cli, err := createDockerClient()
if err != nil { if err != nil {
@ -56,13 +57,13 @@ func NewStatsCollector() (*StatsCollector, error) {
}, nil }, nil
} }
// StartCollection begins monitoring all containers and collecting stats for hs- and ts- containers with matching run ID // StartCollection begins monitoring all containers and collecting stats for hs- and ts- containers with matching run ID.
func (sc *StatsCollector) StartCollection(ctx context.Context, runID string, verbose bool) error { func (sc *StatsCollector) StartCollection(ctx context.Context, runID string, verbose bool) error {
sc.mutex.Lock() sc.mutex.Lock()
defer sc.mutex.Unlock() defer sc.mutex.Unlock()
if sc.collectionStarted { if sc.collectionStarted {
return fmt.Errorf("stats collection already started") return errors.New("stats collection already started")
} }
sc.collectionStarted = true sc.collectionStarted = true
@ -82,7 +83,7 @@ func (sc *StatsCollector) StartCollection(ctx context.Context, runID string, ver
return nil return nil
} }
// StopCollection stops all stats collection // StopCollection stops all stats collection.
func (sc *StatsCollector) StopCollection() { func (sc *StatsCollector) StopCollection() {
// Check if already stopped without holding lock // Check if already stopped without holding lock
sc.mutex.RLock() sc.mutex.RLock()
@ -104,7 +105,7 @@ func (sc *StatsCollector) StopCollection() {
sc.mutex.Unlock() sc.mutex.Unlock()
} }
// monitorExistingContainers checks for existing containers that match our criteria // monitorExistingContainers checks for existing containers that match our criteria.
func (sc *StatsCollector) monitorExistingContainers(ctx context.Context, runID string, verbose bool) { func (sc *StatsCollector) monitorExistingContainers(ctx context.Context, runID string, verbose bool) {
defer sc.wg.Done() defer sc.wg.Done()
@ -123,7 +124,7 @@ func (sc *StatsCollector) monitorExistingContainers(ctx context.Context, runID s
} }
} }
// monitorDockerEvents listens for container start events and begins monitoring relevant containers // monitorDockerEvents listens for container start events and begins monitoring relevant containers.
func (sc *StatsCollector) monitorDockerEvents(ctx context.Context, runID string, verbose bool) { func (sc *StatsCollector) monitorDockerEvents(ctx context.Context, runID string, verbose bool) {
defer sc.wg.Done() defer sc.wg.Done()
@ -171,7 +172,7 @@ func (sc *StatsCollector) monitorDockerEvents(ctx context.Context, runID string,
} }
} }
// shouldMonitorContainer determines if a container should be monitored // shouldMonitorContainer determines if a container should be monitored.
func (sc *StatsCollector) shouldMonitorContainer(cont types.Container, runID string) bool { func (sc *StatsCollector) shouldMonitorContainer(cont types.Container, runID string) bool {
// Check if it has the correct run ID label // Check if it has the correct run ID label
if cont.Labels == nil || cont.Labels["hi.run-id"] != runID { if cont.Labels == nil || cont.Labels["hi.run-id"] != runID {
@ -189,7 +190,7 @@ func (sc *StatsCollector) shouldMonitorContainer(cont types.Container, runID str
return false return false
} }
// startStatsForContainer begins stats collection for a specific container // startStatsForContainer begins stats collection for a specific container.
func (sc *StatsCollector) startStatsForContainer(ctx context.Context, containerID, containerName string, verbose bool) { func (sc *StatsCollector) startStatsForContainer(ctx context.Context, containerID, containerName string, verbose bool) {
containerName = strings.TrimPrefix(containerName, "/") containerName = strings.TrimPrefix(containerName, "/")
@ -215,7 +216,7 @@ func (sc *StatsCollector) startStatsForContainer(ctx context.Context, containerI
go sc.collectStatsForContainer(ctx, containerID, verbose) go sc.collectStatsForContainer(ctx, containerID, verbose)
} }
// collectStatsForContainer collects stats for a specific container using Docker API streaming // collectStatsForContainer collects stats for a specific container using Docker API streaming.
func (sc *StatsCollector) collectStatsForContainer(ctx context.Context, containerID string, verbose bool) { func (sc *StatsCollector) collectStatsForContainer(ctx context.Context, containerID string, verbose bool) {
defer sc.wg.Done() defer sc.wg.Done()
@ -284,7 +285,7 @@ func (sc *StatsCollector) collectStatsForContainer(ctx context.Context, containe
} }
} }
// calculateCPUPercent calculates CPU usage percentage from Docker stats // calculateCPUPercent calculates CPU usage percentage from Docker stats.
func calculateCPUPercent(prevStats, stats *container.Stats) float64 { func calculateCPUPercent(prevStats, stats *container.Stats) float64 {
// CPU calculation based on Docker's implementation // CPU calculation based on Docker's implementation
cpuDelta := float64(stats.CPUStats.CPUUsage.TotalUsage) - float64(prevStats.CPUStats.CPUUsage.TotalUsage) cpuDelta := float64(stats.CPUStats.CPUUsage.TotalUsage) - float64(prevStats.CPUStats.CPUUsage.TotalUsage)
@ -297,12 +298,14 @@ func calculateCPUPercent(prevStats, stats *container.Stats) float64 {
// Fallback: if PercpuUsage is not available, assume 1 CPU // Fallback: if PercpuUsage is not available, assume 1 CPU
numCPUs = 1.0 numCPUs = 1.0
} }
return (cpuDelta / systemDelta) * numCPUs * 100.0 return (cpuDelta / systemDelta) * numCPUs * 100.0
} }
return 0.0 return 0.0
} }
// ContainerStatsSummary represents summary statistics for a container // ContainerStatsSummary represents summary statistics for a container.
type ContainerStatsSummary struct { type ContainerStatsSummary struct {
ContainerName string ContainerName string
SampleCount int SampleCount int
@ -310,21 +313,21 @@ type ContainerStatsSummary struct {
Memory StatsSummary Memory StatsSummary
} }
// MemoryViolation represents a container that exceeded the memory limit // MemoryViolation represents a container that exceeded the memory limit.
type MemoryViolation struct { type MemoryViolation struct {
ContainerName string ContainerName string
MaxMemoryMB float64 MaxMemoryMB float64
LimitMB float64 LimitMB float64
} }
// StatsSummary represents min, max, and average for a metric // StatsSummary represents min, max, and average for a metric.
type StatsSummary struct { type StatsSummary struct {
Min float64 Min float64
Max float64 Max float64
Average float64 Average float64
} }
// GetSummary returns a summary of collected statistics // GetSummary returns a summary of collected statistics.
func (sc *StatsCollector) GetSummary() []ContainerStatsSummary { func (sc *StatsCollector) GetSummary() []ContainerStatsSummary {
// Take snapshot of container references without holding main lock long // Take snapshot of container references without holding main lock long
sc.mutex.RLock() sc.mutex.RLock()
@ -375,7 +378,7 @@ func (sc *StatsCollector) GetSummary() []ContainerStatsSummary {
return summaries return summaries
} }
// calculateStatsSummary calculates min, max, and average for a slice of values // calculateStatsSummary calculates min, max, and average for a slice of values.
func calculateStatsSummary(values []float64) StatsSummary { func calculateStatsSummary(values []float64) StatsSummary {
if len(values) == 0 { if len(values) == 0 {
return StatsSummary{} return StatsSummary{}
@ -402,7 +405,7 @@ func calculateStatsSummary(values []float64) StatsSummary {
} }
} }
// PrintSummary prints the statistics summary to the console // PrintSummary prints the statistics summary to the console.
func (sc *StatsCollector) PrintSummary() { func (sc *StatsCollector) PrintSummary() {
summaries := sc.GetSummary() summaries := sc.GetSummary()
@ -424,7 +427,7 @@ func (sc *StatsCollector) PrintSummary() {
} }
} }
// CheckMemoryLimits checks if any containers exceeded their memory limits // CheckMemoryLimits checks if any containers exceeded their memory limits.
func (sc *StatsCollector) CheckMemoryLimits(hsLimitMB, tsLimitMB float64) []MemoryViolation { func (sc *StatsCollector) CheckMemoryLimits(hsLimitMB, tsLimitMB float64) []MemoryViolation {
if hsLimitMB <= 0 && tsLimitMB <= 0 { if hsLimitMB <= 0 && tsLimitMB <= 0 {
return nil return nil
@ -455,13 +458,13 @@ func (sc *StatsCollector) CheckMemoryLimits(hsLimitMB, tsLimitMB float64) []Memo
return violations return violations
} }
// PrintSummaryAndCheckLimits prints the statistics summary and returns memory violations if any // PrintSummaryAndCheckLimits prints the statistics summary and returns memory violations if any.
func (sc *StatsCollector) PrintSummaryAndCheckLimits(hsLimitMB, tsLimitMB float64) []MemoryViolation { func (sc *StatsCollector) PrintSummaryAndCheckLimits(hsLimitMB, tsLimitMB float64) []MemoryViolation {
sc.PrintSummary() sc.PrintSummary()
return sc.CheckMemoryLimits(hsLimitMB, tsLimitMB) return sc.CheckMemoryLimits(hsLimitMB, tsLimitMB)
} }
// Close closes the stats collector and cleans up resources // Close closes the stats collector and cleans up resources.
func (sc *StatsCollector) Close() error { func (sc *StatsCollector) Close() error {
sc.StopCollection() sc.StopCollection()
return sc.client.Close() return sc.client.Close()