1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
package daemon
import (
"context"
"encoding/json"
"github.com/containerd/containerd/v2/core/content"
"github.com/containerd/log"
"github.com/containerd/platforms"
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/container"
"github.com/docker/docker/image"
"github.com/docker/docker/internal/multierror"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
func migrateContainerOS(ctx context.Context,
migration platformReader,
ctr *container.Container,
) {
deduced, err := deduceContainerPlatform(ctx, migration, ctr)
if err != nil {
log.G(ctx).WithFields(log.Fields{
"container": ctr.ID,
"error": err,
}).Warn("failed to deduce the container architecture")
ctr.ImagePlatform.OS = ctr.OS //nolint:staticcheck // ignore SA1019
return
}
ctr.ImagePlatform = deduced
}
type platformReader interface {
ReadPlatformFromConfigByImageManifest(ctx context.Context, desc ocispec.Descriptor) (ocispec.Platform, error)
ReadPlatformFromImage(ctx context.Context, id image.ID) (ocispec.Platform, error)
}
// deduceContainerPlatform tries to deduce `ctr`'s platform.
// If both `ctr.OS` and `ctr.ImageManifest` are empty, assume the image comes
// from a pre-OS times and use the host platform to match the behavior of
// [container.FromDisk].
// Otherwise:
// - `ctr.ImageManifest.Platform` is used, if it exists and is not empty.
// - The platform from the manifest's config is used, if `ctr.ImageManifest` exists
// and we're able to load its config from the content store.
// - The platform found by loading the image from the image service by ID (using
// `ctr.ImageID`) is used – this looks for the best *present* matching manifest in
// the store.
func deduceContainerPlatform(
ctx context.Context,
migration platformReader,
ctr *container.Container,
) (ocispec.Platform, error) {
if ctr.OS == "" && ctr.ImageManifest == nil { //nolint:staticcheck // ignore SA1019 because we are testing deprecated field migration
return platforms.DefaultSpec(), nil
}
var errs []error
isValidPlatform := func(p ocispec.Platform) bool {
return p.OS != "" && p.Architecture != ""
}
if ctr.ImageManifest != nil {
if ctr.ImageManifest.Platform != nil {
return *ctr.ImageManifest.Platform, nil
}
if ctr.ImageManifest != nil {
p, err := migration.ReadPlatformFromConfigByImageManifest(ctx, *ctr.ImageManifest)
if err != nil {
errs = append(errs, err)
} else {
if isValidPlatform(p) {
return p, nil
}
errs = append(errs, errors.New("malformed image config obtained by ImageManifestDescriptor"))
}
}
}
if ctr.ImageID != "" {
p, err := migration.ReadPlatformFromImage(ctx, ctr.ImageID)
if err != nil {
errs = append(errs, err)
} else {
if isValidPlatform(p) {
return p, nil
}
errs = append(errs, errors.New("malformed image config obtained by image id"))
}
}
return ocispec.Platform{}, errors.Wrap(multierror.Join(errs...), "cannot deduce the container platform")
}
type daemonPlatformReader struct {
imageService ImageService
content content.Provider
}
func (r daemonPlatformReader) ReadPlatformFromConfigByImageManifest(
ctx context.Context,
desc ocispec.Descriptor,
) (ocispec.Platform, error) {
if r.content == nil {
return ocispec.Platform{}, errors.New("not an containerd image store")
}
b, err := content.ReadBlob(ctx, r.content, desc)
if err != nil {
return ocispec.Platform{}, err
}
var mfst ocispec.Manifest
if err := json.Unmarshal(b, &mfst); err != nil {
return ocispec.Platform{}, err
}
b, err = content.ReadBlob(ctx, r.content, mfst.Config)
if err != nil {
return ocispec.Platform{}, err
}
var plat ocispec.Platform
if err := json.Unmarshal(b, &plat); err != nil {
return ocispec.Platform{}, err
}
return plat, nil
}
func (r daemonPlatformReader) ReadPlatformFromImage(ctx context.Context, id image.ID) (ocispec.Platform, error) {
img, err := r.imageService.GetImage(ctx, id.String(), backend.GetImageOpts{})
if err != nil {
return ocispec.Platform{}, err
}
return img.Platform(), nil
}
|