| package daemon |
| |
| import ( |
| "context" |
| "encoding/json" |
| |
| "github.com/containerd/containerd/v2/core/content" |
| "github.com/containerd/log" |
| "github.com/containerd/platforms" |
| "github.com/moby/moby/v2/daemon/container" |
| "github.com/moby/moby/v2/daemon/internal/image" |
| "github.com/moby/moby/v2/daemon/internal/multierror" |
| "github.com/moby/moby/v2/daemon/server/imagebackend" |
| 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(), imagebackend.GetImageOpts{}) |
| if err != nil { |
| return ocispec.Platform{}, err |
| } |
| |
| return img.Platform(), nil |
| } |