sandbox: Join cgroup sandbox on create.

When a new sandbox is created, join to its cgroup path
this will create all proxy, shim, etc in the sandbox cgroup.

Fixes: #1879

Signed-off-by: Jose Carlos Venegas Munoz <jose.carlos.venegas.munoz@intel.com>
This commit is contained in:
Jose Carlos Venegas Munoz 2019-08-19 12:08:14 -05:00
parent 2fcb8bb4d8
commit 074418f56b
3 changed files with 177 additions and 0 deletions

View File

@ -75,6 +75,18 @@ func createSandboxFromConfig(ctx context.Context, sandboxConfig SandboxConfig, f
return nil, err
}
// Move runtime to sandbox cgroup so all process are created there.
if s.config.SandboxCgroupOnly {
if err := s.setupSandboxCgroupOnly(); err != nil {
return nil, err
}
if err := s.joinSandboxCgroup(); err != nil {
return nil, err
}
}
// cleanup sandbox resources in case of any failure
defer func() {
if err != nil {

View File

@ -7,11 +7,13 @@ package virtcontainers
import (
"context"
"encoding/json"
"fmt"
"io"
"math"
"net"
"os"
"path/filepath"
"strings"
"sync"
"syscall"
@ -2069,3 +2071,72 @@ func (s *Sandbox) cpuResources() *specs.LinuxCPU {
return validCPUResources(cpu)
}
// setupSandboxCgroup creates sandbox cgroups for the sandbox config
func (s *Sandbox) setupSandboxCgroupOnly() error {
var PodSandboxConfig *ContainerConfig
if s.config == nil {
return fmt.Errorf("Sandbox config is nil")
}
for _, cConfig := range s.config.Containers {
if cConfig.Annotations[annotations.ContainerTypeKey] == string(PodSandbox) {
PodSandboxConfig = &cConfig
break
}
}
if PodSandboxConfig == nil {
return fmt.Errorf("Failed to find cgroup path for Sandbox: Container of type '%s' not found", PodSandbox)
}
configJSON, ok := PodSandboxConfig.Annotations[annotations.ConfigJSONKey]
if !ok {
return fmt.Errorf("Could not find json config in annotations for container '%s'", PodSandboxConfig.ID)
}
var spec specs.Spec
if err := json.Unmarshal([]byte(configJSON), &spec); err != nil {
return err
}
if spec.Linux == nil {
// Cgroup path is optional, just skip the setup
return nil
}
validContainerCgroup := utils.ValidCgroupPath(spec.Linux.CgroupsPath)
// Use the parent cgroup of the container sandbox as the sandbox cgroup
s.state.CgroupPath = filepath.Join(filepath.Dir(validContainerCgroup), cgroupKataPrefix+"_"+PodSandboxConfig.ID)
_, err := cgroupsNewFunc(cgroups.V1, cgroups.StaticPath(s.state.CgroupPath), &specs.LinuxResources{})
if err != nil {
return fmt.Errorf("Could not create sandbox cgroup in %v: %v", s.state.CgroupPath, err)
}
return nil
}
// joinSandboxCgroup adds the runtime PID to the sandbox defined in sandboxes' CgroupPath
func (s *Sandbox) joinSandboxCgroup() error {
if s.state.CgroupPath == "" {
// This is an optional value
return nil
}
cgroup, err := cgroupsLoadFunc(cgroups.V1, cgroups.StaticPath(s.state.CgroupPath))
if err != nil {
return fmt.Errorf("Could not load sandbox cgroup in %v: %v", s.state.CgroupPath, err)
}
s.Logger().WithField("cgroup:", s.state.CgroupPath).Debug("joining to sandbox cgroup")
runtimePid := os.Getpid()
if err := cgroup.Add(cgroups.Process{Pid: runtimePid}); err != nil {
return fmt.Errorf("Could not add runtime PID %d to sandbox cgroup: %v", runtimePid, err)
}
return nil
}

View File

@ -1473,3 +1473,97 @@ func TestSandboxExperimentalFeature(t *testing.T) {
assert.NotNil(t, exp.Get(testFeature.Name))
assert.True(t, sconfig.valid())
}
func TestSandbox_joinSandboxCgroup(t *testing.T) {
mockValidCgroup := &Sandbox{}
mockValidCgroup.state.CgroupPath = "/my/cgroup"
tests := []struct {
name string
s *Sandbox
wantErr bool
}{
{"New Config", &Sandbox{}, false},
{"Mock cgroup path", mockValidCgroup, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := tt.s.joinSandboxCgroup(); (err != nil) != tt.wantErr {
t.Errorf("Sandbox.joinSandboxCgroup() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestSandbox_SetupSandboxCgroupOnly(t *testing.T) {
sandboxContainer := ContainerConfig{}
sandboxContainer.Annotations = make(map[string]string)
sandboxContainer.Annotations[annotations.ContainerTypeKey] = string(PodSandbox)
emptyJSONLinux := ContainerConfig{}
emptyJSONLinux.Annotations = make(map[string]string)
emptyJSONLinux.Annotations[annotations.ContainerTypeKey] = string(PodSandbox)
emptyJSONLinux.Annotations[annotations.ConfigJSONKey] = "{}"
successfulContainer := ContainerConfig{}
successfulContainer.Annotations = make(map[string]string)
successfulContainer.Annotations[annotations.ContainerTypeKey] = string(PodSandbox)
successfulContainer.Annotations[annotations.ConfigJSONKey] = "{\"linux\": { \"cgroupsPath\": \"/myRuntime/myContainer\" }}"
tests := []struct {
name string
s *Sandbox
wantErr bool
}{
{
"New sandbox",
&Sandbox{},
true,
},
{
"New sandbox, new config",
&Sandbox{config: &SandboxConfig{}},
true,
},
{
"sandbox, container no sandbox type",
&Sandbox{
config: &SandboxConfig{Containers: []ContainerConfig{
{},
}}},
true,
},
{
"sandbox, container sandbox type",
&Sandbox{
config: &SandboxConfig{Containers: []ContainerConfig{
sandboxContainer,
}}},
true,
},
{
"sandbox, empty linux json",
&Sandbox{
config: &SandboxConfig{Containers: []ContainerConfig{
emptyJSONLinux,
}}},
false,
},
{
"sandbox, successful config",
&Sandbox{
config: &SandboxConfig{Containers: []ContainerConfig{
successfulContainer,
}}},
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := tt.s.setupSandboxCgroupOnly(); (err != nil) != tt.wantErr {
t.Errorf("Sandbox.SetupSandboxCgroupOnly() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}