Annotations: Provide container metadata for VM based runtimes

For hypervisor-based container runtimes (like Kata Containers, Clear Containers
or runv) a pod will be created in a VM and then create containers within the VM.

When a runtime is requested for container commands like create and start, both
the instal "pause" container and next containers need to be added to the pod
namespace (same VM).

A runtime does not know if it needs to create/start a VM or if it needs to add a
container to an already running VM pod.

This patch adds a way to provide this information through container annotations.
When starting a container or a sandbox, 2 annotations are added:

- type (Container or Sandbox)
- sandbox name

This allow to a VM based runtime to decide if they need to create a pod VM or
container within the VM pod.

Signed-off-by: Jose Carlos Venegas Munoz <jose.carlos.venegas.munoz@intel.com>
This commit is contained in:
Jose Carlos Venegas Munoz 2017-12-13 17:16:30 +00:00
parent 44cb406bb4
commit b383b0261a
5 changed files with 81 additions and 18 deletions

View File

@ -0,0 +1,32 @@
/*
Copyright 2018 The Containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package annotations
// ContainerType values
const (
// ContainerTypeSandbox represents a pod sandbox container
ContainerTypeSandbox = "sandbox"
// ContainerTypeContainer represents a container running within a pod
ContainerTypeContainer = "container"
// ContainerType is the container type (sandbox or container) annotation
ContainerType = "io.kubernetes.cri.container-type"
// SandboxID is the sandbox ID annotation
SandboxID = "io.kubernetes.cri.sandbox-id"
)

View File

@ -45,6 +45,7 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
"github.com/containerd/cri-containerd/pkg/annotations"
customopts "github.com/containerd/cri-containerd/pkg/containerd/opts" customopts "github.com/containerd/cri-containerd/pkg/containerd/opts"
cio "github.com/containerd/cri-containerd/pkg/server/io" cio "github.com/containerd/cri-containerd/pkg/server/io"
containerstore "github.com/containerd/cri-containerd/pkg/store/container" containerstore "github.com/containerd/cri-containerd/pkg/store/container"
@ -143,10 +144,11 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C
// Generate container runtime spec. // Generate container runtime spec.
mounts := c.generateContainerMounts(getSandboxRootDir(c.config.RootDir, sandboxID), config) mounts := c.generateContainerMounts(getSandboxRootDir(c.config.RootDir, sandboxID), config)
spec, err := c.generateContainerSpec(id, sandboxPid, config, sandboxConfig, &image.ImageSpec.Config, append(mounts, volumeMounts...)) spec, err := c.generateContainerSpec(id, sandboxID, sandboxPid, config, sandboxConfig, &image.ImageSpec.Config, append(mounts, volumeMounts...))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to generate container %q spec: %v", id, err) return nil, fmt.Errorf("failed to generate container %q spec: %v", id, err)
} }
glog.V(4).Infof("Container %q spec: %#+v", id, spew.NewFormatter(spec)) glog.V(4).Infof("Container %q spec: %#+v", id, spew.NewFormatter(spec))
// Set snapshotter before any other options. // Set snapshotter before any other options.
@ -271,7 +273,7 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C
return &runtime.CreateContainerResponse{ContainerId: id}, nil return &runtime.CreateContainerResponse{ContainerId: id}, nil
} }
func (c *criContainerdService) generateContainerSpec(id string, sandboxPid uint32, config *runtime.ContainerConfig, func (c *criContainerdService) generateContainerSpec(id string, sandboxID string, sandboxPid uint32, config *runtime.ContainerConfig,
sandboxConfig *runtime.PodSandboxConfig, imageConfig *imagespec.ImageConfig, extraMounts []*runtime.Mount) (*runtimespec.Spec, error) { sandboxConfig *runtime.PodSandboxConfig, imageConfig *imagespec.ImageConfig, extraMounts []*runtime.Mount) (*runtimespec.Spec, error) {
// Creates a spec Generator with the default spec. // Creates a spec Generator with the default spec.
spec, err := defaultRuntimeSpec(id) spec, err := defaultRuntimeSpec(id)
@ -365,6 +367,9 @@ func (c *criContainerdService) generateContainerSpec(id string, sandboxPid uint3
g.AddProcessAdditionalGid(uint32(group)) g.AddProcessAdditionalGid(uint32(group))
} }
g.AddAnnotation(annotations.ContainerType, annotations.ContainerTypeContainer)
g.AddAnnotation(annotations.SandboxID, sandboxID)
return g.Spec(), nil return g.Spec(), nil
} }

View File

@ -32,6 +32,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
"github.com/containerd/cri-containerd/pkg/annotations"
ostesting "github.com/containerd/cri-containerd/pkg/os/testing" ostesting "github.com/containerd/cri-containerd/pkg/os/testing"
"github.com/containerd/cri-containerd/pkg/util" "github.com/containerd/cri-containerd/pkg/util"
) )
@ -56,7 +57,7 @@ func checkMount(t *testing.T, mounts []runtimespec.Mount, src, dest, typ string,
} }
func getCreateContainerTestData() (*runtime.ContainerConfig, *runtime.PodSandboxConfig, func getCreateContainerTestData() (*runtime.ContainerConfig, *runtime.PodSandboxConfig,
*imagespec.ImageConfig, func(*testing.T, string, uint32, *runtimespec.Spec)) { *imagespec.ImageConfig, func(*testing.T, string, string, uint32, *runtimespec.Spec)) {
config := &runtime.ContainerConfig{ config := &runtime.ContainerConfig{
Metadata: &runtime.ContainerMetadata{ Metadata: &runtime.ContainerMetadata{
Name: "test-name", Name: "test-name",
@ -122,7 +123,7 @@ func getCreateContainerTestData() (*runtime.ContainerConfig, *runtime.PodSandbox
Cmd: []string{"cmd"}, Cmd: []string{"cmd"},
WorkingDir: "/workspace", WorkingDir: "/workspace",
} }
specCheck := func(t *testing.T, id string, sandboxPid uint32, spec *runtimespec.Spec) { specCheck := func(t *testing.T, id string, sandboxID string, sandboxPid uint32, spec *runtimespec.Spec) {
assert.Equal(t, relativeRootfsPath, spec.Root.Path) assert.Equal(t, relativeRootfsPath, spec.Root.Path)
assert.Equal(t, []string{"test", "command", "test", "args"}, spec.Process.Args) assert.Equal(t, []string{"test", "command", "test", "args"}, spec.Process.Args)
assert.Equal(t, "test-cwd", spec.Process.Cwd) assert.Equal(t, "test-cwd", spec.Process.Cwd)
@ -168,6 +169,13 @@ func getCreateContainerTestData() (*runtime.ContainerConfig, *runtime.PodSandbox
Type: runtimespec.UTSNamespace, Type: runtimespec.UTSNamespace,
Path: getUTSNamespace(sandboxPid), Path: getUTSNamespace(sandboxPid),
}) })
t.Logf("Check PodSandbox annotations")
assert.Contains(t, spec.Annotations, annotations.SandboxID)
assert.EqualValues(t, spec.Annotations[annotations.SandboxID], sandboxID)
assert.Contains(t, spec.Annotations, annotations.ContainerType)
assert.EqualValues(t, spec.Annotations[annotations.ContainerType], annotations.ContainerTypeContainer)
} }
return config, sandboxConfig, imageConfig, specCheck return config, sandboxConfig, imageConfig, specCheck
} }
@ -177,13 +185,15 @@ func TestGeneralContainerSpec(t *testing.T) {
testPid := uint32(1234) testPid := uint32(1234)
config, sandboxConfig, imageConfig, specCheck := getCreateContainerTestData() config, sandboxConfig, imageConfig, specCheck := getCreateContainerTestData()
c := newTestCRIContainerdService() c := newTestCRIContainerdService()
spec, err := c.generateContainerSpec(testID, testPid, config, sandboxConfig, imageConfig, nil) testSandboxID := "SandboxID"
spec, err := c.generateContainerSpec(testID, testSandboxID, testPid, config, sandboxConfig, imageConfig, nil)
require.NoError(t, err) require.NoError(t, err)
specCheck(t, testID, testPid, spec) specCheck(t, testID, testSandboxID, testPid, spec)
} }
func TestContainerCapabilities(t *testing.T) { func TestContainerCapabilities(t *testing.T) {
testID := "test-id" testID := "test-id"
testSandboxID := "SandboxID"
testPid := uint32(1234) testPid := uint32(1234)
config, sandboxConfig, imageConfig, specCheck := getCreateContainerTestData() config, sandboxConfig, imageConfig, specCheck := getCreateContainerTestData()
c := newTestCRIContainerdService() c := newTestCRIContainerdService()
@ -231,9 +241,9 @@ func TestContainerCapabilities(t *testing.T) {
} { } {
t.Logf("TestCase %q", desc) t.Logf("TestCase %q", desc)
config.Linux.SecurityContext.Capabilities = test.capability config.Linux.SecurityContext.Capabilities = test.capability
spec, err := c.generateContainerSpec(testID, testPid, config, sandboxConfig, imageConfig, nil) spec, err := c.generateContainerSpec(testID, testSandboxID, testPid, config, sandboxConfig, imageConfig, nil)
require.NoError(t, err) require.NoError(t, err)
specCheck(t, testID, testPid, spec) specCheck(t, testID, testSandboxID, testPid, spec)
t.Log(spec.Process.Capabilities.Bounding) t.Log(spec.Process.Capabilities.Bounding)
for _, include := range test.includes { for _, include := range test.includes {
assert.Contains(t, spec.Process.Capabilities.Bounding, include) assert.Contains(t, spec.Process.Capabilities.Bounding, include)
@ -252,14 +262,15 @@ func TestContainerCapabilities(t *testing.T) {
func TestContainerSpecTty(t *testing.T) { func TestContainerSpecTty(t *testing.T) {
testID := "test-id" testID := "test-id"
testSandboxID := "SandboxID"
testPid := uint32(1234) testPid := uint32(1234)
config, sandboxConfig, imageConfig, specCheck := getCreateContainerTestData() config, sandboxConfig, imageConfig, specCheck := getCreateContainerTestData()
c := newTestCRIContainerdService() c := newTestCRIContainerdService()
for _, tty := range []bool{true, false} { for _, tty := range []bool{true, false} {
config.Tty = tty config.Tty = tty
spec, err := c.generateContainerSpec(testID, testPid, config, sandboxConfig, imageConfig, nil) spec, err := c.generateContainerSpec(testID, testSandboxID, testPid, config, sandboxConfig, imageConfig, nil)
require.NoError(t, err) require.NoError(t, err)
specCheck(t, testID, testPid, spec) specCheck(t, testID, testSandboxID, testPid, spec)
assert.Equal(t, tty, spec.Process.Terminal) assert.Equal(t, tty, spec.Process.Terminal)
if tty { if tty {
assert.Contains(t, spec.Process.Env, "TERM=xterm") assert.Contains(t, spec.Process.Env, "TERM=xterm")
@ -271,20 +282,22 @@ func TestContainerSpecTty(t *testing.T) {
func TestContainerSpecReadonlyRootfs(t *testing.T) { func TestContainerSpecReadonlyRootfs(t *testing.T) {
testID := "test-id" testID := "test-id"
testSandboxID := "SandboxID"
testPid := uint32(1234) testPid := uint32(1234)
config, sandboxConfig, imageConfig, specCheck := getCreateContainerTestData() config, sandboxConfig, imageConfig, specCheck := getCreateContainerTestData()
c := newTestCRIContainerdService() c := newTestCRIContainerdService()
for _, readonly := range []bool{true, false} { for _, readonly := range []bool{true, false} {
config.Linux.SecurityContext.ReadonlyRootfs = readonly config.Linux.SecurityContext.ReadonlyRootfs = readonly
spec, err := c.generateContainerSpec(testID, testPid, config, sandboxConfig, imageConfig, nil) spec, err := c.generateContainerSpec(testID, testSandboxID, testPid, config, sandboxConfig, imageConfig, nil)
require.NoError(t, err) require.NoError(t, err)
specCheck(t, testID, testPid, spec) specCheck(t, testID, testSandboxID, testPid, spec)
assert.Equal(t, readonly, spec.Root.Readonly) assert.Equal(t, readonly, spec.Root.Readonly)
} }
} }
func TestContainerSpecWithExtraMounts(t *testing.T) { func TestContainerSpecWithExtraMounts(t *testing.T) {
testID := "test-id" testID := "test-id"
testSandboxID := "SandboxID"
testPid := uint32(1234) testPid := uint32(1234)
config, sandboxConfig, imageConfig, specCheck := getCreateContainerTestData() config, sandboxConfig, imageConfig, specCheck := getCreateContainerTestData()
c := newTestCRIContainerdService() c := newTestCRIContainerdService()
@ -299,9 +312,9 @@ func TestContainerSpecWithExtraMounts(t *testing.T) {
HostPath: "test-host-path-extra", HostPath: "test-host-path-extra",
Readonly: true, Readonly: true,
} }
spec, err := c.generateContainerSpec(testID, testPid, config, sandboxConfig, imageConfig, []*runtime.Mount{extraMount}) spec, err := c.generateContainerSpec(testID, testSandboxID, testPid, config, sandboxConfig, imageConfig, []*runtime.Mount{extraMount})
require.NoError(t, err) require.NoError(t, err)
specCheck(t, testID, testPid, spec) specCheck(t, testID, testSandboxID, testPid, spec)
var mounts []runtimespec.Mount var mounts []runtimespec.Mount
for _, m := range spec.Mounts { for _, m := range spec.Mounts {
if m.Destination == "test-container-path" { if m.Destination == "test-container-path" {
@ -688,22 +701,23 @@ func TestMountPropagation(t *testing.T) {
func TestPidNamespace(t *testing.T) { func TestPidNamespace(t *testing.T) {
testID := "test-id" testID := "test-id"
testPid := uint32(1234) testPid := uint32(1234)
testSandboxID := "SandboxID"
config, sandboxConfig, imageConfig, specCheck := getCreateContainerTestData() config, sandboxConfig, imageConfig, specCheck := getCreateContainerTestData()
c := newTestCRIContainerdService() c := newTestCRIContainerdService()
t.Logf("should not set pid namespace when host pid is true") t.Logf("should not set pid namespace when host pid is true")
config.Linux.SecurityContext.NamespaceOptions = &runtime.NamespaceOption{HostPid: true} config.Linux.SecurityContext.NamespaceOptions = &runtime.NamespaceOption{HostPid: true}
spec, err := c.generateContainerSpec(testID, testPid, config, sandboxConfig, imageConfig, nil) spec, err := c.generateContainerSpec(testID, testSandboxID, testPid, config, sandboxConfig, imageConfig, nil)
require.NoError(t, err) require.NoError(t, err)
specCheck(t, testID, testPid, spec) specCheck(t, testID, testSandboxID, testPid, spec)
for _, ns := range spec.Linux.Namespaces { for _, ns := range spec.Linux.Namespaces {
assert.NotEqual(t, ns.Type, runtimespec.PIDNamespace) assert.NotEqual(t, ns.Type, runtimespec.PIDNamespace)
} }
t.Logf("should set pid namespace when host pid is false") t.Logf("should set pid namespace when host pid is false")
config.Linux.SecurityContext.NamespaceOptions = &runtime.NamespaceOption{HostPid: false} config.Linux.SecurityContext.NamespaceOptions = &runtime.NamespaceOption{HostPid: false}
spec, err = c.generateContainerSpec(testID, testPid, config, sandboxConfig, imageConfig, nil) spec, err = c.generateContainerSpec(testID, testSandboxID, testPid, config, sandboxConfig, imageConfig, nil)
require.NoError(t, err) require.NoError(t, err)
specCheck(t, testID, testPid, spec) specCheck(t, testID, testSandboxID, testPid, spec)
assert.Contains(t, spec.Linux.Namespaces, runtimespec.LinuxNamespace{ assert.Contains(t, spec.Linux.Namespaces, runtimespec.LinuxNamespace{
Type: runtimespec.PIDNamespace, Type: runtimespec.PIDNamespace,
}) })

View File

@ -34,6 +34,7 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
"github.com/containerd/cri-containerd/pkg/annotations"
customopts "github.com/containerd/cri-containerd/pkg/containerd/opts" customopts "github.com/containerd/cri-containerd/pkg/containerd/opts"
sandboxstore "github.com/containerd/cri-containerd/pkg/store/sandbox" sandboxstore "github.com/containerd/cri-containerd/pkg/store/sandbox"
"github.com/containerd/cri-containerd/pkg/util" "github.com/containerd/cri-containerd/pkg/util"
@ -331,6 +332,9 @@ func (c *criContainerdService) generateSandboxContainerSpec(id string, config *r
g.SetLinuxResourcesCPUShares(uint64(defaultSandboxCPUshares)) g.SetLinuxResourcesCPUShares(uint64(defaultSandboxCPUshares))
g.SetProcessOOMScoreAdj(int(defaultSandboxOOMAdj)) g.SetProcessOOMScoreAdj(int(defaultSandboxOOMAdj))
g.AddAnnotation(annotations.ContainerType, annotations.ContainerTypeSandbox)
g.AddAnnotation(annotations.SandboxID, id)
return g.Spec(), nil return g.Spec(), nil
} }

View File

@ -20,6 +20,7 @@ import (
"os" "os"
"testing" "testing"
"github.com/containerd/cri-containerd/pkg/annotations"
"github.com/containerd/typeurl" "github.com/containerd/typeurl"
"github.com/cri-o/ocicni/pkg/ocicni" "github.com/cri-o/ocicni/pkg/ocicni"
imagespec "github.com/opencontainers/image-spec/specs-go/v1" imagespec "github.com/opencontainers/image-spec/specs-go/v1"
@ -64,6 +65,13 @@ func getRunPodSandboxTestData() (*runtime.PodSandboxConfig, *imagespec.ImageConf
assert.Equal(t, "/workspace", spec.Process.Cwd) assert.Equal(t, "/workspace", spec.Process.Cwd)
assert.EqualValues(t, *spec.Linux.Resources.CPU.Shares, defaultSandboxCPUshares) assert.EqualValues(t, *spec.Linux.Resources.CPU.Shares, defaultSandboxCPUshares)
assert.EqualValues(t, *spec.Process.OOMScoreAdj, defaultSandboxOOMAdj) assert.EqualValues(t, *spec.Process.OOMScoreAdj, defaultSandboxOOMAdj)
t.Logf("Check PodSandbox annotations")
assert.Contains(t, spec.Annotations, annotations.SandboxID)
assert.EqualValues(t, spec.Annotations[annotations.SandboxID], id)
assert.Contains(t, spec.Annotations, annotations.ContainerType)
assert.EqualValues(t, spec.Annotations[annotations.ContainerType], annotations.ContainerTypeSandbox)
} }
return config, imageConfig, specCheck return config, imageConfig, specCheck
} }