github.com/apptainer/singularity@v3.1.1+incompatible/internal/pkg/cgroups/cgroups.go (about)

     1  // Copyright (c) 2018, Sylabs Inc. All rights reserved.
     2  // This software is licensed under a 3-clause BSD license. Please consult the
     3  // LICENSE.md file distributed with the sources of this project regarding your
     4  // rights to use or distribute this software.
     5  
     6  package cgroups
     7  
     8  import (
     9  	"encoding/json"
    10  	"fmt"
    11  	"path/filepath"
    12  	"strings"
    13  
    14  	"github.com/containerd/cgroups"
    15  	specs "github.com/opencontainers/runtime-spec/specs-go"
    16  )
    17  
    18  // Manager manage container cgroup resources restriction
    19  type Manager struct {
    20  	Path   string
    21  	Pid    int
    22  	cgroup cgroups.Cgroup
    23  }
    24  
    25  func readSpecFromFile(path string) (spec specs.LinuxResources, err error) {
    26  	conf, err := LoadConfig(path)
    27  	if err != nil {
    28  		return
    29  	}
    30  
    31  	// convert TOML structures to OCI JSON structures
    32  	data, err := json.Marshal(conf)
    33  	if err != nil {
    34  		return
    35  	}
    36  
    37  	if err = json.Unmarshal(data, &spec); err != nil {
    38  		return
    39  	}
    40  
    41  	return
    42  }
    43  
    44  // GetCgroupRootPath returns cgroup root path
    45  func (m *Manager) GetCgroupRootPath() string {
    46  	if m.cgroup == nil {
    47  		return ""
    48  	}
    49  
    50  	for _, sub := range m.cgroup.Subsystems() {
    51  		processes, err := m.cgroup.Processes(sub.Name(), false)
    52  		if len(processes) == 0 || err != nil {
    53  			continue
    54  		}
    55  		process := processes[0]
    56  		cgroupPath := strings.Split(process.Path, string(sub.Name()))[0]
    57  		return filepath.Clean(cgroupPath)
    58  	}
    59  
    60  	return ""
    61  }
    62  
    63  // ApplyFromSpec applies cgroups ressources restriction from OCI specification
    64  func (m *Manager) ApplyFromSpec(spec *specs.LinuxResources) (err error) {
    65  	var path cgroups.Path
    66  
    67  	if !filepath.IsAbs(m.Path) {
    68  		return fmt.Errorf("cgroup path must be an absolute path")
    69  	}
    70  
    71  	path = cgroups.StaticPath(m.Path)
    72  
    73  	s := spec
    74  	if s == nil {
    75  		s = &specs.LinuxResources{}
    76  	}
    77  
    78  	// creates cgroup
    79  	m.cgroup, err = cgroups.New(cgroups.V1, path, s)
    80  	if err != nil {
    81  		return err
    82  	}
    83  
    84  	if err := m.cgroup.Add(cgroups.Process{Pid: m.Pid}); err != nil {
    85  		return err
    86  	}
    87  
    88  	return
    89  }
    90  
    91  // ApplyFromFile applies cgroups resources restriction from TOML configuration
    92  // file
    93  func (m *Manager) ApplyFromFile(path string) error {
    94  	spec, err := readSpecFromFile(path)
    95  	if err != nil {
    96  		return err
    97  	}
    98  	return m.ApplyFromSpec(&spec)
    99  }
   100  
   101  func (m *Manager) loadFromPid() (err error) {
   102  	if m.Pid == 0 {
   103  		return fmt.Errorf("no process ID specified")
   104  	}
   105  	path := cgroups.PidPath(m.Pid)
   106  	m.cgroup, err = cgroups.Load(cgroups.V1, path)
   107  	return
   108  }
   109  
   110  // UpdateFromSpec updates cgroups resources restriction from OCI specification
   111  func (m *Manager) UpdateFromSpec(spec *specs.LinuxResources) (err error) {
   112  	if m.cgroup == nil {
   113  		if err = m.loadFromPid(); err != nil {
   114  			return
   115  		}
   116  	}
   117  	err = m.cgroup.Update(spec)
   118  	return
   119  }
   120  
   121  // UpdateFromFile updates cgroups resources restriction from TOML configuration
   122  func (m *Manager) UpdateFromFile(path string) error {
   123  	spec, err := readSpecFromFile(path)
   124  	if err != nil {
   125  		return err
   126  	}
   127  	return m.UpdateFromSpec(&spec)
   128  }
   129  
   130  // Remove removes ressources restriction for current managed process
   131  func (m *Manager) Remove() error {
   132  	// deletes subgroup
   133  	return m.cgroup.Delete()
   134  }
   135  
   136  // Pause suspends all processes inside the container
   137  func (m *Manager) Pause() error {
   138  	if m.cgroup == nil {
   139  		if err := m.loadFromPid(); err != nil {
   140  			return err
   141  		}
   142  	}
   143  	return m.cgroup.Freeze()
   144  }
   145  
   146  // Resume resumes all processes that have been previously paused
   147  func (m *Manager) Resume() error {
   148  	if m.cgroup == nil {
   149  		if err := m.loadFromPid(); err != nil {
   150  			return err
   151  		}
   152  	}
   153  	return m.cgroup.Thaw()
   154  }