mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
distribution: match manifest list resolution with containerd
Make finding the correct runtime image from image index more compliant with OCI spec and match containerd implementation. Changes: - Manifest list is allowed to contain manifest lists - Unknown mediatype inside manifest list is skipped instead of causing an error - Platform in descriptor is optional Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
parent
4e09933aed
commit
9adad264d2
2 changed files with 83 additions and 58 deletions
|
@ -833,64 +833,65 @@ func (p *puller) pullManifestList(ctx context.Context, ref reference.Named, mfst
|
||||||
|
|
||||||
manifestMatches := filterManifests(mfstList.Manifests, platform)
|
manifestMatches := filterManifests(mfstList.Manifests, platform)
|
||||||
|
|
||||||
if len(manifestMatches) == 0 {
|
for _, match := range manifestMatches {
|
||||||
errMsg := fmt.Sprintf("no matching manifest for %s in the manifest list entries", formatPlatform(platform))
|
if err := checkImageCompatibility(match.Platform.OS, match.Platform.OSVersion); err != nil {
|
||||||
logrus.Debugf(errMsg)
|
return "", "", err
|
||||||
return "", "", errors.New(errMsg)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if len(manifestMatches) > 1 {
|
desc := specs.Descriptor{
|
||||||
logrus.Debugf("found multiple matches in manifest list, choosing best match %s", manifestMatches[0].Digest.String())
|
Digest: match.Digest,
|
||||||
}
|
Size: match.Size,
|
||||||
match := manifestMatches[0]
|
MediaType: match.MediaType,
|
||||||
|
}
|
||||||
if err := checkImageCompatibility(match.Platform.OS, match.Platform.OSVersion); err != nil {
|
manifest, err := p.manifestStore.Get(ctx, desc)
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
desc := specs.Descriptor{
|
|
||||||
Digest: match.Digest,
|
|
||||||
Size: match.Size,
|
|
||||||
MediaType: match.MediaType,
|
|
||||||
}
|
|
||||||
manifest, err := p.manifestStore.Get(ctx, desc)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
manifestRef, err := reference.WithDigest(reference.TrimNamed(ref), match.Digest)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v := manifest.(type) {
|
|
||||||
case *schema1.SignedManifest:
|
|
||||||
msg := fmt.Sprintf("[DEPRECATION NOTICE] v2 schema1 manifests in manifest lists are not supported and will break in a future release. Suggest author of %s to upgrade to v2 schema2. More information at https://docs.docker.com/registry/spec/deprecated-schema-v1/", ref)
|
|
||||||
logrus.Warn(msg)
|
|
||||||
progress.Message(p.config.ProgressOutput, "", msg)
|
|
||||||
|
|
||||||
platform := toOCIPlatform(manifestMatches[0].Platform)
|
|
||||||
id, _, err = p.pullSchema1(ctx, manifestRef, v, &platform)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
case *schema2.DeserializedManifest:
|
|
||||||
platform := toOCIPlatform(manifestMatches[0].Platform)
|
|
||||||
id, _, err = p.pullSchema2(ctx, manifestRef, v, &platform)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
case *ocischema.DeserializedManifest:
|
|
||||||
platform := toOCIPlatform(manifestMatches[0].Platform)
|
|
||||||
id, _, err = p.pullOCI(ctx, manifestRef, v, &platform)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return "", "", errors.New("unsupported manifest format")
|
|
||||||
}
|
|
||||||
|
|
||||||
return id, manifestListDigest, err
|
manifestRef, err := reference.WithDigest(reference.TrimNamed(ref), match.Digest)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := manifest.(type) {
|
||||||
|
case *schema1.SignedManifest:
|
||||||
|
msg := fmt.Sprintf("[DEPRECATION NOTICE] v2 schema1 manifests in manifest lists are not supported and will break in a future release. Suggest author of %s to upgrade to v2 schema2. More information at https://docs.docker.com/registry/spec/deprecated-schema-v1/", ref)
|
||||||
|
logrus.Warn(msg)
|
||||||
|
progress.Message(p.config.ProgressOutput, "", msg)
|
||||||
|
|
||||||
|
platform := toOCIPlatform(match.Platform)
|
||||||
|
id, _, err = p.pullSchema1(ctx, manifestRef, v, platform)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
case *schema2.DeserializedManifest:
|
||||||
|
platform := toOCIPlatform(match.Platform)
|
||||||
|
id, _, err = p.pullSchema2(ctx, manifestRef, v, platform)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
case *ocischema.DeserializedManifest:
|
||||||
|
platform := toOCIPlatform(match.Platform)
|
||||||
|
id, _, err = p.pullOCI(ctx, manifestRef, v, platform)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
case *manifestlist.DeserializedManifestList:
|
||||||
|
id, _, err = p.pullManifestList(ctx, manifestRef, v, pp)
|
||||||
|
if err != nil {
|
||||||
|
var noMatches noMatchesErr
|
||||||
|
if !errors.As(err, &noMatches) {
|
||||||
|
// test the next match
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// OCI spec requires to skip unknown manifest types
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return id, manifestListDigest, err
|
||||||
|
}
|
||||||
|
return "", "", noMatchesErr{platform: platform}
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -922,6 +923,14 @@ func (p *puller) pullSchema2Config(ctx context.Context, dgst digest.Digest) (con
|
||||||
return configJSON, nil
|
return configJSON, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type noMatchesErr struct {
|
||||||
|
platform specs.Platform
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e noMatchesErr) Error() string {
|
||||||
|
return fmt.Sprintf("no matching manifest for %s in the manifest list entries", formatPlatform(e.platform))
|
||||||
|
}
|
||||||
|
|
||||||
func retry(ctx context.Context, maxAttempts int, sleep time.Duration, f func(ctx context.Context) error) (err error) {
|
func retry(ctx context.Context, maxAttempts int, sleep time.Duration, f func(ctx context.Context) error) (err error) {
|
||||||
attempt := 0
|
attempt := 0
|
||||||
for ; attempt < maxAttempts; attempt++ {
|
for ; attempt < maxAttempts; attempt++ {
|
||||||
|
@ -1054,8 +1063,13 @@ func createDownloadFile() (*os.File, error) {
|
||||||
return os.CreateTemp("", "GetImageBlob")
|
return os.CreateTemp("", "GetImageBlob")
|
||||||
}
|
}
|
||||||
|
|
||||||
func toOCIPlatform(p manifestlist.PlatformSpec) specs.Platform {
|
func toOCIPlatform(p manifestlist.PlatformSpec) *specs.Platform {
|
||||||
return specs.Platform{
|
// distribution pkg does define platform as pointer so this hack for empty struct
|
||||||
|
// is necessary. This is temporary until correct OCI image-spec package is used.
|
||||||
|
if p.OS == "" && p.Architecture == "" && p.Variant == "" && p.OSVersion == "" && p.OSFeatures == nil && p.Features == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &specs.Platform{
|
||||||
OS: p.OS,
|
OS: p.OS,
|
||||||
Architecture: p.Architecture,
|
Architecture: p.Architecture,
|
||||||
Variant: p.Variant,
|
Variant: p.Variant,
|
||||||
|
|
|
@ -24,14 +24,25 @@ func filterManifests(manifests []manifestlist.ManifestDescriptor, p specs.Platfo
|
||||||
m := platforms.Only(p)
|
m := platforms.Only(p)
|
||||||
var matches []manifestlist.ManifestDescriptor
|
var matches []manifestlist.ManifestDescriptor
|
||||||
for _, desc := range manifests {
|
for _, desc := range manifests {
|
||||||
if m.Match(toOCIPlatform(desc.Platform)) {
|
descP := toOCIPlatform(desc.Platform)
|
||||||
|
if descP == nil || m.Match(*descP) {
|
||||||
matches = append(matches, desc)
|
matches = append(matches, desc)
|
||||||
logrus.Debugf("found match for %s with media type %s, digest %s", platforms.Format(p), desc.MediaType, desc.Digest.String())
|
if descP != nil {
|
||||||
|
logrus.Debugf("found match for %s with media type %s, digest %s", platforms.Format(p), desc.MediaType, desc.Digest.String())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.SliceStable(matches, func(i, j int) bool {
|
sort.SliceStable(matches, func(i, j int) bool {
|
||||||
return m.Less(toOCIPlatform(matches[i].Platform), toOCIPlatform(matches[j].Platform))
|
p1 := toOCIPlatform(matches[i].Platform)
|
||||||
|
if p1 == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
p2 := toOCIPlatform(matches[j].Platform)
|
||||||
|
if p2 == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return m.Less(*p1, *p2)
|
||||||
})
|
})
|
||||||
|
|
||||||
// deprecated: backwards compatibility with older versions that didn't compare variant
|
// deprecated: backwards compatibility with older versions that didn't compare variant
|
||||||
|
|
Loading…
Reference in a new issue