
The rpc only reports one field, i.e. the cgroup driver, to kubelet. Containerd determines the effective cgroup driver by looking at all runtime handlers, starting from the default runtime handler (the rest in alphabetical order), and returning the cgroup driver setting of the first runtime handler that supports one. If no runtime handler supports cgroup driver (i.e. has a config option for it) containerd falls back to auto-detection, returning systemd if systemd is running and cgroupfs otherwise. This patch implements the CRI server side of Kubernetes KEP-4033: https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/4033-group-driver-detection-over-cri Signed-off-by: Markus Lehtonen <markus.lehtonen@intel.com>
128 lines
3.3 KiB
Go
128 lines
3.3 KiB
Go
package fs2
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
|
)
|
|
|
|
func setFreezer(dirPath string, state configs.FreezerState) error {
|
|
var stateStr string
|
|
switch state {
|
|
case configs.Undefined:
|
|
return nil
|
|
case configs.Frozen:
|
|
stateStr = "1"
|
|
case configs.Thawed:
|
|
stateStr = "0"
|
|
default:
|
|
return fmt.Errorf("invalid freezer state %q requested", state)
|
|
}
|
|
|
|
fd, err := cgroups.OpenFile(dirPath, "cgroup.freeze", unix.O_RDWR)
|
|
if err != nil {
|
|
// We can ignore this request as long as the user didn't ask us to
|
|
// freeze the container (since without the freezer cgroup, that's a
|
|
// no-op).
|
|
if state != configs.Frozen {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("freezer not supported: %w", err)
|
|
}
|
|
defer fd.Close()
|
|
|
|
if _, err := fd.WriteString(stateStr); err != nil {
|
|
return err
|
|
}
|
|
// Confirm that the cgroup did actually change states.
|
|
if actualState, err := readFreezer(dirPath, fd); err != nil {
|
|
return err
|
|
} else if actualState != state {
|
|
return fmt.Errorf(`expected "cgroup.freeze" to be in state %q but was in %q`, state, actualState)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getFreezer(dirPath string) (configs.FreezerState, error) {
|
|
fd, err := cgroups.OpenFile(dirPath, "cgroup.freeze", unix.O_RDONLY)
|
|
if err != nil {
|
|
// If the kernel is too old, then we just treat the freezer as being in
|
|
// an "undefined" state.
|
|
if os.IsNotExist(err) || errors.Is(err, unix.ENODEV) {
|
|
err = nil
|
|
}
|
|
return configs.Undefined, err
|
|
}
|
|
defer fd.Close()
|
|
|
|
return readFreezer(dirPath, fd)
|
|
}
|
|
|
|
func readFreezer(dirPath string, fd *os.File) (configs.FreezerState, error) {
|
|
if _, err := fd.Seek(0, 0); err != nil {
|
|
return configs.Undefined, err
|
|
}
|
|
state := make([]byte, 2)
|
|
if _, err := fd.Read(state); err != nil {
|
|
return configs.Undefined, err
|
|
}
|
|
switch string(state) {
|
|
case "0\n":
|
|
return configs.Thawed, nil
|
|
case "1\n":
|
|
return waitFrozen(dirPath)
|
|
default:
|
|
return configs.Undefined, fmt.Errorf(`unknown "cgroup.freeze" state: %q`, state)
|
|
}
|
|
}
|
|
|
|
// waitFrozen polls cgroup.events until it sees "frozen 1" in it.
|
|
func waitFrozen(dirPath string) (configs.FreezerState, error) {
|
|
fd, err := cgroups.OpenFile(dirPath, "cgroup.events", unix.O_RDONLY)
|
|
if err != nil {
|
|
return configs.Undefined, err
|
|
}
|
|
defer fd.Close()
|
|
|
|
// XXX: Simple wait/read/retry is used here. An implementation
|
|
// based on poll(2) or inotify(7) is possible, but it makes the code
|
|
// much more complicated. Maybe address this later.
|
|
const (
|
|
// Perform maxIter with waitTime in between iterations.
|
|
waitTime = 10 * time.Millisecond
|
|
maxIter = 1000
|
|
)
|
|
scanner := bufio.NewScanner(fd)
|
|
for i := 0; scanner.Scan(); {
|
|
if i == maxIter {
|
|
return configs.Undefined, fmt.Errorf("timeout of %s reached waiting for the cgroup to freeze", waitTime*maxIter)
|
|
}
|
|
line := scanner.Text()
|
|
val := strings.TrimPrefix(line, "frozen ")
|
|
if val != line { // got prefix
|
|
if val[0] == '1' {
|
|
return configs.Frozen, nil
|
|
}
|
|
|
|
i++
|
|
// wait, then re-read
|
|
time.Sleep(waitTime)
|
|
_, err := fd.Seek(0, 0)
|
|
if err != nil {
|
|
return configs.Undefined, err
|
|
}
|
|
}
|
|
}
|
|
// Should only reach here either on read error,
|
|
// or if the file does not contain "frozen " line.
|
|
return configs.Undefined, scanner.Err()
|
|
}
|