package main import ( "context" "fmt" "log" "regexp" "strings" "github.com/docker/docker/api/types/image" "github.com/docker/docker/client" "github.com/regclient/regclient" "github.com/regclient/regclient/types/ref" ) // Wandelt ein ImageTag in registrykompatibles Format (ggf. registry hinzufügen) func toRegistryImage(imageTag string) (string, error) { r := regexp.MustCompile(`^(?:(?P[^/]+)/)?(?P[^:]+)(?::(?P.+))?$`) match := r.FindStringSubmatch(imageTag) if len(match) == 0 { return "", fmt.Errorf("Image-Tag nicht erkannt: %s", imageTag) } registry := match[r.SubexpIndex("registry")] repo := match[r.SubexpIndex("repo")] tag := match[r.SubexpIndex("tag")] if registry == "" { registry = "registry-1.docker.io" } if tag == "" { tag = "latest" } return fmt.Sprintf("%s/%s:%s", registry, repo, tag), nil } // Extrahiert nur den reinen sha256:<...>-Digest func extractDigest(s string) string { for _, part := range strings.Split(s, "@") { if strings.HasPrefix(part, "sha256:") { return part } } return s // ggf. nur die ID, wenn kein Digest } func main() { ctx := context.Background() cli, err := client.NewClientWithOpts(client.FromEnv) if err != nil { log.Fatal(err) } defer cli.Close() rc := regclient.New() images, err := cli.ImageList(ctx, image.ListOptions{All: true}) if err != nil { log.Fatal(err) } for _, img := range images { for _, tag := range img.RepoTags { imageRef, err := toRegistryImage(tag) if err != nil { continue } // Lokalen Digest extrahieren var localDigest string if len(img.RepoDigests) > 0 { localDigest = extractDigest(img.RepoDigests[0]) } else { localDigest = img.ID } // Remote-Digest bestimmen refObj, err := ref.New(imageRef) if err != nil { fmt.Printf("ImageRef-Fehler bei %s: %v\n", tag, err) continue } desc, err := rc.ManifestHead(ctx, refObj) if err != nil { fmt.Printf("Manifest nicht gefunden (%s): %v\n", tag, err) continue } remoteDigest := desc.GetDigest().String() fmt.Printf("Image: %s\n Local Digest: %s\n Remote Digest: %s\n", tag, localDigest, remoteDigest) if localDigest == remoteDigest { fmt.Println(" -> Kein Update verfügbar.") } else { fmt.Println(" -> Update verfügbar!") } } } }