Add context and client to SpecOpts

In order to do more advanced spec generation with images, snapshots,
etc, we need to inject the context and client into the spec generation
code.

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby 2017-08-22 12:03:21 -04:00
parent ba69f5d488
commit fa14f2ef3a
15 changed files with 100 additions and 92 deletions

View File

@ -2,11 +2,15 @@
package containerd
import specs "github.com/opencontainers/runtime-spec/specs-go"
import (
"context"
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, s *specs.Spec) error {
s.Process.ApparmorProfile = profile
return nil
}

View File

@ -20,7 +20,7 @@ func BenchmarkContainerCreate(b *testing.B) {
b.Error(err)
return
}
spec, err := GenerateSpec(WithImageConfig(ctx, image), withTrue())
spec, err := GenerateSpec(ctx, client, WithImageConfig(image), withTrue())
if err != nil {
b.Error(err)
return
@ -63,7 +63,7 @@ func BenchmarkContainerStart(b *testing.B) {
b.Error(err)
return
}
spec, err := GenerateSpec(WithImageConfig(ctx, image), withTrue())
spec, err := GenerateSpec(ctx, client, WithImageConfig(image), withTrue())
if err != nil {
b.Error(err)
return

View File

@ -98,7 +98,7 @@ func test(c config) error {
return err
}
logrus.Info("generating spec from image")
spec, err := containerd.GenerateSpec(containerd.WithImageConfig(ctx, image), containerd.WithProcessArgs("true"))
spec, err := containerd.GenerateSpec(ctx, client, containerd.WithImageConfig(image), containerd.WithProcessArgs("true"))
if err != nil {
return err
}

View File

@ -24,7 +24,7 @@ type killer interface {
}
func withEnv(context *cli.Context) containerd.SpecOpts {
return func(s *specs.Spec) error {
return func(_ gocontext.Context, _ *containerd.Client, s *specs.Spec) error {
env := context.StringSlice("env")
if len(env) > 0 {
s.Process.Env = replaceOrAppendEnvValues(s.Process.Env, env)
@ -34,7 +34,7 @@ func withEnv(context *cli.Context) containerd.SpecOpts {
}
func withMounts(context *cli.Context) containerd.SpecOpts {
return func(s *specs.Spec) error {
return func(_ gocontext.Context, _ *containerd.Client, s *specs.Spec) error {
for _, mount := range context.StringSlice("mount") {
m, err := parseMountFlag(mount)
if err != nil {

View File

@ -90,7 +90,7 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli
if err != nil {
return nil, err
}
opts = append(opts, containerd.WithImageConfig(ctx, image))
opts = append(opts, containerd.WithImageConfig(image))
cOpts = append(cOpts, containerd.WithImage(image))
cOpts = append(cOpts, containerd.WithSnapshotter(context.String("snapshotter")))
if context.Bool("readonly") {
@ -111,7 +111,7 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli
if context.Bool("net-host") {
opts = append(opts, setHostNetworking())
}
spec, err := containerd.GenerateSpec(opts...)
spec, err := containerd.GenerateSpec(ctx, client, opts...)
if err != nil {
return nil, err
}

View File

@ -28,7 +28,7 @@ func TestCheckpointRestore(t *testing.T) {
t.Error(err)
return
}
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sleep", "100"))
spec, err := GenerateSpec(ctx, client, WithImageConfig(image), WithProcessArgs("sleep", "100"))
if err != nil {
t.Error(err)
return
@ -113,7 +113,7 @@ func TestCheckpointRestoreNewContainer(t *testing.T) {
t.Error(err)
return
}
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sleep", "100"))
spec, err := GenerateSpec(ctx, client, WithImageConfig(image), WithProcessArgs("sleep", "100"))
if err != nil {
t.Error(err)
return
@ -211,7 +211,7 @@ func TestCheckpointLeaveRunning(t *testing.T) {
t.Error(err)
return
}
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sleep", "100"))
spec, err := GenerateSpec(ctx, client, WithImageConfig(image), WithProcessArgs("sleep", "100"))
if err != nil {
t.Error(err)
return

View File

@ -39,7 +39,7 @@ func TestContainerUpdate(t *testing.T) {
t.Error(err)
return
}
spec, err := generateSpec(WithImageConfig(ctx, image), withProcessArgs("sleep", "30"))
spec, err := generateSpec(ctx, client, WithImageConfig(image), withProcessArgs("sleep", "30"))
if err != nil {
t.Error(err)
return
@ -127,7 +127,7 @@ func TestShimInCgroup(t *testing.T) {
t.Error(err)
return
}
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sleep", "30"))
spec, err := GenerateSpec(ctx, client, WithImageConfig(image), WithProcessArgs("sleep", "30"))
if err != nil {
t.Error(err)
return
@ -202,7 +202,7 @@ func TestDaemonRestart(t *testing.T) {
return
}
spec, err := generateSpec(withImageConfig(ctx, image), withProcessArgs("sleep", "30"))
spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "30"))
if err != nil {
t.Error(err)
return

View File

@ -55,15 +55,15 @@ func TestNewContainer(t *testing.T) {
}
defer client.Close()
spec, err := generateSpec()
ctx, cancel := testContext()
defer cancel()
spec, err := generateSpec(ctx, client)
if err != nil {
t.Error(err)
return
}
ctx, cancel := testContext()
defer cancel()
container, err := client.NewContainer(ctx, id, WithSpec(spec))
if err != nil {
t.Error(err)
@ -107,7 +107,7 @@ func TestContainerStart(t *testing.T) {
}
}
spec, err := generateSpec(withImageConfig(ctx, image), withExitStatus(7))
spec, err := generateSpec(ctx, client, withImageConfig(image), withExitStatus(7))
if err != nil {
t.Error(err)
return
@ -185,7 +185,7 @@ func TestContainerOutput(t *testing.T) {
}
}
spec, err := generateSpec(withImageConfig(ctx, image), withProcessArgs("echo", expected))
spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("echo", expected))
if err != nil {
t.Error(err)
return
@ -258,7 +258,7 @@ func TestContainerExec(t *testing.T) {
}
}
spec, err := generateSpec(withImageConfig(ctx, image), withProcessArgs("sleep", "100"))
spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "100"))
if err != nil {
t.Error(err)
return
@ -357,7 +357,7 @@ func TestContainerPids(t *testing.T) {
}
}
spec, err := generateSpec(withImageConfig(ctx, image), withProcessArgs("sleep", "100"))
spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "100"))
if err != nil {
t.Error(err)
return
@ -435,7 +435,7 @@ func TestContainerCloseIO(t *testing.T) {
}
}
spec, err := generateSpec(withImageConfig(ctx, image), withCat())
spec, err := generateSpec(ctx, client, withImageConfig(image), withCat())
if err != nil {
t.Error(err)
return
@ -505,7 +505,7 @@ func TestDeleteRunningContainer(t *testing.T) {
}
}
spec, err := generateSpec(withImageConfig(ctx, image), withProcessArgs("sleep", "100"))
spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "100"))
if err != nil {
t.Error(err)
return
@ -573,7 +573,7 @@ func TestContainerKill(t *testing.T) {
}
}
spec, err := generateSpec(withImageConfig(ctx, image), withProcessArgs("sleep", "10"))
spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "10"))
if err != nil {
t.Error(err)
return
@ -642,7 +642,7 @@ func TestContainerNoBinaryExists(t *testing.T) {
}
}
spec, err := generateSpec(withImageConfig(ctx, image), WithProcessArgs("nothing"))
spec, err := generateSpec(ctx, client, withImageConfig(image), WithProcessArgs("nothing"))
if err != nil {
t.Error(err)
return
@ -696,7 +696,7 @@ func TestContainerExecNoBinaryExists(t *testing.T) {
}
}
spec, err := generateSpec(withImageConfig(ctx, image), withProcessArgs("sleep", "100"))
spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "100"))
if err != nil {
t.Error(err)
return
@ -769,8 +769,8 @@ func TestUserNamespaces(t *testing.T) {
}
}
spec, err := generateSpec(
withImageConfig(ctx, image),
spec, err := generateSpec(ctx, client,
withImageConfig(image),
withExitStatus(7),
withUserNamespace(0, 1000, 10000),
)
@ -852,7 +852,7 @@ func TestWaitStoppedTask(t *testing.T) {
}
}
spec, err := generateSpec(withImageConfig(ctx, image), withExitStatus(7))
spec, err := generateSpec(ctx, client, withImageConfig(image), withExitStatus(7))
if err != nil {
t.Error(err)
return
@ -928,7 +928,7 @@ func TestWaitStoppedProcess(t *testing.T) {
}
}
spec, err := generateSpec(withImageConfig(ctx, image), withProcessArgs("sleep", "100"))
spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "100"))
if err != nil {
t.Error(err)
return
@ -1027,7 +1027,7 @@ func TestTaskForceDelete(t *testing.T) {
return
}
}
spec, err := generateSpec(withImageConfig(ctx, image), withProcessArgs("sleep", "30"))
spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "30"))
if err != nil {
t.Error(err)
return
@ -1080,7 +1080,7 @@ func TestProcessForceDelete(t *testing.T) {
return
}
}
spec, err := generateSpec(withImageConfig(ctx, image), withProcessArgs("sleep", "30"))
spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "30"))
if err != nil {
t.Error(err)
return
@ -1160,8 +1160,8 @@ func TestContainerHostname(t *testing.T) {
}
}
spec, err := generateSpec(
withImageConfig(ctx, image),
spec, err := generateSpec(ctx, client,
withImageConfig(image),
withProcessArgs("hostname"),
WithHostname(expected),
)
@ -1243,7 +1243,7 @@ func TestContainerExitedAtSet(t *testing.T) {
}
}
spec, err := generateSpec(withImageConfig(ctx, image), withTrue())
spec, err := generateSpec(ctx, client, withImageConfig(image), withTrue())
if err != nil {
t.Error(err)
return
@ -1315,7 +1315,7 @@ func TestDeleteContainerExecCreated(t *testing.T) {
}
}
spec, err := generateSpec(withImageConfig(ctx, image), withProcessArgs("sleep", "100"))
spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "100"))
if err != nil {
t.Error(err)
return

View File

@ -11,12 +11,12 @@ import (
const newLine = "\n"
func generateSpec(opts ...SpecOpts) (*specs.Spec, error) {
return GenerateSpec(opts...)
func generateSpec(ctx context.Context, client *Client, opts ...SpecOpts) (*specs.Spec, error) {
return GenerateSpec(ctx, client, opts...)
}
func withExitStatus(es int) SpecOpts {
return func(s *specs.Spec) error {
return func(_ context.Context, _ *Client, s *specs.Spec) error {
s.Process.Args = []string{"sh", "-c", fmt.Sprintf("exit %d", es)}
return nil
}
@ -42,14 +42,9 @@ func withExecArgs(s *specs.Process, args ...string) {
s.Args = args
}
func withImageConfig(ctx context.Context, i Image) SpecOpts {
return WithImageConfig(ctx, i)
}
func withNewSnapshot(id string, i Image) NewContainerOpts {
return WithNewSnapshot(id, i)
}
var withUserNamespace = WithUserNamespace
var withRemappedSnapshot = WithRemappedSnapshot
var (
withUserNamespace = WithUserNamespace
withRemappedSnapshot = WithRemappedSnapshot
withNewSnapshot = WithNewSnapshot
withImageConfig = WithImageConfig
)

View File

@ -12,8 +12,8 @@ import (
const newLine = "\r\n"
func generateSpec(opts ...SpecOpts) (*specs.Spec, error) {
spec, err := GenerateSpec(opts...)
func generateSpec(ctx context.Context, client *Client, opts ...SpecOpts) (*specs.Spec, error) {
spec, err := GenerateSpec(ctx, client, opts...)
if err != nil {
return nil, err
}
@ -24,7 +24,7 @@ func generateSpec(opts ...SpecOpts) (*specs.Spec, error) {
}
func withExitStatus(es int) SpecOpts {
return func(s *specs.Spec) error {
return func(_ context.Context, _ *Client, s *specs.Spec) error {
s.Process.Args = []string{"powershell", "-noprofile", "exit", strconv.Itoa(es)}
return nil
}
@ -50,11 +50,9 @@ func withExecArgs(s *specs.Process, args ...string) {
s.Args = append([]string{"powershell", "-noprofile"}, args...)
}
func withImageConfig(ctx context.Context, i Image) SpecOpts {
func withImageConfig(i Image) SpecOpts {
// TODO: when windows has a snapshotter remove the withImageConfig helper
return func(s *specs.Spec) error {
return nil
}
return withNoop
}
func withNewSnapshot(id string, i Image) NewContainerOpts {
@ -65,9 +63,7 @@ func withNewSnapshot(id string, i Image) NewContainerOpts {
}
func withUserNamespace(u, g, s uint32) SpecOpts {
return func(s *specs.Spec) error {
return nil
}
return withNoop
}
func withRemappedSnapshot(id string, i Image, u, g uint32) NewContainerOpts {
@ -75,3 +71,7 @@ func withRemappedSnapshot(id string, i Image, u, g uint32) NewContainerOpts {
return nil
}
}
func withNoop(_ context.Context, _ *Client, _ *specs.Spec) error {
return nil
}

10
spec.go
View File

@ -1,16 +1,20 @@
package containerd
import specs "github.com/opencontainers/runtime-spec/specs-go"
import (
"context"
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, 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, s); err != nil {
return nil, err
}
}

View File

@ -1,13 +1,17 @@
package containerd
import specs "github.com/opencontainers/runtime-spec/specs-go"
import (
"context"
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, *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, s *specs.Spec) error {
s.Process.Args = args
return nil
}
@ -15,7 +19,7 @@ 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, s *specs.Spec) error {
s.Hostname = name
return nil
}

View File

@ -22,7 +22,7 @@ import (
// 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, s *specs.Spec) error {
s.Process.Terminal = true
s.Process.Env = append(s.Process.Env, "TERM=xterm")
return nil
@ -30,7 +30,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, 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 +44,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, s *specs.Spec) error {
for i, n := range s.Linux.Namespaces {
if n.Type == ns.Type {
before := s.Linux.Namespaces[:i]
@ -60,11 +60,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, 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 {
@ -129,7 +129,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, s *specs.Spec) error {
s.Root = &specs.Root{
Path: path,
Readonly: readonly,
@ -160,13 +160,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, 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, s *specs.Spec) error {
s.Mounts = append(s.Mounts, specs.Mount{
Destination: "/etc/hosts",
Type: "bind",
@ -177,7 +177,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, s *specs.Spec) error {
s.Mounts = append(s.Mounts, specs.Mount{
Destination: "/etc/resolv.conf",
Type: "bind",
@ -188,7 +188,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, s *specs.Spec) error {
s.Mounts = append(s.Mounts, specs.Mount{
Destination: "/etc/localtime",
Type: "bind",
@ -201,7 +201,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, s *specs.Spec) error {
var hasUserns bool
for _, ns := range s.Linux.Namespaces {
if ns.Type == specs.UserNamespace {
@ -271,7 +271,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, s *specs.Spec) error {
s.Linux.CgroupsPath = path
return nil
}
@ -279,8 +279,8 @@ 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(id string) SpecOpts {
return func(ctx context.Context, _ *Client, s *specs.Spec) error {
namespace, err := namespaces.NamespaceRequired(ctx)
if err != nil {
return err
@ -292,7 +292,7 @@ func WithNamespacedCgroup(ctx context.Context, id string) SpecOpts {
// WithUserIDs allows the UID and GID for the Process to be set
func WithUserIDs(uid, gid uint32) SpecOpts {
return func(s *specs.Spec) error {
return func(_ context.Context, _ *Client, s *specs.Spec) error {
s.Process.User.UID = uid
s.Process.User.GID = gid
return nil

View File

@ -15,11 +15,11 @@ import (
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, 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 +52,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, s *specs.Spec) error {
s.Process.Terminal = true
s.Process.ConsoleSize.Width = uint(width)
s.Process.ConsoleSize.Height = uint(height)

View File

@ -3,6 +3,7 @@
package containerd
import (
"context"
"testing"
specs "github.com/opencontainers/runtime-spec/specs-go"
@ -11,7 +12,7 @@ import (
func TestGenerateSpec(t *testing.T) {
t.Parallel()
s, err := GenerateSpec()
s, err := GenerateSpec(context.Background(), nil)
if err != nil {
t.Fatal(err)
}
@ -51,7 +52,7 @@ func TestGenerateSpec(t *testing.T) {
func TestSpecWithTTY(t *testing.T) {
t.Parallel()
s, err := GenerateSpec(WithTTY)
s, err := GenerateSpec(context.Background(), nil, WithTTY)
if err != nil {
t.Fatal(err)
}
@ -68,7 +69,7 @@ func TestWithLinuxNamespace(t *testing.T) {
t.Parallel()
replacedNS := specs.LinuxNamespace{Type: specs.NetworkNamespace, Path: "/var/run/netns/test"}
s, err := GenerateSpec(WithLinuxNamespace(replacedNS))
s, err := GenerateSpec(context.Background(), nil, WithLinuxNamespace(replacedNS))
if err != nil {
t.Fatal(err)
}