add env for interval and exclude containers
This commit is contained in:
parent
23e78f2afe
commit
7530cc18eb
2 changed files with 76 additions and 33 deletions
|
@ -5,6 +5,9 @@ services:
|
|||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
#ports:
|
||||
# - "9788:9788"
|
||||
environment:
|
||||
- CHECK_INTERVAL=6h
|
||||
- EXCLUDE_CONTAINERS=immich_redis,mailcowdockerized-ofelia-mailcow-1
|
||||
restart: always
|
||||
networks:
|
||||
default:
|
||||
|
|
104
go/main.go
104
go/main.go
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -36,9 +37,18 @@ type StatusCache struct {
|
|||
|
||||
var (
|
||||
cache = &StatusCache{Data: []ImageStatus{}, LastCheck: time.Time{}}
|
||||
interval = 6 * time.Hour
|
||||
interval = 6 * time.Hour // Default wird ggf. überschrieben durch CHECK_INTERVAL
|
||||
excludeContainers = map[string]struct{}{}
|
||||
)
|
||||
|
||||
// Helper: image ohne ":tag" erhält "latest"
|
||||
func normalizeImageTag(tag string) string {
|
||||
if !strings.Contains(tag, ":") {
|
||||
return tag + ":latest"
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
func toRegistryImage(imageTag string) (string, error) {
|
||||
r := regexp.MustCompile(`^(?:(?P<registry>[^/]+)/)?(?P<repo>[^:]+)(?::(?P<tag>.+))?$`)
|
||||
match := r.FindStringSubmatch(imageTag)
|
||||
|
@ -66,13 +76,6 @@ func extractDigest(s string) string {
|
|||
return s
|
||||
}
|
||||
|
||||
func normalizeImageTag(tag string) string {
|
||||
if !strings.Contains(tag, ":") {
|
||||
return tag + ":latest"
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
func checkImageUpdates() {
|
||||
ctx := context.Background()
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
|
@ -107,55 +110,51 @@ func checkImageUpdates() {
|
|||
}
|
||||
}
|
||||
}
|
||||
// Debug-Ausgabe aller bekannten imageTagToDigest
|
||||
fmt.Println("DEBUG: Alle bekannten imageTagToDigest:")
|
||||
for k, v := range imageTagToDigest {
|
||||
fmt.Printf(" %s -> %s\n", k, v)
|
||||
}
|
||||
|
||||
results := make([]ImageStatus, 0)
|
||||
for _, ctr := range containers {
|
||||
rawTag := ctr.Image
|
||||
tag := normalizeImageTag(rawTag)
|
||||
|
||||
containerName := "unknown"
|
||||
if len(ctr.Names) > 0 {
|
||||
containerName = ctr.Names[0]
|
||||
containerName = strings.TrimPrefix(ctr.Names[0], "/")
|
||||
}
|
||||
|
||||
imageRef, err := toRegistryImage(tag)
|
||||
if err != nil {
|
||||
log.Printf("Fehler beim Parsen des Image-Namens (%s): %v", tag, err)
|
||||
// Überspringen, wenn im EXCLUDE_CONTAINERS enthalten
|
||||
if _, excluded := excludeContainers[containerName]; excluded {
|
||||
fmt.Printf("⏭️ Container '%s' ist ausgeschlossen.\n", containerName)
|
||||
continue
|
||||
}
|
||||
|
||||
localDigest := imageTagToDigest[tag]
|
||||
rawTag := normalizeImageTag(ctr.Image)
|
||||
localDigest := imageTagToDigest[rawTag]
|
||||
|
||||
imageRef, err := toRegistryImage(rawTag)
|
||||
if err != nil {
|
||||
log.Printf("Ungültige Image-Referenz (%s): %v", rawTag, err)
|
||||
continue
|
||||
}
|
||||
refObj, err := ref.New(imageRef)
|
||||
if err != nil {
|
||||
log.Printf("Ungültige Image-Referenz (%s): %v", tag, err)
|
||||
log.Printf("Fehler beim Erzeugen der Referenz (%s): %v", rawTag, err)
|
||||
continue
|
||||
}
|
||||
|
||||
desc, err := rc.ManifestHead(ctx, refObj)
|
||||
if err != nil {
|
||||
log.Printf("Fehler beim Abrufen des Remote-Manifests (%s): %v", tag, err)
|
||||
log.Printf("Remote-Manifest nicht gefunden (%s): %v", rawTag, err)
|
||||
continue
|
||||
}
|
||||
remoteDigest := desc.GetDigest().String()
|
||||
|
||||
remoteDigest := desc.GetDigest().String()
|
||||
update := 0.0
|
||||
|
||||
if localDigest != remoteDigest {
|
||||
update = 1.0
|
||||
fmt.Printf("Container: %s\n Image: %s\n Local Digest: %s\n Remote Digest: %s\n -> Update verfügbar!\n",
|
||||
containerName, tag, localDigest, remoteDigest)
|
||||
fmt.Printf("⚠️ Container: %s\n -> Image: %s\n -> Update verfügbar!\n", containerName, rawTag)
|
||||
} else {
|
||||
fmt.Printf("Container: %s\n Image: %s\n Local Digest: %s\n Remote Digest: %s\n -> Kein Update verfügbar.\n",
|
||||
containerName, tag, localDigest, remoteDigest)
|
||||
fmt.Printf("✅ Container: %s\n -> Image: %s\n -> Aktuell\n", containerName, rawTag)
|
||||
}
|
||||
|
||||
imageName, imageTag := tag, "latest"
|
||||
if cp := strings.Split(tag, ":"); len(cp) == 2 {
|
||||
imageName, imageTag := rawTag, "latest"
|
||||
if cp := strings.Split(rawTag, ":"); len(cp) == 2 {
|
||||
imageName, imageTag = cp[0], cp[1]
|
||||
}
|
||||
|
||||
|
@ -205,8 +204,40 @@ func (c *imageUpdateCollector) Collect(ch chan<- prometheus.Metric) {
|
|||
}
|
||||
}
|
||||
|
||||
func loadIntervalFromEnv() {
|
||||
if val := os.Getenv("CHECK_INTERVAL"); val != "" {
|
||||
dur, err := time.ParseDuration(val)
|
||||
if err != nil {
|
||||
log.Printf("❌ Ungültiger CHECK_INTERVAL: %s – verwende Default (%s)", val, interval)
|
||||
} else {
|
||||
interval = dur
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadExclusionsFromEnv() {
|
||||
raw := os.Getenv("EXCLUDE_CONTAINERS")
|
||||
if raw == "" {
|
||||
return
|
||||
}
|
||||
list := strings.Split(raw, ",")
|
||||
for _, name := range list {
|
||||
clean := strings.TrimSpace(name)
|
||||
if clean != "" {
|
||||
excludeContainers[clean] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.Printf("🚀 Docker Image Update Exporter gestartet – Intervall: %v", interval)
|
||||
log.Println("🚀 Docker Image Update Exporter startet...")
|
||||
|
||||
loadIntervalFromEnv()
|
||||
loadExclusionsFromEnv()
|
||||
log.Printf("🔁 Prüfintervall: %v", interval)
|
||||
if len(excludeContainers) > 0 {
|
||||
log.Printf("🙈 Ignorierte Container: %v", keys(excludeContainers))
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
|
@ -214,6 +245,7 @@ func main() {
|
|||
time.Sleep(interval)
|
||||
}
|
||||
}()
|
||||
|
||||
checkImageUpdates()
|
||||
|
||||
exporter := newImageUpdateCollector()
|
||||
|
@ -222,3 +254,11 @@ func main() {
|
|||
http.Handle("/metrics", promhttp.Handler())
|
||||
log.Fatal(http.ListenAndServe(":9788", nil))
|
||||
}
|
||||
|
||||
func keys(m map[string]struct{}) []string {
|
||||
var result []string
|
||||
for k := range m {
|
||||
result = append(result, k)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue