Move spec generation to Container Create
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
fa14f2ef3a
commit
c601606f84
@ -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),
|
||||
)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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...)
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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),
|
||||
)
|
||||
|
@ -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
|
||||
}
|
||||
|
5
spec.go
5
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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user