mirror of
https://github.com/henrygd/beszel.git
synced 2025-12-17 02:36:17 +01:00
[Feature] Add env var to exclude containers from being monitored (#1352)
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -53,6 +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)
|
||||||
|
|
||||||
// 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
|
||||||
@@ -94,6 +96,20 @@ func (d *dockerManager) dequeue() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// shouldExcludeContainer checks if a container name matches any exclusion pattern using path.Match
|
||||||
|
func (dm *dockerManager) shouldExcludeContainer(name string) bool {
|
||||||
|
if len(dm.containerExclude) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, pattern := range dm.containerExclude {
|
||||||
|
// Use path.Match for wildcard support
|
||||||
|
if match, _ := path.Match(pattern, name); match {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Returns stats for all running containers with cache-time-aware delta tracking
|
// Returns stats for all running containers with cache-time-aware delta tracking
|
||||||
func (dm *dockerManager) getDockerStats(cacheTimeMs uint16) ([]*container.Stats, error) {
|
func (dm *dockerManager) getDockerStats(cacheTimeMs uint16) ([]*container.Stats, error) {
|
||||||
resp, err := dm.client.Get("http://localhost/containers/json")
|
resp, err := dm.client.Get("http://localhost/containers/json")
|
||||||
@@ -121,6 +137,19 @@ 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
|
||||||
|
if dm.shouldExcludeContainer(name) {
|
||||||
|
slog.Debug("Excluding container", "name", name, "patterns", dm.containerExclude)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
dm.validIds[ctr.IdShort] = struct{}{}
|
dm.validIds[ctr.IdShort] = struct{}{}
|
||||||
// check if container is less than 1 minute old (possible restart)
|
// check if container is less than 1 minute old (possible restart)
|
||||||
// note: can't use Created field because it's not updated on restart
|
// note: can't use Created field because it's not updated on restart
|
||||||
@@ -503,6 +532,22 @@ func newDockerManager(a *Agent) *dockerManager {
|
|||||||
userAgent: "Docker-Client/",
|
userAgent: "Docker-Client/",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read container exclusion patterns from environment variable (comma-separated, supports wildcards)
|
||||||
|
var containerExclude []string
|
||||||
|
if excludeStr, set := GetEnv("CONTAINER_EXCLUDE"); set && excludeStr != "" {
|
||||||
|
// Split by comma and trim whitespace
|
||||||
|
parts := strings.Split(excludeStr, ",")
|
||||||
|
for _, part := range parts {
|
||||||
|
trimmed := strings.TrimSpace(part)
|
||||||
|
if trimmed != "" {
|
||||||
|
containerExclude = append(containerExclude, trimmed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(containerExclude) > 0 {
|
||||||
|
slog.Info("Container exclusion patterns set", "patterns", containerExclude)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
manager := &dockerManager{
|
manager := &dockerManager{
|
||||||
client: &http.Client{
|
client: &http.Client{
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
@@ -512,6 +557,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,
|
||||||
|
|
||||||
// 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),
|
||||||
|
|||||||
@@ -1099,3 +1099,107 @@ func TestAllocateBuffer(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestShouldExcludeContainer(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
containerName string
|
||||||
|
patterns []string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty patterns excludes nothing",
|
||||||
|
containerName: "any-container",
|
||||||
|
patterns: []string{},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "exact match - excluded",
|
||||||
|
containerName: "test-web",
|
||||||
|
patterns: []string{"test-web", "test-api"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "exact match - not excluded",
|
||||||
|
containerName: "prod-web",
|
||||||
|
patterns: []string{"test-web", "test-api"},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wildcard prefix match - excluded",
|
||||||
|
containerName: "test-web",
|
||||||
|
patterns: []string{"test-*"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wildcard prefix match - not excluded",
|
||||||
|
containerName: "prod-web",
|
||||||
|
patterns: []string{"test-*"},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wildcard suffix match - excluded",
|
||||||
|
containerName: "myapp-staging",
|
||||||
|
patterns: []string{"*-staging"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wildcard suffix match - not excluded",
|
||||||
|
containerName: "myapp-prod",
|
||||||
|
patterns: []string{"*-staging"},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wildcard both sides match - excluded",
|
||||||
|
containerName: "test-myapp-staging",
|
||||||
|
patterns: []string{"*-myapp-*"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wildcard both sides match - not excluded",
|
||||||
|
containerName: "prod-yourapp-live",
|
||||||
|
patterns: []string{"*-myapp-*"},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple patterns - matches first",
|
||||||
|
containerName: "test-container",
|
||||||
|
patterns: []string{"test-*", "*-staging"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple patterns - matches second",
|
||||||
|
containerName: "myapp-staging",
|
||||||
|
patterns: []string{"test-*", "*-staging"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple patterns - no match",
|
||||||
|
containerName: "prod-web",
|
||||||
|
patterns: []string{"test-*", "*-staging"},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mixed exact and wildcard - exact match",
|
||||||
|
containerName: "temp-container",
|
||||||
|
patterns: []string{"temp-container", "test-*"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mixed exact and wildcard - wildcard match",
|
||||||
|
containerName: "test-web",
|
||||||
|
patterns: []string{"temp-container", "test-*"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
dm := &dockerManager{
|
||||||
|
containerExclude: tt.patterns,
|
||||||
|
}
|
||||||
|
result := dm.shouldExcludeContainer(tt.containerName)
|
||||||
|
assert.Equal(t, tt.expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user