Repository: moby/moby (Go)

Issue:
c8d: review uses of platforms.Format vs platforms.FormatAll
It occurred to me that we're using `platforms.Format()` here, and not `platforms.FormatAll()`. The latter also includes "os-version" if it's set. While we don't use that / document that (yet) in the CLI documentation, it may become more relevant with Windows images.

I don't think it's problematic to have it here, but I'll open a tracking ticket to make a pass at looking for places where we could replace `Format` with `FormatAll`.

_Originally posted by @thaJeztah in https://github.com/moby/moby/pull/48631#discussion_r1799038581_
            


Discussion:
+1, `FormatAll` is the thing we should be doing, it just wasn't available at the time we wrote most of the code.
Fix PR: https://github.com/moby/moby/pull/49973
From that PR; 

---

We should probably not mark the ticket as fixed until we reviewed the remaining uses (I changed the "fixes" to "addresses"); we can do those separately (changes in this PR look good AFAICS), but make sure that we're not accidentally dropping os.version in places where it shouldn't be dropped.

I had a quick peek (if my IDE is not lying to me), and outside of vendor code, and the good news is that the list is not too long, so probably doable in a follow-up. I found the remaining uses here;

https://github.com/moby/moby/blob/294f0c36e468dc097d56dcf88d4418b7dc3d2856/builder/builder-next/adapters/containerimage/pull.go#L184-L185

https://github.com/moby/moby/blob/294f0c36e468dc097d56dcf88d4418b7dc3d2856/builder/builder-next/worker/worker.go#L163-L166

https://github.com/moby/moby/blob/294f0c36e468dc097d56dcf88d4418b7dc3d2856/daemon/containerd/image_pull.go#L79-L82

https://github.com/moby/moby/blob/294f0c36e468dc097d56dcf88d4418b7dc3d2856/internal/testutils/specialimage/multiplatform.go#L18-L20

https://github.com/moby/moby/blob/294f0c36e468dc097d56dcf88d4418b7dc3d2856/internal/testutils/specialimage/partial.go#L28-L30
PR https://github.com/moby/moby/pull/49973 has been updated to address the remaining use cases.



Patch:
diff --git a/builder/builder-next/adapters/containerimage/pull.go b/builder/builder-next/adapters/containerimage/pull.go
index 9a753fe64cfe8..82a9d78141bd0 100644
--- a/builder/builder-next/adapters/containerimage/pull.go
+++ b/builder/builder-next/adapters/containerimage/pull.go
@@ -182,7 +182,7 @@ func (is *Source) resolveRemote(ctx context.Context, ref string, platform *ocisp
 		p = *platform
 	}
 	// key is used to synchronize resolutions that can happen in parallel when doing multi-stage.
