Update containerd to a6ce1ef2a1

Signed-off-by: Lantao Liu <lantaol@google.com>
This commit is contained in:
Lantao Liu
2017-08-24 21:47:52 +00:00
parent 6f679fd175
commit 8d1b737480
11 changed files with 767 additions and 68 deletions

View File

@@ -60,25 +60,25 @@ image, err := client.Pull(context, "docker.io/library/redis:latest")
err := client.Push(context, "docker.io/library/redis:latest", image.Target())
```
### OCI Runtime Specification
containerd fully supports the OCI runtime specification for running containers. We have built in functions to help you generate runtime specifications based on images as well as custom parameters.
```go
spec, err := containerd.GenerateSpec(containerd.WithImageConfig(context, image))
```
### Containers
In containerd, a container is a metadata object. Resources such as an OCI runtime specification, image, root filesystem, and other metadata can be attached to a container.
```go
redis, err := client.NewContainer(context, "redis-master",
containerd.WithSpec(spec),
)
redis, err := client.NewContainer(context, "redis-master")
defer redis.Delete(context)
```
### OCI Runtime Specification
containerd fully supports the OCI runtime specification for running containers. We have built in functions to help you generate runtime specifications based on images as well as custom parameters.
You can specify options when creating a container about how to modify the specification.
```go
redis, err := client.NewContainer(context, "redis-master", containerd.WithNewSpec(containerd.WithImageConfig(image)))
```
## Root Filesystems
containerd allows you to use overlay or snapshot filesystems with your containers. It comes with builtin support for overlayfs and btrfs.
@@ -89,16 +89,17 @@ image, err := client.Pull(context, "docker.io/library/redis:latest", containerd.
// allocate a new RW root filesystem for a container based on the image
redis, err := client.NewContainer(context, "redis-master",
containerd.WithSpec(spec),
containerd.WithNewSnapshot("redis-rootfs", image),
containerd.WithNewSpec(containerd.WithImageConfig(image)),
)
// use a readonly filesystem with multiple containers
for i := 0; i < 10; i++ {
id := fmt.Sprintf("id-%s", i)
container, err := client.NewContainer(ctx, id,
containerd.WithSpec(spec),
containerd.WithNewSnapshotView(id, image),
containerd.WithNewSpec(containerd.WithImageConfig(image)),
)
}
```

View File

