| 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 | |
| } |