content: unify provider and ingester

The split between provider and ingester was a long standing division
reflecting the client-side use cases. For the most part, we were
differentiating these for the algorithms that operate them, but it made
instantation and use of the types challenging. On the server-side, this
distinction is generally less important. This change unifies these types
and in the process we get a few benefits.

The first is that we now completely access the content store over GRPC.
This was the initial intent and we have now satisfied this goal
completely. There are a few issues around listing content and getting
status, but we resolve these with simple streaming and regexp filters.
More can probably be done to polish this but the result is clean.

Several other content-oriented methods were polished in the process of
unification. We have now properly seperated out the `Abort` method to
cancel ongoing or stalled ingest processes. We have also replaced the
`Active` method with a single status method.

The transition went extremely smoothly. Once the clients were updated to
use the new methods, every thing worked as expected on the first
compile.

Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
Stephen J Day
2017-05-08 20:43:30 -07:00
parent 31cf34b726
commit 193abed96e
34 changed files with 1779 additions and 859 deletions

View File

@@ -242,7 +242,7 @@ func serveDebugAPI() error {
return nil
}
func resolveContentStore() (*content.Store, error) {
func resolveContentStore() (content.Store, error) {
cp := filepath.Join(conf.Root, "content")
return content.NewStore(cp)
}
@@ -315,7 +315,7 @@ func loadMonitor() (plugin.ContainerMonitor, error) {
return plugin.NewMultiContainerMonitor(monitors...), nil
}
func loadSnapshotter(store *content.Store) (snapshot.Snapshotter, error) {
func loadSnapshotter(store content.Store) (snapshot.Snapshotter, error) {
for name, sr := range plugin.Registrations() {
if sr.Type != plugin.SnapshotPlugin {
continue
@@ -356,7 +356,7 @@ func newGRPCServer() *grpc.Server {
return s
}
func loadServices(runtimes map[string]containerd.Runtime, store *content.Store, sn snapshot.Snapshotter, meta *bolt.DB) ([]plugin.Service, error) {
func loadServices(runtimes map[string]containerd.Runtime, store content.Store, sn snapshot.Snapshotter, meta *bolt.DB) ([]plugin.Service, error) {
var o []plugin.Service
for name, sr := range plugin.Registrations() {
if sr.Type != plugin.GRPCPlugin {

View File

@@ -78,7 +78,7 @@ var runCommand = cli.Command{
return err
}
provider, err := getContentProvider(context)
content, err := getContentStore(context)
if err != nil {
return err
}
@@ -102,7 +102,7 @@ var runCommand = cli.Command{
}
// let's close out our db and tx so we don't hold the lock whilst running.
diffIDs, err := image.RootFS(ctx, provider)
diffIDs, err := image.RootFS(ctx, content)
if err != nil {
return err
}
@@ -123,13 +123,13 @@ var runCommand = cli.Command{
return err
}
ic, err := image.Config(ctx, provider)
ic, err := image.Config(ctx, content)
if err != nil {
return err
}
switch ic.MediaType {
case ocispec.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config:
r, err := provider.Reader(ctx, ic.Digest)
r, err := content.Reader(ctx, ic.Digest)
if err != nil {
return err
}

View File

@@ -35,12 +35,12 @@ func getExecutionService(context *cli.Context) (execution.ContainerServiceClient
return execution.NewContainerServiceClient(conn), nil
}
func getContentProvider(context *cli.Context) (content.Provider, error) {
func getContentStore(context *cli.Context) (content.Store, error) {
conn, err := getGRPCConnection(context)
if err != nil {
return nil, err
}
return contentservice.NewProviderFromClient(contentapi.NewContentClient(conn)), nil
return contentservice.NewStoreFromClient(contentapi.NewContentClient(conn)), nil
}
func getRootFSService(context *cli.Context) (rootfsapi.RootFSClient, error) {

11
cmd/dist/active.go vendored
View File

@@ -13,7 +13,7 @@ import (
var activeCommand = cli.Command{
Name: "active",
Usage: "display active transfers.",
ArgsUsage: "[flags] [<key>, ...]",
ArgsUsage: "[flags] [<regexp>]",
Description: `Display the ongoing transfers.`,
Flags: []cli.Flag{
cli.DurationFlag{
@@ -28,12 +28,19 @@ var activeCommand = cli.Command{
},
},
Action: func(context *cli.Context) error {
var (
match = context.Args().First()
)
ctx, cancel := appContext()
defer cancel()
cs, err := resolveContentStore(context)
if err != nil {
return err
}
active, err := cs.Active()
active, err := cs.Status(ctx, match)
if err != nil {
return err
}

11
cmd/dist/common.go vendored
View File

@@ -12,11 +12,13 @@ import (
"time"
"github.com/containerd/console"
contentapi "github.com/containerd/containerd/api/services/content"
imagesapi "github.com/containerd/containerd/api/services/images"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/docker"
contentservice "github.com/containerd/containerd/services/content"
imagesservice "github.com/containerd/containerd/services/images"
"github.com/pkg/errors"
"github.com/urfave/cli"
@@ -42,7 +44,7 @@ var registryFlags = []cli.Flag{
},
}
func resolveContentStore(context *cli.Context) (*content.Store, error) {
func resolveContentStore(context *cli.Context) (content.Store, error) {
root := filepath.Join(context.GlobalString("root"), "content")
if !filepath.IsAbs(root) {
var err error
@@ -51,7 +53,12 @@ func resolveContentStore(context *cli.Context) (*content.Store, error) {
return nil, err
}
}
return content.NewStore(root)
conn, err := connectGRPC(context)
if err != nil {
return nil, err
}
return contentservice.NewStoreFromClient(contentapi.NewContentClient(conn)), nil
}
func resolveImageStore(clicontext *cli.Context) (images.Store, error) {

9
cmd/dist/edit.go vendored
View File

@@ -9,7 +9,7 @@ import (
"os/exec"
contentapi "github.com/containerd/containerd/api/services/content"
contentservice "github.com/containerd/containerd/services/content"
"github.com/containerd/containerd/services/content"
digest "github.com/opencontainers/go-digest"
"github.com/urfave/cli"
)
@@ -50,10 +50,9 @@ var editCommand = cli.Command{
return err
}
provider := contentservice.NewProviderFromClient(contentapi.NewContentClient(conn))
ingester := contentservice.NewIngesterFromClient(contentapi.NewContentClient(conn))
content := content.NewStoreFromClient(contentapi.NewContentClient(conn))
rc, err := provider.Reader(ctx, dgst)
rc, err := content.Reader(ctx, dgst)
if err != nil {
return err
}
@@ -65,7 +64,7 @@ var editCommand = cli.Command{
}
defer nrc.Close()
wr, err := ingester.Writer(ctx, "edit-"+object, 0, "") // TODO(stevvooe): Choose a better key?
wr, err := content.Writer(ctx, "edit-"+object, 0, "") // TODO(stevvooe): Choose a better key?
if err != nil {
return err
}

9
cmd/dist/fetch.go vendored
View File

@@ -59,8 +59,7 @@ Most of this is experimental and there are few leaps to make this work.`,
ongoing := newJobs()
ingester := contentservice.NewIngesterFromClient(contentapi.NewContentClient(conn))
provider := contentservice.NewProviderFromClient(contentapi.NewContentClient(conn))
content := contentservice.NewStoreFromClient(contentapi.NewContentClient(conn))
// TODO(stevvooe): Need to replace this with content store client.
cs, err := resolveContentStore(clicontext)
@@ -85,8 +84,8 @@ Most of this is experimental and there are few leaps to make this work.`,
ongoing.add(remotes.MakeRefKey(ctx, desc))
return nil, nil
}),
remotes.FetchHandler(ingester, fetcher),
images.ChildrenHandler(provider),
remotes.FetchHandler(content, fetcher),
images.ChildrenHandler(content),
),
desc)
})
@@ -114,7 +113,7 @@ Most of this is experimental and there are few leaps to make this work.`,
activeSeen := map[string]struct{}{}
if !done {
active, err := cs.Active()
active, err := cs.Status(ctx, "")
if err != nil {
log.G(ctx).WithError(err).Error("active check failed")
continue

6
cmd/dist/get.go vendored
View File

@@ -4,8 +4,6 @@ import (
"io"
"os"
contentapi "github.com/containerd/containerd/api/services/content"
contentservice "github.com/containerd/containerd/services/content"
digest "github.com/opencontainers/go-digest"
"github.com/urfave/cli"
)
@@ -25,13 +23,11 @@ var getCommand = cli.Command{
return err
}
conn, err := connectGRPC(context)
cs, err := resolveContentStore(context)
if err != nil {
return err
}
cs := contentservice.NewProviderFromClient(contentapi.NewContentClient(conn))
rc, err := cs.Reader(ctx, dgst)
if err != nil {
return err

8
cmd/dist/images.go vendored
View File

@@ -5,10 +5,8 @@ import (
"os"
"text/tabwriter"
contentapi "github.com/containerd/containerd/api/services/content"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/progress"
contentservice "github.com/containerd/containerd/services/content"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
@@ -29,13 +27,11 @@ var imagesListCommand = cli.Command{
return err
}
conn, err := connectGRPC(clicontext)
cs, err := resolveContentStore(clicontext)
if err != nil {
return err
}
provider := contentservice.NewProviderFromClient(contentapi.NewContentClient(conn))
images, err := imageStore.List(ctx)
if err != nil {
return errors.Wrap(err, "failed to list images")
@@ -44,7 +40,7 @@ var imagesListCommand = cli.Command{
tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, ' ', 0)
fmt.Fprintln(tw, "REF\tTYPE\tDIGEST\tSIZE\t")
for _, image := range images {
size, err := image.Size(ctx, provider)
size, err := image.Size(ctx, cs)
if err != nil {
log.G(ctx).WithError(err).Errorf("failed calculating size for image %s", image.Name)
}

14
cmd/dist/ingest.go vendored
View File

@@ -3,9 +3,7 @@ package main
import (
"os"
contentapi "github.com/containerd/containerd/api/services/content"
"github.com/containerd/containerd/content"
contentservice "github.com/containerd/containerd/services/content"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"github.com/urfave/cli"
@@ -40,20 +38,18 @@ var ingestCommand = cli.Command{
return err
}
conn, err := connectGRPC(context)
if err != nil {
return err
}
if ref == "" {
return errors.New("must specify a transaction reference")
}
ingester := contentservice.NewIngesterFromClient(contentapi.NewContentClient(conn))
cs, err := resolveContentStore(context)
if err != nil {
return err
}
// TODO(stevvooe): Allow ingest to be reentrant. Currently, we expect
// all data to be written in a single invocation. Allow multiple writes
// to the same transaction key followed by a commit.
return content.WriteBlob(ctx, ingester, ref, os.Stdin, expectedSize, expectedDigest)
return content.WriteBlob(ctx, cs, ref, os.Stdin, expectedSize, expectedDigest)
},
}

15
cmd/dist/list.go vendored
View File

@@ -9,7 +9,6 @@ import (
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/log"
units "github.com/docker/go-units"
digest "github.com/opencontainers/go-digest"
"github.com/urfave/cli"
)
@@ -46,8 +45,8 @@ var listCommand = cli.Command{
var walkFn content.WalkFunc
if quiet {
walkFn = func(path string, fi os.FileInfo, dgst digest.Digest) error {
fmt.Println(dgst)
walkFn = func(info content.Info) error {
fmt.Println(info.Digest)
return nil
}
} else {
@@ -55,16 +54,16 @@ var listCommand = cli.Command{
defer tw.Flush()
fmt.Fprintln(tw, "DIGEST\tSIZE\tAGE")
walkFn = func(path string, fi os.FileInfo, dgst digest.Digest) error {
walkFn = func(info content.Info) error {
fmt.Fprintf(tw, "%s\t%s\t%s\n",
dgst,
units.HumanSize(float64(fi.Size())),
units.HumanDuration(time.Since(fi.ModTime())))
info.Digest,
units.HumanSize(float64(info.Size)),
units.HumanDuration(time.Since(info.CommittedAt)))
return nil
}
}
return cs.Walk(walkFn)
return cs.Walk(ctx, walkFn)
},
}

29
cmd/dist/pull.go vendored
View File

@@ -7,14 +7,12 @@ import (
"text/tabwriter"
"time"
contentapi "github.com/containerd/containerd/api/services/content"
rootfsapi "github.com/containerd/containerd/api/services/rootfs"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/progress"
"github.com/containerd/containerd/remotes"
contentservice "github.com/containerd/containerd/services/content"
rootfsservice "github.com/containerd/containerd/services/rootfs"
"github.com/opencontainers/image-spec/identity"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
@@ -44,7 +42,7 @@ command. As part of this process, we do the following:
ctx, cancel := appContext()
defer cancel()
conn, err := connectGRPC(clicontext)
cs, err := resolveContentStore(clicontext)
if err != nil {
return err
}
@@ -60,15 +58,6 @@ command. As part of this process, we do the following:
}
ongoing := newJobs()
// TODO(stevvooe): Must unify this type.
ingester := contentservice.NewIngesterFromClient(contentapi.NewContentClient(conn))
provider := contentservice.NewProviderFromClient(contentapi.NewContentClient(conn))
cs, err := resolveContentStore(clicontext)
if err != nil {
return err
}
eg, ctx := errgroup.WithContext(ctx)
var resolvedImageName string
@@ -93,8 +82,8 @@ command. As part of this process, we do the following:
ongoing.add(remotes.MakeRefKey(ctx, desc))
return nil, nil
}),
remotes.FetchHandler(ingester, fetcher),
images.ChildrenHandler(provider)),
remotes.FetchHandler(cs, fetcher),
images.ChildrenHandler(cs)),
desc)
})
@@ -118,9 +107,7 @@ command. As part of this process, we do the following:
log.G(ctx).Fatal(err)
}
provider := contentservice.NewProviderFromClient(contentapi.NewContentClient(conn))
p, err := content.ReadBlob(ctx, provider, image.Target.Digest)
p, err := content.ReadBlob(ctx, cs, image.Target.Digest)
if err != nil {
log.G(ctx).Fatal(err)
}
@@ -130,6 +117,10 @@ command. As part of this process, we do the following:
log.G(ctx).Fatal(err)
}
conn, err := connectGRPC(clicontext)
if err != nil {
log.G(ctx).Fatal(err)
}
rootfs := rootfsservice.NewUnpackerFromClient(rootfsapi.NewRootFSClient(conn))
log.G(ctx).Info("unpacking rootfs")
@@ -138,7 +129,7 @@ command. As part of this process, we do the following:
log.G(ctx).Fatal(err)
}
diffIDs, err := image.RootFS(ctx, provider)
diffIDs, err := image.RootFS(ctx, cs)
if err != nil {
log.G(ctx).WithError(err).Fatal("failed resolving rootfs")
}
@@ -168,7 +159,7 @@ command. As part of this process, we do the following:
activeSeen := map[string]struct{}{}
if !done {
active, err := cs.Active()
active, err := cs.Status(ctx, "")
if err != nil {
log.G(ctx).WithError(err).Error("active check failed")
continue

10
cmd/dist/rootfs.go vendored
View File

@@ -8,11 +8,9 @@ import (
"os"
"strings"
contentapi "github.com/containerd/containerd/api/services/content"
rootfsapi "github.com/containerd/containerd/api/services/rootfs"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/log"
contentservice "github.com/containerd/containerd/services/content"
rootfsservice "github.com/containerd/containerd/services/rootfs"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
@@ -49,8 +47,12 @@ var rootfsUnpackCommand = cli.Command{
return err
}
provider := contentservice.NewProviderFromClient(contentapi.NewContentClient(conn))
m, err := resolveManifest(ctx, provider, dgst)
cs, err := resolveContentStore(clicontext)
if err != nil {
return err
}
m, err := resolveManifest(ctx, cs, dgst)
if err != nil {
return err
}