| package daemon | |
| import ( | |
| "context" | |
| "errors" | |
| "testing" | |
| "github.com/containerd/platforms" | |
| "github.com/moby/moby/v2/daemon/container" | |
| "github.com/moby/moby/v2/daemon/internal/image" | |
| ocispec "github.com/opencontainers/image-spec/specs-go/v1" | |
| "gotest.tools/v3/assert" | |
| ) | |
| type mockPlatformReader struct{} | |
| func (m mockPlatformReader) ReadPlatformFromImage(ctx context.Context, id image.ID) (ocispec.Platform, error) { | |
| switch id { | |
| case "multiplatform": | |
| // This image has multiple platforms, but GetImage will prefer the first one | |
| // because the ID points to the full image index, not a specific platform. | |
| return platforms.DefaultSpec(), nil | |
| case "linux/arm64/v8": | |
| return ocispec.Platform{ | |
| OS: "linux", | |
| Architecture: "arm64", | |
| Variant: "v8", | |
| }, nil | |
| case "linux/amd64": | |
| return ocispec.Platform{ | |
| OS: "linux", | |
| Architecture: "amd64", | |
| }, nil | |
| case "windows/amd64": | |
| return ocispec.Platform{ | |
| OS: "windows", | |
| Architecture: "amd64", | |
| }, nil | |
| default: | |
| return ocispec.Platform{}, errors.New("image not found") | |
| } | |
| } | |
| func (m mockPlatformReader) ReadPlatformFromConfigByImageManifest(ctx context.Context, desc ocispec.Descriptor) (ocispec.Platform, error) { | |
| return m.ReadPlatformFromImage(ctx, image.ID(desc.Digest)) | |
| } | |
| //nolint:staticcheck // ignore SA1019 because we are testing deprecated field migration | |
| func TestContainerMigrateOS(t *testing.T) { | |
| type Container = container.Container | |
| var mock mockPlatformReader | |
| // ImageManifest is nil for containers created with graphdrivers image store | |
| var graphdrivers *ocispec.Descriptor | |
| for _, tc := range []struct { | |
| name string | |
| ctr Container | |
| expected ocispec.Platform | |
| }{ | |
| { | |
| name: "gd pre-OS container", | |
| ctr: Container{ | |
| ImageManifest: graphdrivers, | |
| OS: "", | |
| }, | |
| expected: platforms.DefaultSpec(), | |
| }, | |
| { | |
| name: "gd with linux arm64 image", | |
| ctr: Container{ | |
| ImageManifest: graphdrivers, | |
| ImageID: "linux/arm64/v8", | |
| OS: "linux", | |
| }, | |
| expected: ocispec.Platform{ | |
| OS: "linux", | |
| Architecture: "arm64", | |
| Variant: "v8", | |
| }, | |
| }, | |
| { | |
| name: "gd with windows image", | |
| ctr: Container{ | |
| ImageManifest: graphdrivers, | |
| ImageID: "windows/amd64", | |
| OS: "windows", | |
| }, | |
| expected: ocispec.Platform{ | |
| OS: "windows", | |
| Architecture: "amd64", | |
| }, | |
| }, | |
| { | |
| name: "gd with missing image", | |
| ctr: Container{ | |
| ImageManifest: graphdrivers, | |
| ImageID: "notfound", | |
| OS: "linux", | |
| }, | |
| expected: platforms.Platform{ | |
| OS: "linux", | |
| }, | |
| }, | |
| { | |
| name: "c8d with linux arm64 image", | |
| ctr: Container{ | |
| ImageManifest: &ocispec.Descriptor{ | |
| Digest: "linux/arm64/v8", | |
| }, | |
| OS: "linux", | |
| ImageID: "linux/arm64/v8", | |
| }, | |
| expected: ocispec.Platform{ | |
| OS: "linux", | |
| Architecture: "arm64", | |
| Variant: "v8", | |
| }, | |
| }, | |
| { | |
| name: "c8d with missing image", | |
| ctr: Container{ | |
| ImageManifest: &ocispec.Descriptor{ | |
| Digest: "notfound", | |
| }, | |
| OS: "linux", | |
| ImageID: "notfound", | |
| }, | |
| expected: platforms.Platform{ | |
| OS: "linux", | |
| }, | |
| }, | |
| { | |
| name: "c8d with ImageManifest that is no longer available", | |
| ctr: Container{ | |
| ImageManifest: &ocispec.Descriptor{ | |
| Digest: "notfound", | |
| }, | |
| OS: "linux", | |
| ImageID: "multiplatform", | |
| }, | |
| // Note: This might produce unexpected results, because if the platform-specific manifest | |
| // is not available, and the ImageID points to a multi-platform image, then GetImage will | |
| // return any available platform with host platform being the priority. | |
| // So it will just use whatever platform is returned by GetImage (docker image inspect). | |
| expected: platforms.DefaultSpec(), | |
| }, | |
| { | |
| name: "ImageManifest has priority over ImageID migration", | |
| ctr: Container{ | |
| ImageManifest: &ocispec.Descriptor{ | |
| Digest: "linux/arm64/v8", | |
| }, | |
| OS: "linux", | |
| ImageID: "linux/amd64", | |
| }, | |
| expected: ocispec.Platform{ | |
| OS: "linux", | |
| Architecture: "arm64", | |
| Variant: "v8", | |
| }, | |
| }, | |
| } { | |
| ctr := tc.ctr | |
| t.Run(tc.name, func(t *testing.T) { | |
| migrateContainerOS(context.Background(), mock, &ctr) | |
| assert.DeepEqual(t, tc.expected, ctr.ImagePlatform) | |
| }) | |
| } | |
| } |