@@ -2,11 +2,16 @@
package containerd
import specs "github.com/opencontainers/runtime-spec/specs-go"
import (
"context"
"github.com/containerd/containerd/containers"
specs "github.com/opencontainers/runtime-spec/specs-go"
)
// WithApparmor sets the provided apparmor profile to the spec
func WithApparmorProfile(profile string) SpecOpts {
return func(s *specs.Spec) error {
return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
s.Process.ApparmorProfile = profile
return nil
}

View File

@@ -1,16 +1,21 @@
package containerd
import specs "github.com/opencontainers/runtime-spec/specs-go"
import (
"context"
"github.com/containerd/containerd/containers"
specs "github.com/opencontainers/runtime-spec/specs-go"
)
// GenerateSpec will generate a default spec from the provided image
// for use as a containerd container
func GenerateSpec(opts ...SpecOpts) (*specs.Spec, error) {
func GenerateSpec(ctx context.Context, client *Client, c *containers.Container, opts ...SpecOpts) (*specs.Spec, error) {
s, err := createDefaultSpec()
if err != nil {
return nil, err
}
for _, o := range opts {
if err := o(s); err != nil {
if err := o(ctx, client, c, s); err != nil {
return nil, err
}
}

View File

@@ -1,13 +1,19 @@
package containerd
import specs "github.com/opencontainers/runtime-spec/specs-go"
import (
"context"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/typeurl"
specs "github.com/opencontainers/runtime-spec/specs-go"
)
// SpecOpts sets spec specific information to a newly generated OCI spec
type SpecOpts func(s *specs.Spec) error
type SpecOpts func(context.Context, *Client, *containers.Container, *specs.Spec) error
// WithProcessArgs replaces the args on the generated spec
func WithProcessArgs(args ...string) SpecOpts {
return func(s *specs.Spec) error {
return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
s.Process.Args = args
return nil
}
@@ -15,8 +21,46 @@ func WithProcessArgs(args ...string) SpecOpts {
// WithHostname sets the container's hostname
func WithHostname(name string) SpecOpts {
return func(s *specs.Spec) error {
return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
s.Hostname = name
return nil
}
}
// WithNewSpec generates a new spec for a new container
func WithNewSpec(opts ...SpecOpts) NewContainerOpts {
return func(ctx context.Context, client *Client, c *containers.Container) error {
s, err := createDefaultSpec()
if err != nil {
return err
}
for _, o := range opts {
if err := o(ctx, client, c, s); err != nil {
return err
}
}
any, err := typeurl.MarshalAny(s)
if err != nil {
return err
}
c.Spec = any
return nil
}
}
// WithSpec sets the provided spec on the container
func WithSpec(s *specs.Spec, opts ...SpecOpts) NewContainerOpts {
return func(ctx context.Context, client *Client, c *containers.Container) error {
for _, o := range opts {
if err := o(ctx, client, c, s); err != nil {
return err
}
}
any, err := typeurl.MarshalAny(s)
if err != nil {
return err
}
c.Spec = any
return nil
}
}

View File

@@ -6,23 +6,28 @@ import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"golang.org/x/sys/unix"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/typeurl"
"github.com/opencontainers/image-spec/identity"
"github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/runc/libcontainer/user"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
)
// WithTTY sets the information on the spec as well as the environment variables for
// using a TTY
func WithTTY(s *specs.Spec) error {
func WithTTY(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
s.Process.Terminal = true
s.Process.Env = append(s.Process.Env, "TERM=xterm")
return nil
@@ -30,7 +35,7 @@ func WithTTY(s *specs.Spec) error {
// WithHostNamespace allows a task to run inside the host's linux namespace
func WithHostNamespace(ns specs.LinuxNamespaceType) SpecOpts {
return func(s *specs.Spec) error {
return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
for i, n := range s.Linux.Namespaces {
if n.Type == ns {
s.Linux.Namespaces = append(s.Linux.Namespaces[:i], s.Linux.Namespaces[i+1:]...)
@@ -44,7 +49,7 @@ func WithHostNamespace(ns specs.LinuxNamespaceType) SpecOpts {
// WithLinuxNamespace uses the passed in namespace for the spec. If a namespace of the same type already exists in the
// spec, the existing namespace is replaced by the one provided.
func WithLinuxNamespace(ns specs.LinuxNamespace) SpecOpts {
return func(s *specs.Spec) error {
return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
for i, n := range s.Linux.Namespaces {
if n.Type == ns.Type {
before := s.Linux.Namespaces[:i]
@@ -60,11 +65,11 @@ func WithLinuxNamespace(ns specs.LinuxNamespace) SpecOpts {
}
// WithImageConfig configures the spec to from the configuration of an Image
func WithImageConfig(ctx context.Context, i Image) SpecOpts {
return func(s *specs.Spec) error {
func WithImageConfig(i Image) SpecOpts {
return func(ctx context.Context, client *Client, c *containers.Container, s *specs.Spec) error {
var (
image = i.(*image)
store = image.client.ContentStore()
store = client.ContentStore()
)
ic, err := image.i.Config(ctx, store)
if err != nil {
@@ -100,6 +105,10 @@ func WithImageConfig(ctx context.Context, i Image) SpecOpts {
case 1:
v, err := strconv.ParseUint(parts[0], 0, 10)
if err != nil {
// if we cannot parse as a uint they try to see if it is a username
if err := WithUsername(config.User)(ctx, client, c, s); err != nil {
return err
}
return err
}
uid, gid = uint32(v), uint32(v)
@@ -129,7 +138,7 @@ func WithImageConfig(ctx context.Context, i Image) SpecOpts {
// WithRootFSPath specifies unmanaged rootfs path.
func WithRootFSPath(path string, readonly bool) SpecOpts {
return func(s *specs.Spec) error {
return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
s.Root = &specs.Root{
Path: path,
Readonly: readonly,
@@ -139,18 +148,6 @@ func WithRootFSPath(path string, readonly bool) SpecOpts {
}
}
// WithSpec sets the provided spec for a new container
func WithSpec(spec *specs.Spec) NewContainerOpts {
return func(ctx context.Context, client *Client, c *containers.Container) error {
any, err := typeurl.MarshalAny(spec)
if err != nil {
return err
}
c.Spec = any
return nil
}
}
// WithResources sets the provided resources on the spec for task updates
func WithResources(resources *specs.LinuxResources) UpdateTaskOpts {
return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error {
@@ -160,13 +157,13 @@ func WithResources(resources *specs.LinuxResources) UpdateTaskOpts {
}
// WithNoNewPrivileges sets no_new_privileges on the process for the container
func WithNoNewPrivileges(s *specs.Spec) error {
func WithNoNewPrivileges(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
s.Process.NoNewPrivileges = true
return nil
}
// WithHostHostsFile bind-mounts the host's /etc/hosts into the container as readonly
func WithHostHostsFile(s *specs.Spec) error {
func WithHostHostsFile(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
s.Mounts = append(s.Mounts, specs.Mount{
Destination: "/etc/hosts",
Type: "bind",
@@ -177,7 +174,7 @@ func WithHostHostsFile(s *specs.Spec) error {
}
// WithHostResolvconf bind-mounts the host's /etc/resolv.conf into the container as readonly
func WithHostResolvconf(s *specs.Spec) error {
func WithHostResolvconf(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
s.Mounts = append(s.Mounts, specs.Mount{
Destination: "/etc/resolv.conf",
Type: "bind",
@@ -188,7 +185,7 @@ func WithHostResolvconf(s *specs.Spec) error {
}
// WithHostLocaltime bind-mounts the host's /etc/localtime into the container as readonly
func WithHostLocaltime(s *specs.Spec) error {
func WithHostLocaltime(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
s.Mounts = append(s.Mounts, specs.Mount{
Destination: "/etc/localtime",
Type: "bind",
@@ -201,7 +198,7 @@ func WithHostLocaltime(s *specs.Spec) error {
// WithUserNamespace sets the uid and gid mappings for the task
// this can be called multiple times to add more mappings to the generated spec
func WithUserNamespace(container, host, size uint32) SpecOpts {
return func(s *specs.Spec) error {
return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
var hasUserns bool
for _, ns := range s.Linux.Namespaces {
if ns.Type == specs.UserNamespace {
@@ -271,7 +268,7 @@ func WithRemappedSnapshot(id string, i Image, uid, gid uint32) NewContainerOpts
// WithCgroup sets the container's cgroup path
func WithCgroup(path string) SpecOpts {
return func(s *specs.Spec) error {
return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
s.Linux.CgroupsPath = path
return nil
}
@@ -279,13 +276,69 @@ func WithCgroup(path string) SpecOpts {
// WithNamespacedCgroup uses the namespace set on the context to create a
// root directory for containers in the cgroup with the id as the subcgroup
func WithNamespacedCgroup(ctx context.Context, id string) SpecOpts {
return func(s *specs.Spec) error {
func WithNamespacedCgroup() SpecOpts {
return func(ctx context.Context, _ *Client, c *containers.Container, s *specs.Spec) error {
namespace, err := namespaces.NamespaceRequired(ctx)
if err != nil {
return err
}
s.Linux.CgroupsPath = filepath.Join("/", namespace, id)
s.Linux.CgroupsPath = filepath.Join("/", namespace, c.ID)
return nil
}
}
// WithUserIDs allows the UID and GID for the Process to be set
func WithUserIDs(uid, gid uint32) SpecOpts {
return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
s.Process.User.UID = uid
s.Process.User.GID = gid
return nil
}
}
// WithUsername sets the correct UID and GID for the container
// based on the the image's /etc/passwd contents.
// id is the snapshot id that is used
func WithUsername(username string) SpecOpts {
return func(ctx context.Context, client *Client, c *containers.Container, s *specs.Spec) error {
if c.Snapshotter == "" {
return errors.Errorf("no snapshotter set for container")
}
if c.RootFS == "" {
return errors.Errorf("rootfs not created for container")
}
snapshotter := client.SnapshotService(c.Snapshotter)
mounts, err := snapshotter.Mounts(ctx, c.RootFS)
if err != nil {
return err
}
root, err := ioutil.TempDir("", "ctd-username")
if err != nil {
return err
}
defer os.RemoveAll(root)
for _, m := range mounts {
if err := m.Mount(root); err != nil {
return err
}
}
defer unix.Unmount(root, 0)
f, err := os.Open(filepath.Join(root, "/etc/passwd"))
if err != nil {
return err
}
defer f.Close()
users, err := user.ParsePasswdFilter(f, func(u user.User) bool {
return u.Name == username
})
if err != nil {
return err
}
if len(users) == 0 {
return errors.Errorf("no users found for %s", username)
}
u := users[0]
s.Process.User.UID, s.Process.User.GID = uint32(u.Uid), uint32(u.Gid)
return nil
}
}

View File

@@ -10,16 +10,15 @@ import (
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/typeurl"
"github.com/opencontainers/image-spec/specs-go/v1"
specs "github.com/opencontainers/runtime-spec/specs-go"
)
func WithImageConfig(ctx context.Context, i Image) SpecOpts {
return func(s *specs.Spec) error {
func WithImageConfig(i Image) SpecOpts {
return func(ctx context.Context, client *Client, _ *containers.Container, s *specs.Spec) error {
var (
image = i.(*image)
store = image.client.ContentStore()
store = client.ContentStore()
)
ic, err := image.i.Config(ctx, store)
if err != nil {
@@ -52,7 +51,7 @@ func WithImageConfig(ctx context.Context, i Image) SpecOpts {
}
func WithTTY(width, height int) SpecOpts {
return func(s *specs.Spec) error {
return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
s.Process.Terminal = true
s.Process.ConsoleSize.Width = uint(width)
s.Process.ConsoleSize.Height = uint(height)
@@ -60,17 +59,6 @@ func WithTTY(width, height int) SpecOpts {
}
}
func WithSpec(spec *specs.Spec) NewContainerOpts {
return func(ctx context.Context, client *Client, c *containers.Container) error {
any, err := typeurl.MarshalAny(spec)
if err != nil {
return err
}
c.Spec = any
return nil
}
}
func WithResources(resources *specs.WindowsResources) UpdateTaskOpts {
return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error {
r.Resources = resources