File: migration.go

package info (click to toggle)
docker.io 28.5.2%2Bdfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 69,048 kB
  • sloc: sh: 5,867; makefile: 863; ansic: 184; python: 162; asm: 159
file content (140 lines) | stat: -rw-r--r-- 4,106 bytes parent folder | download
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
}