diff --git a/README.md b/README.md index 6b9692dce..145e2e5dd 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ In containerd, a container is a metadata object. Resources such as an OCI runti ```go redis, err := client.NewContainer(context, "redis-master", - containerd.WithSpec(spec), + containerd.WithNewSpec(spec), ) defer redis.Delete(context) ``` @@ -89,7 +89,7 @@ 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.WithNewSpec(spec), containerd.WithNewSnapshot("redis-rootfs", image), ) @@ -97,7 +97,7 @@ redis, err := client.NewContainer(context, "redis-master", for i := 0; i < 10; i++ { id := fmt.Sprintf("id-%s", i) container, err := client.NewContainer(ctx, id, - containerd.WithSpec(spec), + containerd.WithNewSpec(spec), containerd.WithNewSnapshotView(id, image), ) } diff --git a/apparmor.go b/apparmor.go index 49f21484d..e8485c3f5 100644 --- a/apparmor.go +++ b/apparmor.go @@ -5,12 +5,13 @@ package containerd 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(_ context.Context, _ *Client, s *specs.Spec) error { + return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error { s.Process.ApparmorProfile = profile return nil } diff --git a/benchmark_test.go b/benchmark_test.go deleted file mode 100644 index 4d98c6088..000000000 --- a/benchmark_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package containerd - -import ( - "fmt" - "testing" -) - -func BenchmarkContainerCreate(b *testing.B) { - client, err := newClient(b, address) - if err != nil { - b.Fatal(err) - } - defer client.Close() - - ctx, cancel := testContext() - defer cancel() - - image, err := client.GetImage(ctx, testImage) - if err != nil { - b.Error(err) - return - } - spec, err := GenerateSpec(ctx, client, WithImageConfig(image), withTrue()) - if err != nil { - b.Error(err) - return - } - var containers []Container - defer func() { - for _, c := range containers { - if err := c.Delete(ctx, WithSnapshotCleanup); err != nil { - b.Error(err) - } - } - }() - - // reset the timer before creating containers - b.ResetTimer() - for i := 0; i < b.N; i++ { - id := fmt.Sprintf("%s-%d", b.Name(), i) - container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewSnapshot(id, image)) - if err != nil { - b.Error(err) - return - } - containers = append(containers, container) - } - b.StopTimer() -} - -func BenchmarkContainerStart(b *testing.B) { - client, err := newClient(b, address) - if err != nil { - b.Fatal(err) - } - defer client.Close() - - ctx, cancel := testContext() - defer cancel() - - image, err := client.GetImage(ctx, testImage) - if err != nil { - b.Error(err) - return - } - spec, err := GenerateSpec(ctx, client, WithImageConfig(image), withTrue()) - if err != nil { - b.Error(err) - return - } - var containers []Container - defer func() { - for _, c := range containers { - if err := c.Delete(ctx, WithSnapshotCleanup); err != nil { - b.Error(err) - } - } - }() - - for i := 0; i < b.N; i++ { - id := fmt.Sprintf("%s-%d", b.Name(), i) - container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewSnapshot(id, image)) - if err != nil { - b.Error(err) - return - } - containers = append(containers, container) - - } - // reset the timer before starting tasks - b.ResetTimer() - for _, c := range containers { - task, err := c.NewTask(ctx, empty()) - if err != nil { - b.Error(err) - return - } - defer task.Delete(ctx) - if err := task.Start(ctx); err != nil { - b.Error(err) - return - } - } - b.StopTimer() -} diff --git a/cmd/containerd-stress/main.go b/cmd/containerd-stress/main.go index 7a9ff18f0..a62c03285 100644 --- a/cmd/containerd-stress/main.go +++ b/cmd/containerd-stress/main.go @@ -98,7 +98,7 @@ func test(c config) error { return err } logrus.Info("generating spec from image") - spec, err := containerd.GenerateSpec(ctx, client, containerd.WithImageConfig(image), containerd.WithProcessArgs("true")) + spec, err := containerd.GenerateSpec(ctx, client, nil, containerd.WithImageConfig(image), containerd.WithProcessArgs("true")) if err != nil { return err } diff --git a/cmd/ctr/run.go b/cmd/ctr/run.go index 8a9f91c63..880000876 100644 --- a/cmd/ctr/run.go +++ b/cmd/ctr/run.go @@ -8,6 +8,7 @@ import ( "github.com/containerd/console" "github.com/containerd/containerd" + "github.com/containerd/containerd/containers" digest "github.com/opencontainers/go-digest" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -24,7 +25,7 @@ type killer interface { } func withEnv(context *cli.Context) containerd.SpecOpts { - return func(_ gocontext.Context, _ *containerd.Client, s *specs.Spec) error { + return func(_ gocontext.Context, _ *containerd.Client, _ *containers.Container, s *specs.Spec) error { env := context.StringSlice("env") if len(env) > 0 { s.Process.Env = replaceOrAppendEnvValues(s.Process.Env, env) @@ -34,7 +35,7 @@ func withEnv(context *cli.Context) containerd.SpecOpts { } func withMounts(context *cli.Context) containerd.SpecOpts { - return func(_ gocontext.Context, _ *containerd.Client, s *specs.Spec) error { + return func(_ gocontext.Context, _ *containerd.Client, _ *containers.Container, s *specs.Spec) error { for _, mount := range context.StringSlice("mount") { m, err := parseMountFlag(mount) if err != nil { diff --git a/cmd/ctr/run_unix.go b/cmd/ctr/run_unix.go index 46a3e8aa0..659400ada 100644 --- a/cmd/ctr/run_unix.go +++ b/cmd/ctr/run_unix.go @@ -111,11 +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(ctx, client, opts...) - if err != nil { - return nil, err - } - cOpts = append([]containerd.NewContainerOpts{containerd.WithSpec(spec)}, cOpts...) + cOpts = append([]containerd.NewContainerOpts{containerd.WithNewSpec(opts...)}, cOpts...) return client.NewContainer(ctx, id, cOpts...) } diff --git a/cmd/ctr/run_windows.go b/cmd/ctr/run_windows.go index c679fe03b..f48b50542 100644 --- a/cmd/ctr/run_windows.go +++ b/cmd/ctr/run_windows.go @@ -115,7 +115,7 @@ func newContainer(ctx gocontext.Context, client *containerd.Client, context *cli } return client.NewContainer(ctx, id, - containerd.WithSpec(spec), + containerd.WithNewSpec(spec), containerd.WithContainerLabels(labels), containerd.WithRuntime(context.String("runtime")), // TODO(mlaventure): containerd.WithImage(image), diff --git a/container_checkpoint_test.go b/container_checkpoint_test.go index 55d9c672f..d7636b2fb 100644 --- a/container_checkpoint_test.go +++ b/container_checkpoint_test.go @@ -28,12 +28,7 @@ func TestCheckpointRestore(t *testing.T) { t.Error(err) return } - spec, err := GenerateSpec(ctx, client, WithImageConfig(image), WithProcessArgs("sleep", "100")) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(WithImageConfig(image), WithProcessArgs("sleep", "100")), WithNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -113,12 +108,7 @@ func TestCheckpointRestoreNewContainer(t *testing.T) { t.Error(err) return } - spec, err := GenerateSpec(ctx, client, WithImageConfig(image), WithProcessArgs("sleep", "100")) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(WithImageConfig(image), WithProcessArgs("sleep", "100")), WithNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -211,12 +201,7 @@ func TestCheckpointLeaveRunning(t *testing.T) { t.Error(err) return } - spec, err := GenerateSpec(ctx, client, WithImageConfig(image), WithProcessArgs("sleep", "100")) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(WithImageConfig(image), WithProcessArgs("sleep", "100")), WithNewSnapshot(id, image)) if err != nil { t.Error(err) return diff --git a/container_linux_test.go b/container_linux_test.go index 4dc7f0899..6e53187ad 100644 --- a/container_linux_test.go +++ b/container_linux_test.go @@ -14,6 +14,7 @@ import ( "time" "github.com/containerd/cgroups" + "github.com/containerd/containerd/containers" "github.com/containerd/containerd/linux/runcopts" specs "github.com/opencontainers/runtime-spec/specs-go" "golang.org/x/sys/unix" @@ -39,16 +40,14 @@ func TestContainerUpdate(t *testing.T) { t.Error(err) return } - spec, err := generateSpec(ctx, client, WithImageConfig(image), withProcessArgs("sleep", "30")) - if err != nil { - t.Error(err) - return - } limit := int64(32 * 1024 * 1024) - spec.Linux.Resources.Memory = &specs.LinuxMemory{ - Limit: &limit, + memory := func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error { + s.Linux.Resources.Memory = &specs.LinuxMemory{ + Limit: &limit, + } + return nil } - container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(WithImageConfig(image), withProcessArgs("sleep", "30"), memory), WithNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -127,12 +126,7 @@ func TestShimInCgroup(t *testing.T) { t.Error(err) return } - spec, err := GenerateSpec(ctx, client, WithImageConfig(image), WithProcessArgs("sleep", "30")) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(WithImageConfig(image), WithProcessArgs("sleep", "30")), WithNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -202,12 +196,7 @@ func TestDaemonRestart(t *testing.T) { return } - spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "30")) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "30")), withNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -293,12 +282,7 @@ func TestContainerAttach(t *testing.T) { } } - spec, err := generateSpec(withImageConfig(ctx, image), withCat()) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withCat()), withNewSnapshot(id, image)) if err != nil { t.Error(err) return diff --git a/container_test.go b/container_test.go index 3f589af4d..3bd901956 100644 --- a/container_test.go +++ b/container_test.go @@ -58,13 +58,7 @@ func TestNewContainer(t *testing.T) { ctx, cancel := testContext() defer cancel() - spec, err := generateSpec(ctx, client) - if err != nil { - t.Error(err) - return - } - - container, err := client.NewContainer(ctx, id, WithSpec(spec)) + container, err := client.NewContainer(ctx, id, WithNewSpec()) if err != nil { t.Error(err) return @@ -106,13 +100,7 @@ func TestContainerStart(t *testing.T) { return } } - - spec, err := generateSpec(ctx, client, withImageConfig(image), withExitStatus(7)) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withExitStatus(7)), withNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -184,13 +172,7 @@ func TestContainerOutput(t *testing.T) { return } } - - spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("echo", expected)) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("echo", expected)), withNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -258,12 +240,7 @@ func TestContainerExec(t *testing.T) { } } - spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "100")) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "100")), withNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -287,6 +264,11 @@ func TestContainerExec(t *testing.T) { t.Error(err) return } + spec, err := container.Spec() + if err != nil { + t.Error(err) + return + } // start an exec process without running the original container process info processSpec := spec.Process @@ -357,12 +339,7 @@ func TestContainerPids(t *testing.T) { } } - spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "100")) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "100")), withNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -435,12 +412,7 @@ func TestContainerCloseIO(t *testing.T) { } } - spec, err := generateSpec(ctx, client, withImageConfig(image), withCat()) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withCat()), withNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -505,12 +477,7 @@ func TestDeleteRunningContainer(t *testing.T) { } } - spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "100")) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "100")), withNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -573,12 +540,7 @@ func TestContainerKill(t *testing.T) { } } - spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "10")) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "10")), withNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -642,12 +604,7 @@ func TestContainerNoBinaryExists(t *testing.T) { } } - spec, err := generateSpec(ctx, client, withImageConfig(image), WithProcessArgs("nothing")) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), WithProcessArgs("nothing")), withNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -696,12 +653,7 @@ func TestContainerExecNoBinaryExists(t *testing.T) { } } - spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "100")) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "100")), withNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -723,6 +675,11 @@ func TestContainerExecNoBinaryExists(t *testing.T) { t.Error(err) return } + spec, err := container.Spec() + if err != nil { + t.Error(err) + return + } // start an exec process without running the original container process processSpec := spec.Process @@ -769,17 +726,11 @@ func TestUserNamespaces(t *testing.T) { } } - spec, err := generateSpec(ctx, client, - withImageConfig(image), - withExitStatus(7), - withUserNamespace(0, 1000, 10000), - ) - if err != nil { - t.Error(err) - return - } container, err := client.NewContainer(ctx, id, - WithSpec(spec), + WithNewSpec(withImageConfig(image), + withExitStatus(7), + withUserNamespace(0, 1000, 10000), + ), withRemappedSnapshot(id, image, 1000, 1000), ) if err != nil { @@ -852,12 +803,7 @@ func TestWaitStoppedTask(t *testing.T) { } } - spec, err := generateSpec(ctx, client, withImageConfig(image), withExitStatus(7)) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withExitStatus(7)), withNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -928,12 +874,7 @@ func TestWaitStoppedProcess(t *testing.T) { } } - spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "100")) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "100")), withNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -956,6 +897,11 @@ func TestWaitStoppedProcess(t *testing.T) { t.Error(err) return } + spec, err := container.Spec() + if err != nil { + t.Error(err) + return + } // start an exec process without running the original container process info processSpec := spec.Process @@ -1027,12 +973,7 @@ func TestTaskForceDelete(t *testing.T) { return } } - spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "30")) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "30")), withNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -1080,12 +1021,7 @@ func TestProcessForceDelete(t *testing.T) { return } } - spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "30")) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "30")), withNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -1110,6 +1046,12 @@ func TestProcessForceDelete(t *testing.T) { t.Error(err) return } + spec, err := container.Spec() + if err != nil { + t.Error(err) + return + } + processSpec := spec.Process withExecArgs(processSpec, "sleep", "20") execID := t.Name() + "_exec" @@ -1160,16 +1102,11 @@ func TestContainerHostname(t *testing.T) { } } - spec, err := generateSpec(ctx, client, - withImageConfig(image), + container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("hostname"), WithHostname(expected), - ) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + ), + withNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -1243,12 +1180,7 @@ func TestContainerExitedAtSet(t *testing.T) { } } - spec, err := generateSpec(ctx, client, withImageConfig(image), withTrue()) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withTrue()), withNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -1315,12 +1247,7 @@ func TestDeleteContainerExecCreated(t *testing.T) { } } - spec, err := generateSpec(ctx, client, withImageConfig(image), withProcessArgs("sleep", "100")) - if err != nil { - t.Error(err) - return - } - container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + container, err := client.NewContainer(ctx, id, WithNewSpec(withImageConfig(image), withProcessArgs("sleep", "100")), withNewSnapshot(id, image)) if err != nil { t.Error(err) return @@ -1343,6 +1270,11 @@ func TestDeleteContainerExecCreated(t *testing.T) { t.Error(err) return } + spec, err := container.Spec() + if err != nil { + t.Error(err) + return + } // start an exec process without running the original container process info processSpec := spec.Process diff --git a/docs/client-opts.md b/docs/client-opts.md index cd6cff426..4810aa2b7 100644 --- a/docs/client-opts.md +++ b/docs/client-opts.md @@ -10,7 +10,7 @@ For many functions and methods within the client package you will generally see If we look at the `NewContainer` method on the client we can see that it has a required argument of `id` and then additional `NewContainerOpts`. -There are a few built in options that allow the container to be created with an existing spec, `WithSpec`, and snapshot opts for creating or using an existing snapshot. +There are a few built in options that allow the container to be created with an existing spec, `WithNewSpec`, and snapshot opts for creating or using an existing snapshot. ```go func (c *Client) NewContainer(ctx context.Context, id string, opts ...NewContainerOpts) (Container, error) { diff --git a/docs/getting-started.md b/docs/getting-started.md index ab96f76db..f50b13e2f 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -109,7 +109,7 @@ The container will be based off of the image, use the runtime information in the container, err := client.NewContainer( ctx, "redis-server", - containerd.WithSpec(spec), + containerd.WithNewSpec(spec), containerd.WithImage(image), containerd.WithNewSnapshot("redis-server-snapshot", image), ) @@ -254,7 +254,7 @@ func redisExample() error { container, err := client.NewContainer( ctx, "redis-server", - containerd.WithSpec(spec), + containerd.WithNewSpec(spec), containerd.WithImage(image), containerd.WithNewSnapshot("redis-server-snapshot", image), ) diff --git a/helpers_unix_test.go b/helpers_unix_test.go index bb50ad4da..7b2aa3385 100644 --- a/helpers_unix_test.go +++ b/helpers_unix_test.go @@ -6,17 +6,14 @@ import ( "context" "fmt" + "github.com/containerd/containerd/containers" specs "github.com/opencontainers/runtime-spec/specs-go" ) const newLine = "\n" -func generateSpec(ctx context.Context, client *Client, opts ...SpecOpts) (*specs.Spec, error) { - return GenerateSpec(ctx, client, opts...) -} - func withExitStatus(es int) SpecOpts { - return func(_ context.Context, _ *Client, s *specs.Spec) error { + return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error { s.Process.Args = []string{"sh", "-c", fmt.Sprintf("exit %d", es)} return nil } diff --git a/spec.go b/spec.go index d139069bc..fc1201467 100644 --- a/spec.go +++ b/spec.go @@ -3,18 +3,19 @@ package containerd 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(ctx context.Context, client *Client, 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(ctx, client, s); err != nil { + if err := o(ctx, client, c, s); err != nil { return nil, err } } diff --git a/spec_opts.go b/spec_opts.go index a1abc1b3e..608d122ae 100644 --- a/spec_opts.go +++ b/spec_opts.go @@ -3,15 +3,16 @@ package containerd import ( "context" + "github.com/containerd/containerd/containers" specs "github.com/opencontainers/runtime-spec/specs-go" ) // SpecOpts sets spec specific information to a newly generated OCI spec -type SpecOpts func(context.Context, *Client, *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(_ context.Context, _ *Client, s *specs.Spec) error { + return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error { s.Process.Args = args return nil } @@ -19,7 +20,7 @@ func WithProcessArgs(args ...string) SpecOpts { // WithHostname sets the container's hostname func WithHostname(name string) SpecOpts { - return func(_ context.Context, _ *Client, s *specs.Spec) error { + return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error { s.Hostname = name return nil } diff --git a/spec_opts_unix.go b/spec_opts_unix.go index 3c80736e9..86f3420c8 100644 --- a/spec_opts_unix.go +++ b/spec_opts_unix.go @@ -22,7 +22,7 @@ import ( // WithTTY sets the information on the spec as well as the environment variables for // using a TTY -func WithTTY(_ context.Context, _ *Client, 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 +30,7 @@ func WithTTY(_ context.Context, _ *Client, s *specs.Spec) error { // WithHostNamespace allows a task to run inside the host's linux namespace func WithHostNamespace(ns specs.LinuxNamespaceType) SpecOpts { - return func(_ context.Context, _ *Client, 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 +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(_ context.Context, _ *Client, 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] @@ -61,7 +61,7 @@ func WithLinuxNamespace(ns specs.LinuxNamespace) SpecOpts { // WithImageConfig configures the spec to from the configuration of an Image func WithImageConfig(i Image) SpecOpts { - return func(ctx context.Context, client *Client, s *specs.Spec) error { + return func(ctx context.Context, client *Client, c *containers.Container, s *specs.Spec) error { var ( image = i.(*image) store = client.ContentStore() @@ -129,7 +129,7 @@ func WithImageConfig(i Image) SpecOpts { // WithRootFSPath specifies unmanaged rootfs path. func WithRootFSPath(path string, readonly bool) SpecOpts { - return func(_ context.Context, _ *Client, s *specs.Spec) error { + return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error { s.Root = &specs.Root{ Path: path, Readonly: readonly, @@ -139,10 +139,31 @@ func WithRootFSPath(path string, readonly bool) SpecOpts { } } -// WithSpec sets the provided spec for a new container -func WithSpec(spec *specs.Spec) NewContainerOpts { +// 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 { - any, err := typeurl.MarshalAny(spec) + 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) NewContainerOpts { + return func(ctx context.Context, client *Client, c *containers.Container) error { + any, err := typeurl.MarshalAny(s) if err != nil { return err } @@ -160,13 +181,13 @@ func WithResources(resources *specs.LinuxResources) UpdateTaskOpts { } // WithNoNewPrivileges sets no_new_privileges on the process for the container -func WithNoNewPrivileges(_ context.Context, _ *Client, 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(_ context.Context, _ *Client, 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 +198,7 @@ func WithHostHostsFile(_ context.Context, _ *Client, s *specs.Spec) error { } // WithHostResolvconf bind-mounts the host's /etc/resolv.conf into the container as readonly -func WithHostResolvconf(_ context.Context, _ *Client, 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 +209,7 @@ func WithHostResolvconf(_ context.Context, _ *Client, s *specs.Spec) error { } // WithHostLocaltime bind-mounts the host's /etc/localtime into the container as readonly -func WithHostLocaltime(_ context.Context, _ *Client, 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 +222,7 @@ func WithHostLocaltime(_ context.Context, _ *Client, 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(_ context.Context, _ *Client, 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 +292,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(_ context.Context, _ *Client, s *specs.Spec) error { + return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error { s.Linux.CgroupsPath = path return nil } @@ -279,20 +300,20 @@ 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(id string) SpecOpts { - return func(ctx context.Context, _ *Client, 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, s *specs.Spec) error { + return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error { s.Process.User.UID = uid s.Process.User.GID = gid return nil diff --git a/spec_opts_windows.go b/spec_opts_windows.go index 498607766..8b7eff8e9 100644 --- a/spec_opts_windows.go +++ b/spec_opts_windows.go @@ -60,7 +60,7 @@ func WithTTY(width, height int) SpecOpts { } } -func WithSpec(spec *specs.Spec) NewContainerOpts { +func WithNewSpec(spec *specs.Spec) NewContainerOpts { return func(ctx context.Context, client *Client, c *containers.Container) error { any, err := typeurl.MarshalAny(spec) if err != nil { diff --git a/spec_unix_test.go b/spec_unix_test.go index fa3e5685d..41a6471f5 100644 --- a/spec_unix_test.go +++ b/spec_unix_test.go @@ -12,7 +12,7 @@ import ( func TestGenerateSpec(t *testing.T) { t.Parallel() - s, err := GenerateSpec(context.Background(), nil) + s, err := GenerateSpec(context.Background(), nil, nil) if err != nil { t.Fatal(err) } @@ -52,7 +52,7 @@ func TestGenerateSpec(t *testing.T) { func TestSpecWithTTY(t *testing.T) { t.Parallel() - s, err := GenerateSpec(context.Background(), nil, WithTTY) + s, err := GenerateSpec(context.Background(), nil, nil, WithTTY) if err != nil { t.Fatal(err) } @@ -69,7 +69,7 @@ func TestWithLinuxNamespace(t *testing.T) { t.Parallel() replacedNS := specs.LinuxNamespace{Type: specs.NetworkNamespace, Path: "/var/run/netns/test"} - s, err := GenerateSpec(context.Background(), nil, WithLinuxNamespace(replacedNS)) + s, err := GenerateSpec(context.Background(), nil, nil, WithLinuxNamespace(replacedNS)) if err != nil { t.Fatal(err) }