Explorar o código

daemon/c8d: Use Docker imagespec

This makes the c8d code which creates/reads OCI types not lose
Docker-specific features like ONBUILD or Healthcheck.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
Paweł Gronowski hai 1 ano
pai
achega
0ffa3dd870

+ 4 - 37
daemon/containerd/image.go

@@ -14,14 +14,12 @@ import (
 	"github.com/containerd/containerd/log"
 	cplatforms "github.com/containerd/containerd/platforms"
 	"github.com/docker/distribution/reference"
-	containertypes "github.com/docker/docker/api/types/container"
 	imagetype "github.com/docker/docker/api/types/image"
 	"github.com/docker/docker/daemon/images"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/image"
-	"github.com/docker/docker/layer"
+	imagespec "github.com/docker/docker/image/spec/specs-go/v1"
 	"github.com/docker/docker/pkg/platforms"
-	"github.com/docker/go-connections/nat"
 	"github.com/opencontainers/go-digest"
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/pkg/errors"
@@ -44,7 +42,7 @@ func (i *ImageService) GetImage(ctx context.Context, refOrID string, options ima
 
 	cs := i.client.ContentStore()
 
-	var presentImages []ocispec.Image
+	var presentImages []imagespec.DockerOCIImage
 	err = i.walkImageManifests(ctx, desc, func(img *ImageManifest) error {
 		conf, err := img.Config(ctx)
 		if err != nil {
@@ -57,7 +55,7 @@ func (i *ImageService) GetImage(ctx context.Context, refOrID string, options ima
 			return errdefs.System(fmt.Errorf("failed to get config descriptor: %w", err))
 		}
 
-		var ociimage ocispec.Image
+		var ociimage imagespec.DockerOCIImage
 		if err := readConfig(ctx, cs, conf, &ociimage); err != nil {
 			if cerrdefs.IsNotFound(err) {
 				log.G(ctx).WithFields(log.Fields{
@@ -84,38 +82,7 @@ func (i *ImageService) GetImage(ctx context.Context, refOrID string, options ima
 	})
 	ociimage := presentImages[0]
 
-	rootfs := image.NewRootFS()
-	for _, id := range ociimage.RootFS.DiffIDs {
-		rootfs.Append(layer.DiffID(id))
-	}
-	exposedPorts := make(nat.PortSet, len(ociimage.Config.ExposedPorts))
-	for k, v := range ociimage.Config.ExposedPorts {
-		exposedPorts[nat.Port(k)] = v
-	}
-
-	img := image.NewImage(image.ID(desc.Target.Digest))
-	img.V1Image = image.V1Image{
-		ID:           string(desc.Target.Digest),
-		OS:           ociimage.OS,
-		Architecture: ociimage.Architecture,
-		Variant:      ociimage.Variant,
-		Created:      ociimage.Created,
-		Config: &containertypes.Config{
-			Entrypoint:   ociimage.Config.Entrypoint,
-			Env:          ociimage.Config.Env,
-			Cmd:          ociimage.Config.Cmd,
-			User:         ociimage.Config.User,
-			WorkingDir:   ociimage.Config.WorkingDir,
-			ExposedPorts: exposedPorts,
-			Volumes:      ociimage.Config.Volumes,
-			Labels:       ociimage.Config.Labels,
-			StopSignal:   ociimage.Config.StopSignal,
-		},
-	}
-
-	img.RootFS = rootfs
-	img.History = ociimage.History
-
+	img := dockerOciImageToDockerImagePartial(image.ID(desc.Target.Digest), ociimage)
 	if options.Details {
 		lastUpdated := time.Unix(0, 0)
 		size, err := i.size(ctx, desc.Target, platform)

+ 1 - 37
daemon/containerd/image_builder.go

@@ -390,43 +390,7 @@ func (i *ImageService) CreateImage(ctx context.Context, config []byte, parent st
 		return nil, err
 	}
 
-	rootFS := ocispec.RootFS{
-		Type:    imgToCreate.RootFS.Type,
-		DiffIDs: []digest.Digest{},
-	}
-	for _, diffId := range imgToCreate.RootFS.DiffIDs {
-		rootFS.DiffIDs = append(rootFS.DiffIDs, digest.Digest(diffId))
-	}
-	exposedPorts := make(map[string]struct{}, len(imgToCreate.Config.ExposedPorts))
-	for k, v := range imgToCreate.Config.ExposedPorts {
-		exposedPorts[string(k)] = v
-	}
-
-	// make an ocispec.Image from the docker/image.Image
-	ociImgToCreate := ocispec.Image{
-		Created: imgToCreate.Created,
-		Author:  imgToCreate.Author,
-		Platform: ocispec.Platform{
-			Architecture: imgToCreate.Architecture,
-			Variant:      imgToCreate.Variant,
-			OS:           imgToCreate.OS,
-			OSVersion:    imgToCreate.OSVersion,
-			OSFeatures:   imgToCreate.OSFeatures,
-		},
-		Config: ocispec.ImageConfig{
-			User:         imgToCreate.Config.User,
-			ExposedPorts: exposedPorts,
-			Env:          imgToCreate.Config.Env,
-			Entrypoint:   imgToCreate.Config.Entrypoint,
-			Cmd:          imgToCreate.Config.Cmd,
-			Volumes:      imgToCreate.Config.Volumes,
-			WorkingDir:   imgToCreate.Config.WorkingDir,
-			Labels:       imgToCreate.Config.Labels,
-			StopSignal:   imgToCreate.Config.StopSignal,
-		},
-		RootFS:  rootFS,
-		History: imgToCreate.History,
-	}
+	ociImgToCreate := dockerImageToDockerOCIImage(*imgToCreate)
 
 	var layers []ocispec.Descriptor
 	// if the image has a parent, we need to start with the parents layers descriptors

+ 25 - 23
daemon/containerd/image_commit.go

@@ -21,6 +21,7 @@ import (
 	"github.com/containerd/containerd/snapshots"
 	"github.com/docker/docker/api/types/backend"
 	"github.com/docker/docker/image"
+	imagespec "github.com/docker/docker/image/spec/specs-go/v1"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/image-spec/identity"
@@ -40,7 +41,7 @@ func (i *ImageService) CommitImage(ctx context.Context, cc backend.CommitConfig)
 	cs := i.client.ContentStore()
 
 	var parentManifest ocispec.Manifest
-	var parentImage ocispec.Image
+	var parentImage imagespec.DockerOCIImage
 
 	// ImageManifest can be nil when committing an image with base FROM scratch
 	if container.ImageManifest != nil {
@@ -121,7 +122,7 @@ func (i *ImageService) CommitImage(ctx context.Context, cc backend.CommitConfig)
 
 // generateCommitImageConfig generates an OCI Image config based on the
 // container's image and the CommitConfig options.
-func generateCommitImageConfig(baseConfig ocispec.Image, diffID digest.Digest, opts backend.CommitConfig) ocispec.Image {
+func generateCommitImageConfig(baseConfig imagespec.DockerOCIImage, diffID digest.Digest, opts backend.CommitConfig) imagespec.DockerOCIImage {
 	if opts.Author == "" {
 		opts.Author = baseConfig.Author
 	}
@@ -144,31 +145,32 @@ func generateCommitImageConfig(baseConfig ocispec.Image, diffID digest.Digest, o
 		diffIds = append(diffIds, diffID)
 	}
 
-	return ocispec.Image{
-		Platform: ocispec.Platform{
-			Architecture: arch,
-			OS:           os,
-		},
-		Created: &createdTime,
-		Author:  opts.Author,
-		Config:  containerConfigToOciImageConfig(opts.Config),
-		RootFS: ocispec.RootFS{
-			Type:    "layers",
-			DiffIDs: diffIds,
+	return imagespec.DockerOCIImage{
+		Image: ocispec.Image{
+			Platform: ocispec.Platform{
+				Architecture: arch,
+				OS:           os,
+			},
+			Created: &createdTime,
+			Author:  opts.Author,
+			RootFS: ocispec.RootFS{
+				Type:    "layers",
+				DiffIDs: diffIds,
+			},
+			History: append(baseConfig.History, ocispec.History{
+				Created:    &createdTime,
+				CreatedBy:  strings.Join(opts.ContainerConfig.Cmd, " "),
+				Author:     opts.Author,
+				Comment:    opts.Comment,
+				EmptyLayer: diffID == "",
+			}),
 		},
-		History: append(baseConfig.History, ocispec.History{
-			Created:   &createdTime,
-			CreatedBy: strings.Join(opts.ContainerConfig.Cmd, " "),
-			Author:    opts.Author,
-			Comment:   opts.Comment,
-			// TODO(laurazard): this check might be incorrect
-			EmptyLayer: diffID == "",
-		}),
+		Config: containerConfigToDockerOCIImageConfig(opts.Config),
 	}
 }
 
 // writeContentsForImage will commit oci image config and manifest into containerd's content store.
-func writeContentsForImage(ctx context.Context, snName string, cs content.Store, newConfig ocispec.Image, layers []ocispec.Descriptor) (ocispec.Descriptor, error) {
+func writeContentsForImage(ctx context.Context, snName string, cs content.Store, newConfig imagespec.DockerOCIImage, layers []ocispec.Descriptor) (ocispec.Descriptor, error) {
 	newConfigJSON, err := json.Marshal(newConfig)
 	if err != nil {
 		return ocispec.Descriptor{}, err
@@ -275,7 +277,7 @@ func createDiff(ctx context.Context, name string, sn snapshots.Snapshotter, cs c
 }
 
 // applyDiffLayer will apply diff layer content created by createDiff into the snapshotter.
-func applyDiffLayer(ctx context.Context, name string, baseImg ocispec.Image, sn snapshots.Snapshotter, differ diff.Applier, diffDesc ocispec.Descriptor) (retErr error) {
+func applyDiffLayer(ctx context.Context, name string, baseImg imagespec.DockerOCIImage, sn snapshots.Snapshotter, differ diff.Applier, diffDesc ocispec.Descriptor) (retErr error) {
 	var (
 		key    = uniquePart() + "-" + name
 		parent = identity.ChainID(baseImg.RootFS.DiffIDs).String()

+ 20 - 39
daemon/containerd/image_import.go

@@ -19,6 +19,7 @@ import (
 	"github.com/docker/docker/builder/dockerfile"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/image"
+	imagespec "github.com/docker/docker/image/spec/specs-go/v1"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/pools"
 	"github.com/google/uuid"
@@ -87,26 +88,28 @@ func (i *ImageService) ImportImage(ctx context.Context, ref reference.Named, pla
 		Size:      size,
 	}
 
-	ociCfg := containerConfigToOciImageConfig(imageConfig)
+	dockerCfg := containerConfigToDockerOCIImageConfig(imageConfig)
 	createdAt := time.Now()
-	config := ocispec.Image{
-		Platform: *platform,
-		Created:  &createdAt,
-		Author:   "",
-		Config:   ociCfg,
-		RootFS: ocispec.RootFS{
-			Type:    "layers",
-			DiffIDs: []digest.Digest{uncompressedDigest},
-		},
-		History: []ocispec.History{
-			{
-				Created:    &createdAt,
-				CreatedBy:  "",
-				Author:     "",
-				Comment:    msg,
-				EmptyLayer: false,
+	config := imagespec.DockerOCIImage{
+		Image: ocispec.Image{
+			Platform: *platform,
+			Created:  &createdAt,
+			Author:   "",
+			RootFS: ocispec.RootFS{
+				Type:    "layers",
+				DiffIDs: []digest.Digest{uncompressedDigest},
+			},
+			History: []ocispec.History{
+				{
+					Created:    &createdAt,
+					CreatedBy:  "",
+					Author:     "",
+					Comment:    msg,
+					EmptyLayer: false,
+				},
 			},
 		},
+		Config: dockerCfg,
 	}
 	configDesc, err := storeJson(ctx, cs, ocispec.MediaTypeImageConfig, config, nil)
 	if err != nil {
@@ -383,25 +386,3 @@ func storeJson(ctx context.Context, cs content.Ingester, mt string, obj interfac
 	}
 	return desc, nil
 }
-
-func containerConfigToOciImageConfig(cfg *container.Config) ocispec.ImageConfig {
-	ociCfg := ocispec.ImageConfig{
-		User:        cfg.User,
-		Env:         cfg.Env,
-		Entrypoint:  cfg.Entrypoint,
-		Cmd:         cfg.Cmd,
-		Volumes:     cfg.Volumes,
-		WorkingDir:  cfg.WorkingDir,
-		Labels:      cfg.Labels,
-		StopSignal:  cfg.StopSignal,
-		ArgsEscaped: cfg.ArgsEscaped,
-	}
-	if len(cfg.ExposedPorts) > 0 {
-		ociCfg.ExposedPorts = map[string]struct{}{}
-		for k, v := range cfg.ExposedPorts {
-			ociCfg.ExposedPorts[string(k)] = v
-		}
-	}
-
-	return ociCfg
-}

+ 2 - 2
daemon/containerd/image_import_test.go

@@ -10,8 +10,8 @@ import (
 )
 
 // regression test for https://github.com/moby/moby/issues/45904
-func TestContainerConfigToOciImageConfig(t *testing.T) {
-	ociCFG := containerConfigToOciImageConfig(&container.Config{
+func TestContainerConfigToDockerImageConfig(t *testing.T) {
+	ociCFG := containerConfigToDockerOCIImageConfig(&container.Config{
 		ExposedPorts: nat.PortSet{
 			"80/tcp": struct{}{},
 		},

+ 128 - 0
daemon/containerd/imagespec.go

@@ -0,0 +1,128 @@
+package containerd
+
+import (
+	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/dockerversion"
+	"github.com/docker/docker/image"
+	imagespec "github.com/docker/docker/image/spec/specs-go/v1"
+	"github.com/docker/docker/layer"
+	"github.com/docker/go-connections/nat"
+	"github.com/opencontainers/go-digest"
+	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+// dockerOciImageToDockerImagePartial creates an image.Image from the imagespec.DockerOCIImage
+// It doesn't set:
+// - V1Image.ContainerConfig
+// - V1Image.Container
+// - Details
+func dockerOciImageToDockerImagePartial(id image.ID, img imagespec.DockerOCIImage) *image.Image {
+	v1Image := image.V1Image{
+		DockerVersion: dockerversion.Version,
+		Config:        dockerOCIImageConfigToContainerConfig(img.Config),
+		Architecture:  img.Platform.Architecture,
+		Variant:       img.Platform.Variant,
+		OS:            img.Platform.OS,
+		Author:        img.Author,
+		Created:       img.Created,
+	}
+
+	rootFS := &image.RootFS{
+		Type: img.RootFS.Type,
+	}
+	for _, diffId := range img.RootFS.DiffIDs {
+		rootFS.DiffIDs = append(rootFS.DiffIDs, layer.DiffID(diffId))
+	}
+
+	out := image.NewImage(id)
+	out.V1Image = v1Image
+	out.RootFS = rootFS
+	out.History = img.History
+	out.OSFeatures = img.OSFeatures
+	out.OSVersion = img.OSVersion
+	return out
+}
+
+func dockerImageToDockerOCIImage(img image.Image) imagespec.DockerOCIImage {
+	rootfs := ocispec.RootFS{
+		Type:    img.RootFS.Type,
+		DiffIDs: []digest.Digest{},
+	}
+	for _, diffId := range img.RootFS.DiffIDs {
+		rootfs.DiffIDs = append(rootfs.DiffIDs, digest.Digest(diffId))
+	}
+
+	return imagespec.DockerOCIImage{
+		Image: ocispec.Image{
+			Created: img.Created,
+			Author:  img.Author,
+			Platform: ocispec.Platform{
+				Architecture: img.Architecture,
+				Variant:      img.Variant,
+				OS:           img.OS,
+				OSVersion:    img.OSVersion,
+				OSFeatures:   img.OSFeatures,
+			},
+			RootFS:  rootfs,
+			History: img.History,
+		},
+		Config: containerConfigToDockerOCIImageConfig(img.Config),
+	}
+}
+
+func containerConfigToDockerOCIImageConfig(cfg *container.Config) imagespec.DockerOCIImageConfig {
+	var ociCfg ocispec.ImageConfig
+	var ext imagespec.DockerOCIImageConfigExt
+
+	if cfg != nil {
+		ociCfg = ocispec.ImageConfig{
+			User:        cfg.User,
+			Env:         cfg.Env,
+			Entrypoint:  cfg.Entrypoint,
+			Cmd:         cfg.Cmd,
+			Volumes:     cfg.Volumes,
+			WorkingDir:  cfg.WorkingDir,
+			Labels:      cfg.Labels,
+			StopSignal:  cfg.StopSignal,
+			ArgsEscaped: cfg.ArgsEscaped, //nolint:staticcheck // Ignore SA1019. Need to keep it in image.
+		}
+
+		if len(cfg.ExposedPorts) > 0 {
+			ociCfg.ExposedPorts = map[string]struct{}{}
+			for k, v := range cfg.ExposedPorts {
+				ociCfg.ExposedPorts[string(k)] = v
+			}
+		}
+		ext.Healthcheck = cfg.Healthcheck
+		ext.OnBuild = cfg.OnBuild
+		ext.Shell = cfg.Shell
+	}
+
+	return imagespec.DockerOCIImageConfig{
+		ImageConfig:             ociCfg,
+		DockerOCIImageConfigExt: ext,
+	}
+}
+
+func dockerOCIImageConfigToContainerConfig(cfg imagespec.DockerOCIImageConfig) *container.Config {
+	exposedPorts := make(nat.PortSet, len(cfg.ExposedPorts))
+	for k, v := range cfg.ExposedPorts {
+		exposedPorts[nat.Port(k)] = v
+	}
+
+	return &container.Config{
+		Entrypoint:   cfg.Entrypoint,
+		Env:          cfg.Env,
+		Cmd:          cfg.Cmd,
+		User:         cfg.User,
+		WorkingDir:   cfg.WorkingDir,
+		ExposedPorts: exposedPorts,
+		Volumes:      cfg.Volumes,
+		Labels:       cfg.Labels,
+		ArgsEscaped:  cfg.ArgsEscaped, //nolint:staticcheck // Ignore SA1019. Need to keep it in image.
+		StopSignal:   cfg.StopSignal,
+		Healthcheck:  cfg.Healthcheck,
+		OnBuild:      cfg.OnBuild,
+		Shell:        cfg.Shell,
+	}
+}