From 074418f56bb738a2ff4f4407cd69577c9d338815 Mon Sep 17 00:00:00 2001 From: Jose Carlos Venegas Munoz Date: Mon, 19 Aug 2019 12:08:14 -0500 Subject: [PATCH] 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 --- virtcontainers/api.go | 12 +++++ virtcontainers/sandbox.go | 71 +++++++++++++++++++++++++ virtcontainers/sandbox_test.go | 94 ++++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+) diff --git a/virtcontainers/api.go b/virtcontainers/api.go index 285d3f6786..a5d33fe7ac 100644 --- a/virtcontainers/api.go +++ b/virtcontainers/api.go @@ -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 { diff --git a/virtcontainers/sandbox.go b/virtcontainers/sandbox.go index 1edc8dc09a..ffe0052559 100644 --- a/virtcontainers/sandbox.go +++ b/virtcontainers/sandbox.go @@ -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 +} diff --git a/virtcontainers/sandbox_test.go b/virtcontainers/sandbox_test.go index df2a16a3fe..04ee10f258 100644 --- a/virtcontainers/sandbox_test.go +++ b/virtcontainers/sandbox_test.go @@ -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) + } + }) + } +}