-	key := "getconfig::" + ref + "::" + platforms.Format(p)
+	key := "getconfig::" + ref + "::" + platforms.FormatAll(p)
 	res, err := is.g.Do(ctx, key, func(ctx context.Context) (*resolveRemoteResult, error) {
 		res := resolver.DefaultPool.GetResolver(is.RegistryHosts, ref, "pull", sm, g)
 		dgst, dt, err := imageutil.Config(ctx, ref, res, is.ContentStore, is.LeaseManager, platform)
diff --git a/daemon/containerd/image_builder.go b/daemon/containerd/image_builder.go
index 513ac91e85514..87b9b86b6e063 100644
--- a/daemon/containerd/image_builder.go
+++ b/daemon/containerd/image_builder.go
@@ -166,7 +166,7 @@ func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConf
 WARNING: Pulled image with specified platform (%s), but the resulting image's configured platform (%s) does not match.
 This is most likely caused by a bug in the build system that created the fetched image (%s).
 Please notify the image author to correct the configuration.`,
-					platforms.Format(p), platforms.Format(imgPlat), name,
+					platforms.FormatAll(p), platforms.FormatAll(imgPlat), name,
 				)
 				log.G(ctx).WithError(err).WithField("image", name).Warn("Ignoring error about platform mismatch where the manifest list points to an image whose configuration does not match the platform in the manifest.")
 			}
diff --git a/daemon/containerd/image_pull.go b/daemon/containerd/image_pull.go
index 6f4113d1caa08..5a60d13dd9e2c 100644
--- a/daemon/containerd/image_pull.go
+++ b/daemon/containerd/image_pull.go
@@ -79,7 +79,7 @@ func (i *ImageService) PullImage(ctx context.Context, baseRef reference.Named, p
 func (i *ImageService) pullTag(ctx context.Context, ref reference.Named, platform *ocispec.Platform, metaHeaders map[string][]string, authConfig *registrytypes.AuthConfig, out progress.Output) error {
 	var opts []containerd.RemoteOpt
 	if platform != nil {
-		opts = append(opts, containerd.WithPlatform(platforms.Format(*platform)))
+		opts = append(opts, containerd.WithPlatform(platforms.FormatAll(*platform)))
 	}
 
 	resolver, _ := i.newResolverFromAuthConfig(ctx, authConfig, ref)
@@ -223,7 +223,7 @@ func (i *ImageService) pullTag(ctx context.Context, ref reference.Named, platfor
 			if strings.Contains(err.Error(), "platform") {
 				platformStr := platforms.DefaultString()
 				if platform != nil {
-					platformStr = platforms.Format(*platform)
+					platformStr = platforms.FormatAll(*platform)
 				}
 				return errdefs.NotFound(fmt.Errorf("no matching manifest for %s in the manifest list entries: %w", platformStr, err))
 			}
diff --git a/daemon/create.go b/daemon/create.go
index 1e0c3ed3ccfa6..624382f214fd0 100644
--- a/daemon/create.go
+++ b/daemon/create.go
@@ -108,7 +108,7 @@ func (daemon *Daemon) containerCreate(ctx context.Context, daemonCfg *configStor
 			}
 
 			if !images.OnlyPlatformWithFallback(p).Match(imgPlat) {
-				warnings = append(warnings, fmt.Sprintf("The requested image's platform (%s) does not match the detected host platform (%s) and no specific platform was requested", platforms.Format(imgPlat), platforms.Format(p)))
+				warnings = append(warnings, fmt.Sprintf("The requested image's platform (%s) does not match the detected host platform (%s) and no specific platform was requested", platforms.FormatAll(imgPlat), platforms.FormatAll(p)))
 			}
 		}
 	}
diff --git a/daemon/images/image.go b/daemon/images/image.go
index 8faa161a2b67f..739935a28e46c 100644
--- a/daemon/images/image.go
+++ b/daemon/images/image.go
@@ -54,7 +54,7 @@ func (i *ImageService) manifestMatchesPlatform(ctx context.Context, img *image.I
 		log.G(ctx).WithFields(log.Fields{
 			"error":           err,
 			"image":           img.ID,
-			"desiredPlatform": platforms.Format(platform),
+			"desiredPlatform": platforms.FormatAll(platform),
 		}).Error("Error looking up image leases")
 		return false, err
 	}
@@ -75,7 +75,7 @@ func (i *ImageService) manifestMatchesPlatform(ctx context.Context, img *image.I
 	for _, r := range ls {
 		logger := log.G(ctx).WithFields(log.Fields{
 			"image":           img.ID,
-			"desiredPlatform": platforms.Format(platform),
+			"desiredPlatform": platforms.FormatAll(platform),
 			"resourceID":      r.ID,
 			"resourceType":    r.Type,
 		})
@@ -121,7 +121,7 @@ func (i *ImageService) manifestMatchesPlatform(ctx context.Context, img *image.I
 				Variant:      md.Platform.Variant,
 			}
 			if !comparer.Match(p) {
-				logger.WithField("otherPlatform", platforms.Format(p)).Debug("Manifest is not a match")
+				logger.WithField("otherPlatform", platforms.FormatAll(p)).Debug("Manifest is not a match")
 				continue
 			}
 
@@ -195,7 +195,7 @@ func (i *ImageService) GetImage(ctx context.Context, refOrID string, options bac
 		if ref, err := reference.ParseNamed(refOrID); err == nil {
 			imgName = reference.FamiliarString(ref)
 		}
-		retErr = errdefs.NotFound(errors.Errorf("image with reference %s was found but its platform (%s) does not match the specified platform (%s)", imgName, platforms.Format(imgPlat), platforms.Format(p)))
+		retErr = errdefs.NotFound(errors.Errorf("image with reference %s was found but its platform (%s) does not match the specified platform (%s)", imgName, platforms.FormatAll(imgPlat), platforms.FormatAll(p)))
 	}()
 	ref, err := reference.ParseAnyReference(refOrID)
 	if err != nil {
diff --git a/daemon/images/image_builder.go b/daemon/images/image_builder.go
index 3e2302952a951..f6f6f8caad229 100644
--- a/daemon/images/image_builder.go
+++ b/daemon/images/image_builder.go
@@ -180,7 +180,7 @@ func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConf
 WARNING: Pulled image with specified platform (%s), but the resulting image's configured platform (%s) does not match.
 This is most likely caused by a bug in the build system that created the fetched image (%s).
 Please notify the image author to correct the configuration.`,
-				platforms.Format(p), platforms.Format(imgPlat), name,
+				platforms.FormatAll(p), platforms.FormatAll(imgPlat), name,
 			)
 			log.G(ctx).WithError(err).WithField("image", name).Warn("Ignoring error about platform mismatch where the manifest list points to an image whose configuration does not match the platform in the manifest.")
 			err = nil
diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go
index 49d35c587f2b8..3cb22b5779194 100644
--- a/distribution/pull_v2.go
+++ b/distribution/pull_v2.go
@@ -836,7 +836,7 @@ func (p *puller) pullManifestList(ctx context.Context, ref reference.Named, mfst
 	if pp != nil {
 		platform = *pp
 	}
-	log.G(ctx).Debugf("%s resolved to a manifestList object with %d entries; looking for a %s match", ref, len(mfstList.Manifests), platforms.Format(platform))
+	log.G(ctx).Debugf("%s resolved to a manifestList object with %d entries; looking for a %s match", ref, len(mfstList.Manifests), platforms.FormatAll(platform))
 
 	manifestMatches := filterManifests(mfstList.Manifests, platform)
 
diff --git a/distribution/pull_v2_unix.go b/distribution/pull_v2_unix.go
index 1bb05b79875db..7984f72546c85 100644
--- a/distribution/pull_v2_unix.go
+++ b/distribution/pull_v2_unix.go
@@ -27,7 +27,7 @@ func filterManifests(manifests []manifestlist.ManifestDescriptor, p ocispec.Plat
 		if descP == nil || m.Match(*descP) {
 			matches = append(matches, desc)
 			if descP != nil {
-				log.G(context.TODO()).Debugf("found match for %s with media type %s, digest %s", platforms.Format(p), desc.MediaType, desc.Digest.String())
+				log.G(context.TODO()).Debugf("found match for %s with media type %s, digest %s", platforms.FormatAll(p), desc.MediaType, desc.Digest.String())
 			}
 		}
 	}


Test patch:
diff --git a/internal/testutils/specialimage/multiplatform.go b/internal/testutils/specialimage/multiplatform.go
index 00d56ed3f46a1..70dfc8cdc9afb 100644
--- a/internal/testutils/specialimage/multiplatform.go
+++ b/internal/testutils/specialimage/multiplatform.go
@@ -16,7 +16,7 @@ func MultiPlatform(dir string, imageRef string, imagePlatforms []ocispec.Platfor
 	var descs []ocispec.Descriptor
 
 	for _, platform := range imagePlatforms {
-		ps := platforms.Format(platform)
+		ps := platforms.FormatAll(platform)
 		manifestDesc, _, err := oneLayerPlatformManifest(dir, platform, FileInLayer{Path: "bash", Content: []byte("layer-" + ps)})
 		if err != nil {
 			return nil, nil, err
diff --git a/internal/testutils/specialimage/partial.go b/internal/testutils/specialimage/partial.go
index 13a873d232c82..6fe941d757388 100644
--- a/internal/testutils/specialimage/partial.go
+++ b/internal/testutils/specialimage/partial.go
@@ -26,7 +26,7 @@ func PartialMultiPlatform(dir string, imageRef string, opts PartialOpts) (*ocisp
 	var descs []ocispec.Descriptor
 
 	for _, platform := range opts.Stored {
-		ps := platforms.Format(platform)
+		ps := platforms.FormatAll(platform)
 		manifestDesc, _, err := oneLayerPlatformManifest(dir, platform, FileInLayer{Path: "bash", Content: []byte("layer-" + ps)})
 		if err != nil {
 			return nil, nil, err