mirror of
https://github.com/henrygd/beszel.git
synced 2026-09-25 19:07:47 +02:00
Compare commits
2 Commits
container-
...
3dbc48727e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3dbc48727e | ||
|
|
85ac2e5e9a |
@@ -54,7 +54,7 @@ type dockerManager struct {
|
|||||||
buf *bytes.Buffer // Buffer to store and read response bodies
|
buf *bytes.Buffer // Buffer to store and read response bodies
|
||||||
decoder *json.Decoder // Reusable JSON decoder that reads from buf
|
decoder *json.Decoder // Reusable JSON decoder that reads from buf
|
||||||
apiStats *container.ApiStats // Reusable API stats object
|
apiStats *container.ApiStats // Reusable API stats object
|
||||||
containerExclude []string // Patterns to exclude containers by name (supports wildcards)
|
excludeContainers []string // Patterns to exclude containers by name
|
||||||
|
|
||||||
// Cache-time-aware tracking for CPU stats (similar to cpu.go)
|
// Cache-time-aware tracking for CPU stats (similar to cpu.go)
|
||||||
// Maps cache time intervals to container-specific CPU usage tracking
|
// Maps cache time intervals to container-specific CPU usage tracking
|
||||||
@@ -96,13 +96,12 @@ func (d *dockerManager) dequeue() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// shouldExcludeContainer checks if a container name matches any exclusion pattern using path.Match
|
// shouldExcludeContainer checks if a container name matches any exclusion pattern
|
||||||
func (dm *dockerManager) shouldExcludeContainer(name string) bool {
|
func (dm *dockerManager) shouldExcludeContainer(name string) bool {
|
||||||
if len(dm.containerExclude) == 0 {
|
if len(dm.excludeContainers) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, pattern := range dm.containerExclude {
|
for _, pattern := range dm.excludeContainers {
|
||||||
// Use path.Match for wildcard support
|
|
||||||
if match, _ := path.Match(pattern, name); match {
|
if match, _ := path.Match(pattern, name); match {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -138,15 +137,9 @@ func (dm *dockerManager) getDockerStats(cacheTimeMs uint16) ([]*container.Stats,
|
|||||||
for _, ctr := range dm.apiContainerList {
|
for _, ctr := range dm.apiContainerList {
|
||||||
ctr.IdShort = ctr.Id[:12]
|
ctr.IdShort = ctr.Id[:12]
|
||||||
|
|
||||||
// Extract container name and check if it should be excluded
|
|
||||||
name := ctr.Names[0]
|
|
||||||
if len(name) > 0 && name[0] == '/' {
|
|
||||||
name = name[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip this container if it matches the exclusion pattern
|
// Skip this container if it matches the exclusion pattern
|
||||||
if dm.shouldExcludeContainer(name) {
|
if dm.shouldExcludeContainer(ctr.Names[0][1:]) {
|
||||||
slog.Debug("Excluding container", "name", name, "patterns", dm.containerExclude)
|
slog.Debug("Excluding container", "name", ctr.Names[0][1:])
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -532,20 +525,17 @@ func newDockerManager(a *Agent) *dockerManager {
|
|||||||
userAgent: "Docker-Client/",
|
userAgent: "Docker-Client/",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read container exclusion patterns from environment variable (comma-separated, supports wildcards)
|
// Read container exclusion patterns from environment variable
|
||||||
var containerExclude []string
|
var excludeContainers []string
|
||||||
if excludeStr, set := GetEnv("CONTAINER_EXCLUDE"); set && excludeStr != "" {
|
if excludeStr, set := GetEnv("EXCLUDE_CONTAINERS"); set && excludeStr != "" {
|
||||||
// Split by comma and trim whitespace
|
parts := strings.SplitSeq(excludeStr, ",")
|
||||||
parts := strings.Split(excludeStr, ",")
|
for part := range parts {
|
||||||
for _, part := range parts {
|
|
||||||
trimmed := strings.TrimSpace(part)
|
trimmed := strings.TrimSpace(part)
|
||||||
if trimmed != "" {
|
if trimmed != "" {
|
||||||
containerExclude = append(containerExclude, trimmed)
|
excludeContainers = append(excludeContainers, trimmed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(containerExclude) > 0 {
|
slog.Info("EXCLUDE_CONTAINERS", "patterns", excludeContainers)
|
||||||
slog.Info("Container exclusion patterns set", "patterns", containerExclude)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
manager := &dockerManager{
|
manager := &dockerManager{
|
||||||
@@ -557,7 +547,7 @@ func newDockerManager(a *Agent) *dockerManager {
|
|||||||
sem: make(chan struct{}, 5),
|
sem: make(chan struct{}, 5),
|
||||||
apiContainerList: []*container.ApiInfo{},
|
apiContainerList: []*container.ApiInfo{},
|
||||||
apiStats: &container.ApiStats{},
|
apiStats: &container.ApiStats{},
|
||||||
containerExclude: containerExclude,
|
excludeContainers: excludeContainers,
|
||||||
|
|
||||||
// Initialize cache-time-aware tracking structures
|
// Initialize cache-time-aware tracking structures
|
||||||
lastCpuContainer: make(map[uint16]map[string]uint64),
|
lastCpuContainer: make(map[uint16]map[string]uint64),
|
||||||
|
|||||||
@@ -1196,7 +1196,7 @@ func TestShouldExcludeContainer(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
dm := &dockerManager{
|
dm := &dockerManager{
|
||||||
containerExclude: tt.patterns,
|
excludeContainers: tt.patterns,
|
||||||
}
|
}
|
||||||
result := dm.shouldExcludeContainer(tt.containerName)
|
result := dm.shouldExcludeContainer(tt.containerName)
|
||||||
assert.Equal(t, tt.expected, result)
|
assert.Equal(t, tt.expected, result)
|
||||||
|
|||||||
@@ -49,7 +49,12 @@ func (gm *GPUManager) updateIntelFromStats(sample *intelGpuStats) bool {
|
|||||||
|
|
||||||
// collectIntelStats executes intel_gpu_top in text mode (-l) and parses the output
|
// collectIntelStats executes intel_gpu_top in text mode (-l) and parses the output
|
||||||
func (gm *GPUManager) collectIntelStats() (err error) {
|
func (gm *GPUManager) collectIntelStats() (err error) {
|
||||||
cmd := exec.Command(intelGpuStatsCmd, "-s", intelGpuStatsInterval, "-l")
|
// Build command arguments, optionally selecting a device via -d
|
||||||
|
args := []string{"-s", intelGpuStatsInterval, "-l"}
|
||||||
|
if dev, ok := GetEnv("INTEL_GPU_DEVICE"); ok && dev != "" {
|
||||||
|
args = append(args, "-d", dev)
|
||||||
|
}
|
||||||
|
cmd := exec.Command(intelGpuStatsCmd, args...)
|
||||||
// Avoid blocking if intel_gpu_top writes to stderr
|
// Avoid blocking if intel_gpu_top writes to stderr
|
||||||
cmd.Stderr = io.Discard
|
cmd.Stderr = io.Discard
|
||||||
stdout, err := cmd.StdoutPipe()
|
stdout, err := cmd.StdoutPipe()
|
||||||
|
|||||||
@@ -4,8 +4,10 @@
|
|||||||
package agent
|
package agent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -1624,3 +1626,42 @@ func TestParseIntelData(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIntelCollectorDeviceEnv(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
t.Setenv("PATH", dir)
|
||||||
|
|
||||||
|
// Prepare a file to capture args
|
||||||
|
argsFile := filepath.Join(dir, "args.txt")
|
||||||
|
|
||||||
|
// Create a fake intel_gpu_top that records its arguments and prints minimal valid output
|
||||||
|
scriptPath := filepath.Join(dir, "intel_gpu_top")
|
||||||
|
script := fmt.Sprintf(`#!/bin/sh
|
||||||
|
echo "$@" > %s
|
||||||
|
echo "Freq MHz IRQ RC6 Power W IMC MiB/s RCS VCS"
|
||||||
|
echo " req act /s %% gpu pkg rd wr %% se wa %% se wa"
|
||||||
|
echo "226 223 338 58 2.00 2.69 1820 965 0.00 0 0 0.00 0 0"
|
||||||
|
echo "189 187 412 67 1.80 2.45 1950 823 8.50 2 1 15.00 1 0"
|
||||||
|
`, argsFile)
|
||||||
|
if err := os.WriteFile(scriptPath, []byte(script), 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set device selector via prefixed env var
|
||||||
|
t.Setenv("BESZEL_AGENT_INTEL_GPU_DEVICE", "sriov")
|
||||||
|
|
||||||
|
gm := &GPUManager{GpuDataMap: make(map[string]*system.GPUData)}
|
||||||
|
if err := gm.collectIntelStats(); err != nil {
|
||||||
|
t.Fatalf("collectIntelStats error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that -d sriov was passed
|
||||||
|
data, err := os.ReadFile(argsFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed reading args file: %v", err)
|
||||||
|
}
|
||||||
|
argsStr := strings.TrimSpace(string(data))
|
||||||
|
require.Contains(t, argsStr, "-d sriov")
|
||||||
|
require.Contains(t, argsStr, "-s ")
|
||||||
|
require.Contains(t, argsStr, "-l")
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
## 0.15.4
|
||||||
|
|
||||||
|
- Add `INTEL_GPU_DEVICE` environment variable to select Intel GPU device. (#1285)
|
||||||
|
|
||||||
## 0.15.3
|
## 0.15.3
|
||||||
|
|
||||||
- Improve parsing of edge case S.M.A.R.T. power on times. (#1347)
|
- Improve parsing of edge case S.M.A.R.T. power on times. (#1347)
|
||||||
|
|||||||
Reference in New Issue
Block a user