From 8a9d19498477e67bd0a52c9542e02c31a9b42bfe Mon Sep 17 00:00:00 2001 From: Simon Rieger Date: Thu, 17 Jul 2025 03:15:14 +0200 Subject: [PATCH] add logging --- main.go | 117 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 70 insertions(+), 47 deletions(-) diff --git a/main.go b/main.go index c7b3d4d..5359e0c 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( "net/http" "regexp" "strings" + "sync" "github.com/docker/docker/api/types/image" "github.com/docker/docker/client" @@ -16,7 +17,7 @@ import ( "github.com/regclient/regclient/types/ref" ) -// Hilfsfunktion: registrykompatibles Format aus repo:tag erzeugen +// Hilfsfunktion: registry/repo:tag für regclient func toRegistryImage(imageTag string) (string, error) { r := regexp.MustCompile(`^(?:(?P[^/]+)/)?(?P[^:]+)(?::(?P.+))?$`) match := r.FindStringSubmatch(imageTag) @@ -35,7 +36,7 @@ func toRegistryImage(imageTag string) (string, error) { return fmt.Sprintf("%s/%s:%s", registry, repo, tag), nil } -// Extrahiert nur den sha256:... Digest +// Extrahiere nur den sha256:digest func extractDigest(s string) string { for _, part := range strings.Split(s, "@") { if strings.HasPrefix(part, "sha256:") { @@ -45,31 +46,30 @@ func extractDigest(s string) string { return s } -// Prometheus Collector-Struktur -type dockerImageUpdateCollector struct { - desc *prometheus.Desc +type imageUpdateCollector struct { + metric *prometheus.Desc } -func newDockerImageUpdateCollector() prometheus.Collector { - return &dockerImageUpdateCollector{ - desc: prometheus.NewDesc( +func newImageUpdateCollector() *imageUpdateCollector { + return &imageUpdateCollector{ + metric: prometheus.NewDesc( "docker_image_update_available", - "Whether a new image digest is available for this local image tag (1=update, 0=current)", - []string{"image"}, + "Ob Update für das lokale Docker-Image für das Tag im Registry verfügbar ist (1=Update, 0=aktuell)", + []string{"image", "tag"}, nil, ), } } -func (c *dockerImageUpdateCollector) Describe(ch chan<- *prometheus.Desc) { - ch <- c.desc +func (c *imageUpdateCollector) Describe(ch chan<- *prometheus.Desc) { + ch <- c.metric } -func (c *dockerImageUpdateCollector) Collect(ch chan<- prometheus.Metric) { +func (c *imageUpdateCollector) Collect(ch chan<- prometheus.Metric) { ctx := context.Background() cli, err := client.NewClientWithOpts(client.FromEnv) if err != nil { - log.Println("Docker-Client-Fehler:", err) + log.Printf("Fehler bei Docker-Client: %v", err) return } defer cli.Close() @@ -77,48 +77,71 @@ func (c *dockerImageUpdateCollector) Collect(ch chan<- prometheus.Metric) { images, err := cli.ImageList(ctx, image.ListOptions{All: true}) if err != nil { - log.Println("Docker-ImageList-Fehler:", err) + log.Printf("Fehler bei ImageList: %v", err) return } + var wg sync.WaitGroup for _, img := range images { for _, tag := range img.RepoTags { - imageRef, err := toRegistryImage(tag) - if err != nil { - continue - } - var localDigest string - if len(img.RepoDigests) > 0 { - localDigest = extractDigest(img.RepoDigests[0]) - } else { - localDigest = img.ID - } - refObj, err := ref.New(imageRef) - if err != nil { - continue - } - desc, err := rc.ManifestHead(ctx, refObj) - if err != nil { - continue - } - remoteDigest := desc.GetDigest().String() - var value float64 = 0 - if localDigest != remoteDigest { - value = 1 - } - ch <- prometheus.MustNewConstMetric( - c.desc, - prometheus.GaugeValue, - value, - tag, - ) + wg.Add(1) + go func(tag string, img image.Summary) { + defer wg.Done() + imageRef, err := toRegistryImage(tag) + if err != nil { + log.Printf("ImageRef-Fehler bei %s: %v", tag, err) + return + } + + // Lokalen Digest holen + var localDigest string + if len(img.RepoDigests) > 0 { + localDigest = extractDigest(img.RepoDigests[0]) + } else { + localDigest = img.ID + } + + // Remote Digest holen + refObj, err := ref.New(imageRef) + if err != nil { + log.Printf("ImageRef (regclient) ungültig (%s): %v", tag, err) + return + } + desc, err := rc.ManifestHead(ctx, refObj) + if err != nil { + log.Printf("Remote-Manifest nicht gefunden (%s): %v", tag, err) + return + } + remoteDigest := desc.GetDigest().String() + + // Logging + fmt.Printf("Image: %s\n Local Digest: %s\n Remote Digest: %s\n", tag, localDigest, remoteDigest) + var update float64 = 0 + if localDigest != remoteDigest { + fmt.Println(" -> Update verfügbar!") + update = 1 + } else { + fmt.Println(" -> Kein Update verfügbar.") + } + + labelImg, labelTag := tag, "latest" + if cp := strings.Split(tag, ":"); len(cp) == 2 { + labelImg, labelTag = cp[0], cp[1] + } + ch <- prometheus.MustNewConstMetric( + c.metric, prometheus.GaugeValue, + update, labelImg, labelTag, + ) + }(tag, img) } } + wg.Wait() } func main() { - prometheus.MustRegister(newDockerImageUpdateCollector()) + log.Println("Starte Docker-Image-Update Prometheus Exporter (Port 9788)...") + exporter := newImageUpdateCollector() + prometheus.MustRegister(exporter) http.Handle("/metrics", promhttp.Handler()) - log.Println("Prometheus Exporter läuft auf :9888/metrics") - log.Fatal(http.ListenAndServe(":9888", nil)) + log.Fatal(http.ListenAndServe(":9788", nil)) }