From 6dd7552537728a120a47f3575d1de342f1be8070 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Tue, 13 Jan 2015 00:54:00 +0300 Subject: [PATCH] new-api: implement fs and systemd cgroup managers Signed-off-by: Andrey Vagin --- cgroup_manager.go | 30 ------------------ cgroups/cgroups.go | 11 +++++++ cgroups/fs/apply_raw.go | 43 +++++++++++++++++++------ cgroups/manager/manager.go | 19 ++++++++++++ cgroups/systemd/apply_nosystemd.go | 23 ++++++++++++-- cgroups/systemd/apply_systemd.go | 50 ++++++++++++++++++++---------- linux_container.go | 5 +-- linux_container_test.go | 15 +++++++++ linux_factory.go | 6 ++-- namespaces/exec.go | 23 +++----------- 10 files changed, 144 insertions(+), 81 deletions(-) delete mode 100644 cgroup_manager.go create mode 100644 cgroups/manager/manager.go diff --git a/cgroup_manager.go b/cgroup_manager.go deleted file mode 100644 index 1bcb1bc4..00000000 --- a/cgroup_manager.go +++ /dev/null @@ -1,30 +0,0 @@ -package libcontainer - -import ( - "github.com/docker/libcontainer/cgroups" -) - -// TODO(vmarmol): Move this to cgroups and rename to Manager. -type CgroupManager interface { - GetPids() ([]int, error) - GetStats() (*cgroups.Stats, error) -} - -func NewCgroupManager() CgroupManager { - return &fsManager{} -} - -type fsManager struct { -} - -func (m *fsManager) GetPids() ([]int, error) { - // TODO(vmarmol): Implement - //return fs.GetPids(config) - panic("not implemented") -} - -func (m *fsManager) GetStats() (*cgroups.Stats, error) { - // TODO(vmarmol): Implement - //return fs.GetStats(config) - panic("not implemented") -} diff --git a/cgroups/cgroups.go b/cgroups/cgroups.go index fe360059..0ce7cc97 100644 --- a/cgroups/cgroups.go +++ b/cgroups/cgroups.go @@ -6,6 +6,17 @@ import ( "github.com/docker/libcontainer/devices" ) +type Manager interface { + Apply(pid int) error + + GetPids() ([]int, error) + GetStats() (*Stats, error) + + RemovePaths() error + GetPaths() map[string]string + SetPaths(map[string]string) +} + type FreezerState string const ( diff --git a/cgroups/fs/apply_raw.go b/cgroups/fs/apply_raw.go index 6f85793d..68789286 100644 --- a/cgroups/fs/apply_raw.go +++ b/cgroups/fs/apply_raw.go @@ -24,6 +24,11 @@ var ( CgroupProcesses = "cgroup.procs" ) +type Manager struct { + Cgroups *cgroups.Cgroup + paths map[string]string +} + // The absolute path to the root of the cgroup hierarchies. var cgroupRoot string @@ -57,10 +62,14 @@ type data struct { pid int } -func Apply(c *cgroups.Cgroup, pid int) (map[string]string, error) { - d, err := getCgroupData(c, pid) +func (m *Manager) Apply(pid int) error { + if m.Cgroups == nil { + return nil + } + + d, err := getCgroupData(m.Cgroups, pid) if err != nil { - return nil, err + return err } paths := make(map[string]string) @@ -71,7 +80,7 @@ func Apply(c *cgroups.Cgroup, pid int) (map[string]string, error) { }() for name, sys := range subsystems { if err := sys.Set(d); err != nil { - return nil, err + return err } // FIXME: Apply should, ideally, be reentrant or be broken up into a separate // create and join phase so that the cgroup hierarchy for a container can be @@ -81,11 +90,25 @@ func Apply(c *cgroups.Cgroup, pid int) (map[string]string, error) { if cgroups.IsNotFound(err) { continue } - return nil, err + return err } paths[name] = p } - return paths, nil + m.paths = paths + + return nil +} + +func (m *Manager) RemovePaths() error { + return cgroups.RemovePaths(m.paths) +} + +func (m *Manager) GetPaths() map[string]string { + return m.paths +} + +func (m *Manager) SetPaths(paths map[string]string) { + m.paths = paths } // Symmetrical public function to update device based cgroups. Also available @@ -101,9 +124,9 @@ func ApplyDevices(c *cgroups.Cgroup, pid int) error { return devices.Set(d) } -func GetStats(systemPaths map[string]string) (*cgroups.Stats, error) { +func (m *Manager) GetStats() (*cgroups.Stats, error) { stats := cgroups.NewStats() - for name, path := range systemPaths { + for name, path := range m.paths { sys, ok := subsystems[name] if !ok { continue @@ -131,8 +154,8 @@ func Freeze(c *cgroups.Cgroup, state cgroups.FreezerState) error { return freezer.Set(d) } -func GetPids(c *cgroups.Cgroup) ([]int, error) { - d, err := getCgroupData(c, 0) +func (m *Manager) GetPids() ([]int, error) { + d, err := getCgroupData(m.Cgroups, 0) if err != nil { return nil, err } diff --git a/cgroups/manager/manager.go b/cgroups/manager/manager.go new file mode 100644 index 00000000..3ee625b4 --- /dev/null +++ b/cgroups/manager/manager.go @@ -0,0 +1,19 @@ +package manager + +import ( + "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/cgroups/fs" + "github.com/docker/libcontainer/cgroups/systemd" +) + +func NewCgroupManager(cgroups *cgroups.Cgroup) cgroups.Manager { + if systemd.UseSystemd() { + return &systemd.Manager{ + Cgroups: cgroups, + } + } + + return &fs.Manager{ + Cgroups: cgroups, + } +} diff --git a/cgroups/systemd/apply_nosystemd.go b/cgroups/systemd/apply_nosystemd.go index 4b9a2f5b..f35eb594 100644 --- a/cgroups/systemd/apply_nosystemd.go +++ b/cgroups/systemd/apply_nosystemd.go @@ -8,15 +8,34 @@ import ( "github.com/docker/libcontainer/cgroups" ) +type Manager struct { + Cgroups *cgroups.Cgroup +} + func UseSystemd() bool { return false } -func Apply(c *cgroups.Cgroup, pid int) (map[string]string, error) { +func (m *Manager) Apply(pid int) error { + return fmt.Errorf("Systemd not supported") +} + +func (m *Manager) GetPids() ([]int, error) { return nil, fmt.Errorf("Systemd not supported") } -func GetPids(c *cgroups.Cgroup) ([]int, error) { +func (m *Manager) RemovePaths() error { + return fmt.Errorf("Systemd not supported") +} + +func (m *Manager) GetPaths() map[string]string { + return nil +} + +func (m *Manager) SetPaths(paths map[string]string) { +} + +func (m *Manager) GetStats() (*cgroups.Stats, error) { return nil, fmt.Errorf("Systemd not supported") } diff --git a/cgroups/systemd/apply_systemd.go b/cgroups/systemd/apply_systemd.go index 3d898114..96ffdebe 100644 --- a/cgroups/systemd/apply_systemd.go +++ b/cgroups/systemd/apply_systemd.go @@ -19,8 +19,9 @@ import ( "github.com/godbus/dbus" ) -type systemdCgroup struct { - cgroup *cgroups.Cgroup +type Manager struct { + Cgroups *cgroups.Cgroup + paths map[string]string } type subsystem interface { @@ -81,16 +82,14 @@ func getIfaceForUnit(unitName string) string { return "Unit" } -func Apply(c *cgroups.Cgroup, pid int) (map[string]string, error) { +func (m *Manager) Apply(pid int) error { var ( + c = m.Cgroups unitName = getUnitName(c) slice = "system.slice" properties []systemd.Property - res = &systemdCgroup{} ) - res.cgroup = c - if c.Slice != "" { slice = c.Slice } @@ -120,19 +119,19 @@ func Apply(c *cgroups.Cgroup, pid int) (map[string]string, error) { } if _, err := theConn.StartTransientUnit(unitName, "replace", properties...); err != nil { - return nil, err + return err } if !c.AllowAllDevices { if err := joinDevices(c, pid); err != nil { - return nil, err + return err } } // -1 disables memorySwap if c.MemorySwap >= 0 && (c.Memory != 0 || c.MemorySwap > 0) { if err := joinMemory(c, pid); err != nil { - return nil, err + return err } } @@ -140,11 +139,11 @@ func Apply(c *cgroups.Cgroup, pid int) (map[string]string, error) { // we need to manually join the freezer and cpuset cgroup in systemd // because it does not currently support it via the dbus api. if err := joinFreezer(c, pid); err != nil { - return nil, err + return err } if err := joinCpuset(c, pid); err != nil { - return nil, err + return err } paths := make(map[string]string) @@ -158,17 +157,32 @@ func Apply(c *cgroups.Cgroup, pid int) (map[string]string, error) { "perf_event", "freezer", } { - subsystemPath, err := getSubsystemPath(res.cgroup, sysname) + subsystemPath, err := getSubsystemPath(m.Cgroups, sysname) if err != nil { // Don't fail if a cgroup hierarchy was not found, just skip this subsystem if cgroups.IsNotFound(err) { continue } - return nil, err + return err } paths[sysname] = subsystemPath } - return paths, nil + + m.paths = paths + + return nil +} + +func (m *Manager) RemovePaths() error { + return cgroups.RemovePaths(m.paths) +} + +func (m *Manager) GetPaths() map[string]string { + return m.paths +} + +func (m *Manager) SetPaths(paths map[string]string) { + m.paths = paths } func writeFile(dir, file, data string) error { @@ -229,8 +243,8 @@ func Freeze(c *cgroups.Cgroup, state cgroups.FreezerState) error { return nil } -func GetPids(c *cgroups.Cgroup) ([]int, error) { - path, err := getSubsystemPath(c, "cpu") +func (m *Manager) GetPids() ([]int, error) { + path, err := getSubsystemPath(m.Cgroups, "cpu") if err != nil { return nil, err } @@ -238,6 +252,10 @@ func GetPids(c *cgroups.Cgroup) ([]int, error) { return cgroups.ReadProcsFile(path) } +func (m *Manager) GetStats() (*cgroups.Stats, error) { + panic("not implemented") +} + func getUnitName(c *cgroups.Cgroup) string { return fmt.Sprintf("%s-%s.scope", c.Parent, c.Name) } diff --git a/linux_container.go b/linux_container.go index 7845f8db..fffd7c67 100644 --- a/linux_container.go +++ b/linux_container.go @@ -10,6 +10,7 @@ import ( "path/filepath" "syscall" + "github.com/docker/libcontainer/cgroups" "github.com/docker/libcontainer/configs" "github.com/docker/libcontainer/namespaces" "github.com/docker/libcontainer/network" @@ -21,7 +22,7 @@ type linuxContainer struct { root string config *configs.Config state *configs.State - cgroupManager CgroupManager + cgroupManager cgroups.Manager initArgs []string } @@ -133,7 +134,7 @@ func (c *linuxContainer) updateStateFile() error { } func (c *linuxContainer) startInitProcess(cmd *exec.Cmd, config *ProcessConfig) error { - err := namespaces.Exec(config.Args, config.Env, cmd, c.config, c.state) + err := namespaces.Exec(config.Args, config.Env, cmd, c.config, c.cgroupManager, c.state) if err != nil { return err } diff --git a/linux_container_test.go b/linux_container_test.go index 64d4fb8b..a3c6e306 100644 --- a/linux_container_test.go +++ b/linux_container_test.go @@ -22,6 +22,21 @@ func (m *mockCgroupManager) GetStats() (*cgroups.Stats, error) { return m.stats, nil } +func (m *mockCgroupManager) Apply(pid int) error { + return nil +} + +func (m *mockCgroupManager) RemovePaths() error { + return nil +} + +func (m *mockCgroupManager) GetPaths() map[string]string { + return nil +} + +func (m *mockCgroupManager) SetPaths(map[string]string) { +} + func TestGetContainerPids(t *testing.T) { container := &linuxContainer{ id: "myid", diff --git a/linux_factory.go b/linux_factory.go index 10e464ec..a8311668 100644 --- a/linux_factory.go +++ b/linux_factory.go @@ -11,6 +11,7 @@ import ( "github.com/golang/glog" + cgroups "github.com/docker/libcontainer/cgroups/manager" "github.com/docker/libcontainer/configs" "github.com/docker/libcontainer/namespaces" ) @@ -88,7 +89,7 @@ func (l *linuxFactory) Create(id string, config *configs.Config) (Container, err return nil, newGenericError(err, SystemError) } - cgroupManager := NewCgroupManager() + cgroupManager := cgroups.NewCgroupManager(config.Cgroups) return &linuxContainer{ id: id, root: containerRoot, @@ -116,7 +117,8 @@ func (l *linuxFactory) Load(id string) (Container, error) { return nil, err } - cgroupManager := NewCgroupManager() + cgroupManager := cgroups.NewCgroupManager(config.Cgroups) + cgroupManager.SetPaths(state.CgroupPaths) glog.Infof("using %s as cgroup manager", cgroupManager) return &linuxContainer{ id: id, diff --git a/namespaces/exec.go b/namespaces/exec.go index 1d7914a0..ff20c414 100644 --- a/namespaces/exec.go +++ b/namespaces/exec.go @@ -10,8 +10,6 @@ import ( "syscall" "github.com/docker/libcontainer/cgroups" - "github.com/docker/libcontainer/cgroups/fs" - "github.com/docker/libcontainer/cgroups/systemd" "github.com/docker/libcontainer/configs" "github.com/docker/libcontainer/network" "github.com/docker/libcontainer/system" @@ -21,7 +19,7 @@ import ( // Move this to libcontainer package. // Exec performs setup outside of a namespace so that a container can be // executed. Exec is a high level function for working with container namespaces. -func Exec(args []string, env []string, command *exec.Cmd, container *configs.Config, state *configs.State) error { +func Exec(args []string, env []string, command *exec.Cmd, container *configs.Config, cgroupManager cgroups.Manager, state *configs.State) error { var err error // create a pipe so that we can syncronize with the namespaced process and @@ -70,11 +68,11 @@ func Exec(args []string, env []string, command *exec.Cmd, container *configs.Con // Do this before syncing with child so that no children // can escape the cgroup - cgroupPaths, err := SetupCgroups(container, command.Process.Pid) + err = cgroupManager.Apply(command.Process.Pid) if err != nil { return terminate(err) } - defer cgroups.RemovePaths(cgroupPaths) + defer cgroupManager.RemovePaths() var networkState network.NetworkState if err := InitializeNetworking(container, command.Process.Pid, &networkState); err != nil { @@ -102,7 +100,7 @@ func Exec(args []string, env []string, command *exec.Cmd, container *configs.Con state.InitPid = command.Process.Pid state.InitStartTime = started state.NetworkState = networkState - state.CgroupPaths = cgroupPaths + state.CgroupPaths = cgroupManager.GetPaths() return nil } @@ -140,19 +138,6 @@ func DefaultCreateCommand(container *configs.Config, console, dataPath, init str return command } -// SetupCgroups applies the cgroup restrictions to the process running in the container based -// on the container's configuration -func SetupCgroups(container *configs.Config, nspid int) (map[string]string, error) { - if container.Cgroups != nil { - c := container.Cgroups - if systemd.UseSystemd() { - return systemd.Apply(c, nspid) - } - return fs.Apply(c, nspid) - } - return map[string]string{}, nil -} - // InitializeNetworking creates the container's network stack outside of the namespace and moves // interfaces into the container's net namespaces if necessary func InitializeNetworking(container *configs.Config, nspid int, networkState *network.NetworkState) error {