From 413813db7f261531c7b68ff3dfd4b024cc00c03b Mon Sep 17 00:00:00 2001 From: Djordje Lukic Date: Tue, 5 Jul 2022 16:33:39 +0200 Subject: [PATCH] Create the containerd image service Initial pull/ls works Build is deactivated if the feature is active Signed-off-by: Djordje Lukic --- cmd/dockerd/daemon.go | 65 ++++++---- daemon/containerd/service.go | 244 +++++++++++++++++++++++++++++++++++ daemon/daemon.go | 11 +- daemon/oci_linux.go | 4 +- 4 files changed, 291 insertions(+), 33 deletions(-) create mode 100644 daemon/containerd/service.go diff --git a/cmd/dockerd/daemon.go b/cmd/dockerd/daemon.go index a5ff9b6ba4665..9e65d74c37666 100644 --- a/cmd/dockerd/daemon.go +++ b/cmd/dockerd/daemon.go @@ -234,7 +234,7 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) { routerOptions.api = cli.api routerOptions.cluster = c - initRouter(routerOptions) + initRouter(d, routerOptions) go d.ProcessClusterNotifications(ctx, c.GetWatchStream()) @@ -291,34 +291,39 @@ func newRouterOptions(config *config.Config, d *daemon.Daemon) (routerOptions, e return opts, err } cgroupParent := newCgroupParent(config) - bk, err := buildkit.New(buildkit.Opt{ - SessionManager: sm, - Root: filepath.Join(config.Root, "buildkit"), - Dist: d.DistributionServices(), - NetworkController: d.NetworkController(), - DefaultCgroupParent: cgroupParent, - RegistryHosts: d.RegistryHosts(), - BuilderConfig: config.Builder, - Rootless: d.Rootless(), - IdentityMapping: d.IdentityMapping(), - DNSConfig: config.DNSConfig, - ApparmorProfile: daemon.DefaultApparmorProfile(), - }) - if err != nil { - return opts, err - } - - bb, err := buildbackend.NewBackend(d.ImageService(), manager, bk, d.EventsService) - if err != nil { - return opts, errors.Wrap(err, "failed to create buildmanager") - } - return routerOptions{ + ro := routerOptions{ sessionManager: sm, - buildBackend: bb, - buildkit: bk, features: d.Features(), daemon: d, - }, nil + } + if !d.UsesSnapshotter() { + bk, err := buildkit.New(buildkit.Opt{ + SessionManager: sm, + Root: filepath.Join(config.Root, "buildkit"), + Dist: d.DistributionServices(), + NetworkController: d.NetworkController(), + DefaultCgroupParent: cgroupParent, + RegistryHosts: d.RegistryHosts(), + BuilderConfig: config.Builder, + Rootless: d.Rootless(), + IdentityMapping: d.IdentityMapping(), + DNSConfig: config.DNSConfig, + ApparmorProfile: daemon.DefaultApparmorProfile(), + }) + if err != nil { + return opts, err + } + + bb, err := buildbackend.NewBackend(d.ImageService(), manager, bk, d.EventsService) + if err != nil { + return opts, errors.Wrap(err, "failed to create buildmanager") + } + + ro.buildBackend = bb + ro.buildkit = bk + } + + return ro, nil } func (cli *DaemonCli) reloadConfig() { @@ -521,7 +526,7 @@ func checkDeprecatedOptions(config *config.Config) error { return nil } -func initRouter(opts routerOptions) { +func initRouter(d *daemon.Daemon, opts routerOptions) { decoder := runconfig.ContainerDecoder{ GetSysInfo: func() *sysinfo.SysInfo { return opts.daemon.RawSysInfo() @@ -548,7 +553,11 @@ func initRouter(opts routerOptions) { } grpcBackends := []grpcrouter.Backend{} - for _, b := range []interface{}{opts.daemon, opts.buildBackend} { + backends := []interface{}{opts.daemon} + if !d.UsesSnapshotter() { + backends = append(backends, opts.buildBackend) + } + for _, b := range backends { if b, ok := b.(grpcrouter.Backend); ok { grpcBackends = append(grpcBackends, b) } diff --git a/daemon/containerd/service.go b/daemon/containerd/service.go new file mode 100644 index 0000000000000..ccd545d800907 --- /dev/null +++ b/daemon/containerd/service.go @@ -0,0 +1,244 @@ +package containerd + +import ( + "context" + "io" + + "github.com/containerd/containerd" + "github.com/containerd/containerd/platforms" + "github.com/docker/distribution" + "github.com/docker/distribution/reference" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/backend" + "github.com/docker/docker/api/types/filters" + imagetype "github.com/docker/docker/api/types/image" + registrytypes "github.com/docker/docker/api/types/registry" + "github.com/docker/docker/builder" + "github.com/docker/docker/container" + "github.com/docker/docker/daemon/images" + "github.com/docker/docker/errdefs" + "github.com/docker/docker/image" + "github.com/docker/docker/layer" + "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +type containerdStore struct { + client *containerd.Client +} + +func NewService(c *containerd.Client) *containerdStore { + return &containerdStore{ + client: c, + } +} + +func (cs *containerdStore) PullImage(ctx context.Context, image, tag string, platform *ocispec.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { + var opts []containerd.RemoteOpt + if platform != nil { + opts = append(opts, containerd.WithPlatform(platforms.Format(*platform))) + } + ref, err := reference.ParseNormalizedNamed(image) + if err != nil { + return errdefs.InvalidParameter(err) + } + + if tag != "" { + // The "tag" could actually be a digest. + var dgst digest.Digest + dgst, err = digest.Parse(tag) + if err == nil { + ref, err = reference.WithDigest(reference.TrimNamed(ref), dgst) + } else { + ref, err = reference.WithTag(ref, tag) + } + if err != nil { + return errdefs.InvalidParameter(err) + } + } + + _, err = cs.client.Pull(ctx, ref.String(), opts...) + return err +} + +func (cs *containerdStore) Images(ctx context.Context, opts types.ImageListOptions) ([]*types.ImageSummary, error) { + images, err := cs.client.ListImages(ctx) + if err != nil { + return nil, err + } + + var ret []*types.ImageSummary + for _, image := range images { + size, err := image.Size(ctx) + if err != nil { + return nil, err + } + + ret = append(ret, &types.ImageSummary{ + RepoDigests: []string{image.Name() + "@" + image.Target().Digest.String()}, // "hello-world@sha256:bfea6278a0a267fad2634554f4f0c6f31981eea41c553fdf5a83e95a41d40c38"}, + RepoTags: []string{image.Name()}, + Containers: -1, + ParentID: "", + SharedSize: -1, + VirtualSize: 10, + ID: image.Target().Digest.String(), + Created: image.Metadata().CreatedAt.Unix(), + Size: size, + }) + } + + return ret, nil +} + +func (cs *containerdStore) LogImageEvent(imageID, refName, action string) { + panic("not implemented") +} + +func (cs *containerdStore) LogImageEventWithAttributes(imageID, refName, action string, attributes map[string]string) { + panic("not implemented") +} + +func (cs *containerdStore) GetLayerFolders(img *image.Image, rwLayer layer.RWLayer) ([]string, error) { + panic("not implemented") +} + +func (cs *containerdStore) Map() map[image.ID]*image.Image { + panic("not implemented") +} + +func (cs *containerdStore) GetLayerByID(string) (layer.RWLayer, error) { + panic("not implemented") +} + +func (cs *containerdStore) GetLayerMountID(string) (string, error) { + panic("not implemented") +} + +func (cs *containerdStore) Cleanup() error { + return nil +} + +func (cs *containerdStore) GraphDriverName() string { + return "" +} + +func (cs *containerdStore) CommitBuildStep(c backend.CommitConfig) (image.ID, error) { + panic("not implemented") +} + +func (cs *containerdStore) CreateImage(config []byte, parent string) (builder.Image, error) { + panic("not implemented") +} + +func (cs *containerdStore) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ROLayer, error) { + panic("not implemented") +} + +func (cs *containerdStore) MakeImageCache(sourceRefs []string) builder.ImageCache { + panic("not implemented") +} + +func (cs *containerdStore) TagImageWithReference(imageID image.ID, newTag reference.Named) error { + panic("not implemented") +} + +func (cs *containerdStore) SquashImage(id, parent string) (string, error) { + panic("not implemented") +} + +func (cs *containerdStore) ExportImage(names []string, outStream io.Writer) error { + panic("not implemented") +} + +func (cs *containerdStore) ImageDelete(imageRef string, force, prune bool) ([]types.ImageDeleteResponseItem, error) { + panic("not implemented") +} + +func (cs *containerdStore) ImageHistory(name string) ([]*imagetype.HistoryResponseItem, error) { + panic("not implemented") +} + +func (cs *containerdStore) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error) { + panic("not implemented") +} + +func (cs *containerdStore) ImportImage(src string, repository string, platform *ocispec.Platform, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error { + panic("not implemented") +} + +func (cs *containerdStore) LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error { + panic("not implemented") +} + +func (cs *containerdStore) LookupImage(ctx context.Context, name string) (*types.ImageInspect, error) { + panic("not implemented") +} + +func (cs *containerdStore) PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { + panic("not implemented") +} + +func (cs *containerdStore) SearchRegistryForImages(ctx context.Context, searchFilters filters.Args, term string, limit int, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registrytypes.SearchResults, error) { + panic("not implemented") +} + +func (cs *containerdStore) TagImage(imageName, repository, tag string) (string, error) { + panic("not implemented") +} + +func (cs *containerdStore) GetRepository(context.Context, reference.Named, *types.AuthConfig) (distribution.Repository, error) { + panic("not implemented") +} + +func (cs *containerdStore) ImageDiskUsage(ctx context.Context) ([]*types.ImageSummary, error) { + panic("not implemented") +} + +func (cs *containerdStore) LayerDiskUsage(ctx context.Context) (int64, error) { + panic("not implemented") +} + +func (cs *containerdStore) ReleaseLayer(rwlayer layer.RWLayer) error { + panic("not implemented") +} + +func (cs *containerdStore) CommitImage(c backend.CommitConfig) (image.ID, error) { + panic("not implemented") +} + +func (cs *containerdStore) GetImage(refOrID string, platform *ocispec.Platform) (retImg *image.Image, retErr error) { + panic("not implemented") +} + +func (cs *containerdStore) CreateLayer(container *container.Container, initFunc layer.MountInit) (layer.RWLayer, error) { + panic("not implemented") +} + +func (cs *containerdStore) DistributionServices() images.DistributionServices { + return images.DistributionServices{} +} + +func (cs *containerdStore) CountImages() int { + imgs, err := cs.client.ListImages(context.TODO()) + if err != nil { + return 0 + } + + return len(imgs) +} + +func (cs *containerdStore) LayerStoreStatus() [][2]string { + return [][2]string{} +} + +func (cs *containerdStore) GetContainerLayerSize(containerID string) (int64, int64) { + panic("not implemented") +} + +func (cs *containerdStore) UpdateConfig(maxDownloads, maxUploads int) { + panic("not implemented") +} + +func (cs *containerdStore) Children(id image.ID) []image.ID { + panic("not implemented") +} diff --git a/daemon/daemon.go b/daemon/daemon.go index c4502a59caba9..64128399a3718 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -28,6 +28,7 @@ import ( "github.com/docker/docker/builder" "github.com/docker/docker/container" "github.com/docker/docker/daemon/config" + ctrd "github.com/docker/docker/daemon/containerd" "github.com/docker/docker/daemon/events" "github.com/docker/docker/daemon/exec" _ "github.com/docker/docker/daemon/graphdriver/register" // register graph drivers @@ -147,8 +148,8 @@ func (daemon *Daemon) Features() *map[string]bool { return &daemon.configStore.Features } -// usesSnapshotter returns true if feature flag to use containerd snapshotter is enabled -func (daemon *Daemon) usesSnapshotter() bool { +// UsesSnapshotter returns true if feature flag to use containerd snapshotter is enabled +func (daemon *Daemon) UsesSnapshotter() bool { if daemon.configStore.Features != nil { if b, ok := daemon.configStore.Features["containerd-snapshotter"]; ok { return b @@ -1091,7 +1092,11 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S // TODO: imageStore, distributionMetadataStore, and ReferenceStore are only // used above to run migration. They could be initialized in ImageService // if migration is called from daemon/images. layerStore might move as well. - d.imageService = images.NewImageService(imgSvcConfig) + if d.UsesSnapshotter() { + d.imageService = ctrd.NewService(d.containerdCli) + } else { + d.imageService = images.NewImageService(imgSvcConfig) + } logrus.Debugf("Max Concurrent Downloads: %d", imgSvcConfig.MaxConcurrentDownloads) logrus.Debugf("Max Concurrent Uploads: %d", imgSvcConfig.MaxConcurrentUploads) logrus.Debugf("Max Download Attempts: %d", imgSvcConfig.MaxDownloadAttempts) diff --git a/daemon/oci_linux.go b/daemon/oci_linux.go index 93efeea8d94f5..8c9aca041010a 100644 --- a/daemon/oci_linux.go +++ b/daemon/oci_linux.go @@ -720,14 +720,14 @@ func sysctlExists(s string) bool { // WithCommonOptions sets common docker options func WithCommonOptions(daemon *Daemon, c *container.Container) coci.SpecOpts { return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { - if c.BaseFS == nil && !daemon.usesSnapshotter() { + if c.BaseFS == nil && !daemon.UsesSnapshotter() { return errors.New("populateCommonSpec: BaseFS of container " + c.ID + " is unexpectedly nil") } linkedEnv, err := daemon.setupLinkedContainers(c) if err != nil { return err } - if !daemon.usesSnapshotter() { + if !daemon.UsesSnapshotter() { s.Root = &specs.Root{ Path: c.BaseFS.Path(), Readonly: c.HostConfig.ReadonlyRootfs,