Skip to content

Commit 3e1e7b5

Browse files
authored
fix docker update pathological loop (#757)
1 parent a68815c commit 3e1e7b5

2 files changed

Lines changed: 94 additions & 22 deletions

File tree

goseg/docker/docker.go

Lines changed: 92 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -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
653725
func FindContainer(containerName string) (*container.Summary, error) {
654726
cli, err := dockerclient.New()

goseg/routines/version.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ func CheckVersionLoop() {
4343
}
4444

4545
func callUpdater(releaseChannel string) {
46+
currentChannelVersion := config.LocalVersion().Groundseg[releaseChannel]
4647
// Get latest information
4748
latestVersion, _ := config.CheckVersion()
48-
currentChannelVersion := config.VersionInfo
4949
latestChannelVersion := latestVersion
5050
// check docker updates
5151
if latestChannelVersion != currentChannelVersion {
@@ -277,7 +277,7 @@ func updateDocker(release string, currentVersion structs.Channel, latestVersion
277277
typeOfVersion := valCurrent.Type()
278278

279279
for i := 0; i < valCurrent.NumField(); i++ {
280-
sw := typeOfVersion.Field(i).Name
280+
sw := strings.ToLower(typeOfVersion.Field(i).Name)
281281
if sw != "groundseg" {
282282
currentDetail := valCurrent.Field(i).Interface().(structs.VersionDetails)
283283
latestDetail := valLatest.Field(i).Interface().(structs.VersionDetails)

0 commit comments

Comments
 (0)