Podman info add support for status of cgroup controllers

Signed-off-by: flouthoc <flouthoc.git@gmail.com>
This commit is contained in:
flouthoc
2021-05-19 01:19:20 +05:30
committed by Matthew Heon
parent ac8b7ddd8d
commit c53638e9f6
5 changed files with 163 additions and 41 deletions

View File

@ -32,6 +32,12 @@ $ podman info
host:
arch: amd64
buildahVersion: 1.19.0-dev
cgroupControllers:
- cpuset
- cpu
- io
- memory
- pids
cgroupManager: systemd
cgroupVersion: v2
conmon:
@ -145,6 +151,13 @@ Run podman info with JSON formatted response:
"buildahVersion": "1.19.0-dev",
"cgroupManager": "systemd",
"cgroupVersion": "v2",
"cgroupControllers": [
"cpuset",
"cpu",
"io",
"memory",
"pids"
],
"conmon": {
"package": "conmon-2.0.22-2.fc33.x86_64",
"path": "/usr/bin/conmon",

View File

@ -23,21 +23,22 @@ type SecurityInfo struct {
// HostInfo describes the libpod host
type HostInfo struct {
Arch string `json:"arch"`
BuildahVersion string `json:"buildahVersion"`
CgroupManager string `json:"cgroupManager"`
CGroupsVersion string `json:"cgroupVersion"`
Conmon *ConmonInfo `json:"conmon"`
CPUs int `json:"cpus"`
Distribution DistributionInfo `json:"distribution"`
EventLogger string `json:"eventLogger"`
Hostname string `json:"hostname"`
IDMappings IDMappings `json:"idMappings,omitempty"`
Kernel string `json:"kernel"`
MemFree int64 `json:"memFree"`
MemTotal int64 `json:"memTotal"`
OCIRuntime *OCIRuntimeInfo `json:"ociRuntime"`
OS string `json:"os"`
Arch string `json:"arch"`
BuildahVersion string `json:"buildahVersion"`
CgroupManager string `json:"cgroupManager"`
CGroupsVersion string `json:"cgroupVersion"`
CgroupControllers []string `json:"cgroupControllers"`
Conmon *ConmonInfo `json:"conmon"`
CPUs int `json:"cpus"`
Distribution DistributionInfo `json:"distribution"`
EventLogger string `json:"eventLogger"`
Hostname string `json:"hostname"`
IDMappings IDMappings `json:"idMappings,omitempty"`
Kernel string `json:"kernel"`
MemFree int64 `json:"memFree"`
MemTotal int64 `json:"memTotal"`
OCIRuntime *OCIRuntimeInfo `json:"ociRuntime"`
OS string `json:"os"`
// RemoteSocket returns the UNIX domain socket the Podman service is listening on
RemoteSocket *RemoteSocket `json:"remoteSocket,omitempty"`
RuntimeInfo map[string]interface{} `json:"runtimeInfo,omitempty"`

View File

@ -93,20 +93,33 @@ func (r *Runtime) hostInfo() (*define.HostInfo, error) {
return nil, errors.Wrapf(err, "error getting Seccomp profile path")
}
// CGroups version
unified, err := cgroups.IsCgroup2UnifiedMode()
if err != nil {
return nil, errors.Wrapf(err, "error reading cgroups mode")
}
// Get Map of all available controllers
availableControllers, err := cgroups.GetAvailableControllers(nil, unified)
if err != nil {
return nil, errors.Wrapf(err, "error getting available cgroup controllers")
}
info := define.HostInfo{
Arch: runtime.GOARCH,
BuildahVersion: buildah.Version,
CgroupManager: r.config.Engine.CgroupManager,
Linkmode: linkmode.Linkmode(),
CPUs: runtime.NumCPU(),
Distribution: hostDistributionInfo,
EventLogger: r.eventer.String(),
Hostname: host,
IDMappings: define.IDMappings{},
Kernel: kv,
MemFree: mi.MemFree,
MemTotal: mi.MemTotal,
OS: runtime.GOOS,
Arch: runtime.GOARCH,
BuildahVersion: buildah.Version,
CgroupManager: r.config.Engine.CgroupManager,
CgroupControllers: availableControllers,
Linkmode: linkmode.Linkmode(),
CPUs: runtime.NumCPU(),
Distribution: hostDistributionInfo,
EventLogger: r.eventer.String(),
Hostname: host,
IDMappings: define.IDMappings{},
Kernel: kv,
MemFree: mi.MemFree,
MemTotal: mi.MemTotal,
OS: runtime.GOOS,
Security: define.SecurityInfo{
AppArmorEnabled: apparmor.IsEnabled(),
DefaultCapabilities: strings.Join(r.config.Containers.DefaultCapabilities, ","),
@ -120,11 +133,6 @@ func (r *Runtime) hostInfo() (*define.HostInfo, error) {
SwapTotal: mi.SwapTotal,
}
// CGroups version
unified, err := cgroups.IsCgroup2UnifiedMode()
if err != nil {
return nil, errors.Wrapf(err, "error reading cgroups mode")
}
cgroupVersion := "v1"
if unified {
cgroupVersion = "v2"

View File

@ -128,28 +128,118 @@ func init() {
// getAvailableControllers get the available controllers
func getAvailableControllers(exclude map[string]controllerHandler, cgroup2 bool) ([]controller, error) {
if cgroup2 {
return nil, fmt.Errorf("getAvailableControllers not implemented yet for cgroup v2")
controllers := []controller{}
subtreeControl := cgroupRoot + "/cgroup.subtree_control"
// rootless cgroupv2: check available controllers for current user ,systemd or servicescope will inherit
if rootless.IsRootless() {
userSlice, err := getCgroupPathForCurrentProcess()
if err != nil {
return controllers, err
}
//userSlice already contains '/' so not adding here
basePath := cgroupRoot + userSlice
subtreeControl = fmt.Sprintf("%s/cgroup.subtree_control", basePath)
}
subtreeControlBytes, err := ioutil.ReadFile(subtreeControl)
if err != nil {
return nil, errors.Wrapf(err, "failed while reading controllers for cgroup v2 from %q", subtreeControl)
}
for _, controllerName := range strings.Fields(string(subtreeControlBytes)) {
c := controller{
name: controllerName,
symlink: false,
}
controllers = append(controllers, c)
}
return controllers, nil
}
infos, err := ioutil.ReadDir(cgroupRoot)
if err != nil {
return nil, err
}
subsystems, _ := cgroupV1GetAllSubsystems()
controllers := []controller{}
for _, i := range infos {
name := i.Name()
// cgroupv1 and rootless: No subsystem is available: delegation is unsafe.
if rootless.IsRootless() {
return controllers, nil
}
for _, name := range subsystems {
if _, found := exclude[name]; found {
continue
}
isSymLink := false
fileInfo, err := os.Stat(cgroupRoot + "/" + name)
if err != nil {
isSymLink = !fileInfo.IsDir()
}
c := controller{
name: name,
symlink: !i.IsDir(),
symlink: isSymLink,
}
controllers = append(controllers, c)
}
return controllers, nil
}
// GetAvailableControllers get string:bool map of all the available controllers
func GetAvailableControllers(exclude map[string]controllerHandler, cgroup2 bool) ([]string, error) {
availableControllers, err := getAvailableControllers(exclude, cgroup2)
if err != nil {
return nil, err
}
controllerList := []string{}
for _, controller := range availableControllers {
controllerList = append(controllerList, controller.name)
}
return controllerList, nil
}
func cgroupV1GetAllSubsystems() ([]string, error) {
f, err := os.Open("/proc/cgroups")
if err != nil {
return nil, err
}
defer f.Close()
subsystems := []string{}
s := bufio.NewScanner(f)
for s.Scan() {
text := s.Text()
if text[0] != '#' {
parts := strings.Fields(text)
if len(parts) >= 4 && parts[3] != "0" {
subsystems = append(subsystems, parts[0])
}
}
}
if err := s.Err(); err != nil {
return nil, err
}
return subsystems, nil
}
func getCgroupPathForCurrentProcess() (string, error) {
path := fmt.Sprintf("/proc/%d/cgroup", os.Getpid())
f, err := os.Open(path)
if err != nil {
return "", err
}
defer f.Close()
cgroupPath := ""
s := bufio.NewScanner(f)
for s.Scan() {
text := s.Text()
procEntries := strings.SplitN(text, "::", 2)
cgroupPath = procEntries[1]
}
if err := s.Err(); err != nil {
return cgroupPath, err
}
return cgroupPath, nil
}
// getCgroupv1Path is a helper function to get the cgroup v1 path
func (c *CgroupControl) getCgroupv1Path(name string) string {
return filepath.Join(cgroupRoot, name, c.path)

View File

@ -135,4 +135,14 @@ var _ = Describe("Podman Info", func() {
Expect(session.OutputToString()).To(ContainSubstring("false"))
}
})
It("Podman info must contain cgroupControllers with ReleventControllers", func() {
SkipIfRootless("Hard to tell which controllers are going to be enabled for rootless")
SkipIfRootlessCgroupsV1("Disable cgroups not supported on cgroupv1 for rootless users")
session := podmanTest.Podman([]string{"info", "--format", "{{.Host.CgroupControllers}}"})
session.WaitWithDefaultTimeout()
Expect(session).To(Exit(0))
Expect(session.OutputToString()).To(ContainSubstring("memory"))
Expect(session.OutputToString()).To(ContainSubstring("pids"))
})
})