@@ -303,6 +303,7 @@ func StartContainer(containerName string, containerType string) (structs.Contain
303303 }
304304 var imageInfo map [string ]string
305305 desiredImage := containerConfig .Image
306+ desiredImageID := ""
306307 if containerType == "minio" {
307308 if desiredImage == "" {
308309 return containerState , fmt .Errorf ("empty image ref for %s" , containerName )
@@ -323,6 +324,9 @@ func StartContainer(containerName string, containerType string) (structs.Contain
323324 if err != nil {
324325 return containerState , err
325326 }
327+ if desiredImageID , err = getLocalImageID (desiredImage , imageInfo ); err != nil {
328+ zap .L ().Warn (fmt .Sprintf ("Unable to inspect desired image %s: %v" , desiredImage , err ))
329+ }
326330 }
327331 // check if container exists
328332 existingContainer , _ := FindContainer (containerName )
@@ -376,7 +380,8 @@ func StartContainer(containerName string, containerType string) (structs.Contain
376380 if len (digestParts ) > 1 {
377381 currentDigest = digestParts [1 ]
378382 }
379- if currentDigest != imageInfo ["hash" ] {
383+ imageMatches := desiredImageID != "" && imageIDsEqual (existingContainer .ImageID , desiredImageID )
384+ if ! imageMatches && currentDigest != imageInfo ["hash" ] {
380385 // if the hashes don't match, recreate the container with the new one
381386 // for vere containers, gracefully stop with a 60s timeout before removing
382387 if containerType == "vere" {
@@ -600,15 +605,12 @@ func PullImageIfNotExist(desiredImage string, imageInfo map[string]string) (bool
600605 return false , err
601606 }
602607 defer cli .Close ()
603- images , err := cli .ImageList (ctx , imagetypes.ListOptions {})
604- if err != nil {
605- return false , err
606- }
607- for _ , img := range images {
608- if slices .Contains (img .RepoDigests , fmt .Sprintf ("%s@sha256:%s" , imageInfo ["repo" ], imageInfo ["hash" ])) {
609- return true , nil
610- }
608+
609+ refs := imageRefCandidates (desiredImage , imageInfo )
610+ if _ , ok := inspectImageRefs (ctx , cli , refs ); ok {
611+ return true , nil
611612 }
613+
612614 resp , err := cli .ImagePull (ctx , fmt .Sprintf ("%s@sha256:%s" , imageInfo ["repo" ], imageInfo ["hash" ]), imagetypes.PullOptions {})
613615 if err != nil {
614616 return false , err
@@ -627,17 +629,8 @@ func PullImageByRef(imageRef string) error {
627629 }
628630 defer cli .Close ()
629631
630- images , err := cli .ImageList (ctx , imagetypes.ListOptions {})
631- if err != nil {
632- return err
633- }
634- for _ , img := range images {
635- if slices .Contains (img .RepoTags , imageRef ) {
636- return nil
637- }
638- if slices .Contains (img .RepoDigests , imageRef ) {
639- return nil
640- }
632+ if _ , ok := inspectImageRefs (ctx , cli , dockerHubRefAliases (imageRef )); ok {
633+ return nil
641634 }
642635
643636 resp , err := cli .ImagePull (ctx , imageRef , imagetypes.PullOptions {})
@@ -649,6 +642,85 @@ func PullImageByRef(imageRef string) error {
649642 return nil
650643}
651644
645+ func getLocalImageID (desiredImage string , imageInfo map [string ]string ) (string , error ) {
646+ ctx := context .Background ()
647+ cli , err := dockerclient .New ()
648+ if err != nil {
649+ return "" , err
650+ }
651+ defer cli .Close ()
652+ id , ok := inspectImageRefs (ctx , cli , imageRefCandidates (desiredImage , imageInfo ))
653+ if ! ok {
654+ return "" , fmt .Errorf ("image not found locally" )
655+ }
656+ return id , nil
657+ }
658+
659+ func inspectImageRefs (ctx context.Context , cli * client.Client , refs []string ) (string , bool ) {
660+ for _ , ref := range refs {
661+ if ref == "" {
662+ continue
663+ }
664+ img , _ , err := cli .ImageInspectWithRaw (ctx , ref )
665+ if err == nil {
666+ return img .ID , true
667+ }
668+ }
669+ return "" , false
670+ }
671+
672+ func imageRefCandidates (desiredImage string , imageInfo map [string ]string ) []string {
673+ refs := dockerHubRefAliases (desiredImage )
674+ repo := imageInfo ["repo" ]
675+ tag := imageInfo ["tag" ]
676+ hash := imageInfo ["hash" ]
677+ for _ , repoAlias := range dockerHubRepoAliases (repo ) {
678+ if hash != "" {
679+ refs = appendUnique (refs , fmt .Sprintf ("%s@sha256:%s" , repoAlias , hash ))
680+ if tag != "" {
681+ refs = appendUnique (refs , fmt .Sprintf ("%s:%s@sha256:%s" , repoAlias , tag , hash ))
682+ }
683+ }
684+ if tag != "" && hash == "" {
685+ refs = appendUnique (refs , fmt .Sprintf ("%s:%s" , repoAlias , tag ))
686+ }
687+ }
688+ return refs
689+ }
690+
691+ func dockerHubRefAliases (ref string ) []string {
692+ if ref == "" {
693+ return nil
694+ }
695+ refs := []string {ref }
696+ if strings .HasPrefix (ref , "registry.hub.docker.com/" ) {
697+ trimmed := strings .TrimPrefix (ref , "registry.hub.docker.com/" )
698+ refs = appendUnique (refs , trimmed )
699+ refs = appendUnique (refs , "docker.io/" + trimmed )
700+ }
701+ if strings .HasPrefix (ref , "docker.io/" ) {
702+ trimmed := strings .TrimPrefix (ref , "docker.io/" )
703+ refs = appendUnique (refs , trimmed )
704+ refs = appendUnique (refs , "registry.hub.docker.com/" + trimmed )
705+ }
706+ return refs
707+ }
708+
709+ func dockerHubRepoAliases (repo string ) []string {
710+ return dockerHubRefAliases (repo )
711+ }
712+
713+ func appendUnique (items []string , item string ) []string {
714+ if item == "" || slices .Contains (items , item ) {
715+ return items
716+ }
717+ return append (items , item )
718+ }
719+
720+ func imageIDsEqual (a string , b string ) bool {
721+ return strings .TrimPrefix (a , "sha256:" ) == strings .TrimPrefix (b , "sha256:" )
722+ }
723+
652724// looks for a container with the given name and returns it, or nil if not found
653725func FindContainer (containerName string ) (* container.Summary , error ) {
654726 cli , err := dockerclient .New ()
0 commit comments