diff options
Diffstat (limited to 'libpod/oci_conmon_linux.go')
-rw-r--r-- | libpod/oci_conmon_linux.go | 316 |
1 files changed, 248 insertions, 68 deletions
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index fde8624b0..0cdfe90e9 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -7,6 +7,7 @@ import ( "bufio" "bytes" "context" + "errors" "fmt" "io" "io/ioutil" @@ -23,8 +24,13 @@ import ( "text/template" "time" + runcconfig "github.com/opencontainers/runc/libcontainer/configs" + "github.com/opencontainers/runc/libcontainer/devices" + "github.com/containers/common/pkg/cgroups" "github.com/containers/common/pkg/config" + "github.com/containers/common/pkg/resize" + cutil "github.com/containers/common/pkg/util" conmonConfig "github.com/containers/conmon/runner/config" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/libpod/logs" @@ -38,7 +44,6 @@ import ( pmount "github.com/containers/storage/pkg/mount" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux/label" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -78,7 +83,7 @@ type ConmonOCIRuntime struct { // libpod. func newConmonOCIRuntime(name string, paths []string, conmonPath string, runtimeFlags []string, runtimeCfg *config.Config) (OCIRuntime, error) { if name == "" { - return nil, errors.Wrapf(define.ErrInvalidArg, "the OCI runtime must be provided a non-empty name") + return nil, fmt.Errorf("the OCI runtime must be provided a non-empty name: %w", define.ErrInvalidArg) } // Make lookup tables for runtime support @@ -122,7 +127,7 @@ func newConmonOCIRuntime(name string, paths []string, conmonPath string, runtime if os.IsNotExist(err) { continue } - return nil, errors.Wrapf(err, "cannot stat OCI runtime %s path", name) + return nil, fmt.Errorf("cannot stat OCI runtime %s path: %w", name, err) } if !stat.Mode().IsRegular() { continue @@ -143,7 +148,7 @@ func newConmonOCIRuntime(name string, paths []string, conmonPath string, runtime } if !foundPath { - return nil, errors.Wrapf(define.ErrInvalidArg, "no valid executable found for OCI runtime %s", name) + return nil, fmt.Errorf("no valid executable found for OCI runtime %s: %w", name, define.ErrInvalidArg) } runtime.exitsDir = filepath.Join(runtime.tmpDir, "exits") @@ -152,7 +157,7 @@ func newConmonOCIRuntime(name string, paths []string, conmonPath string, runtime if err := os.MkdirAll(runtime.exitsDir, 0750); err != nil { // The directory is allowed to exist if !os.IsExist(err) { - return nil, errors.Wrapf(err, "error creating OCI runtime exit files directory") + return nil, fmt.Errorf("error creating OCI runtime exit files directory: %w", err) } } return runtime, nil @@ -228,7 +233,7 @@ func (r *ConmonOCIRuntime) CreateContainer(ctr *Container, restoreOptions *Conta // changes are propagated to the host. err = unix.Mount("/sys", "/sys", "none", unix.MS_REC|unix.MS_SLAVE, "") if err != nil { - return 0, errors.Wrapf(err, "cannot make /sys slave") + return 0, fmt.Errorf("cannot make /sys slave: %w", err) } mounts, err := pmount.GetMounts() @@ -241,7 +246,7 @@ func (r *ConmonOCIRuntime) CreateContainer(ctr *Container, restoreOptions *Conta } err = unix.Unmount(m.Mountpoint, 0) if err != nil && !os.IsNotExist(err) { - return 0, errors.Wrapf(err, "cannot unmount %s", m.Mountpoint) + return 0, fmt.Errorf("cannot unmount %s: %w", m.Mountpoint, err) } } return r.createOCIContainer(ctr, restoreOptions) @@ -264,11 +269,6 @@ func (r *ConmonOCIRuntime) CreateContainer(ctr *Container, restoreOptions *Conta // status, but will instead only check for the existence of the conmon exit file // and update state to stopped if it exists. func (r *ConmonOCIRuntime) UpdateContainerStatus(ctr *Container) error { - exitFile, err := r.ExitFilePath(ctr) - if err != nil { - return err - } - runtimeDir, err := util.GetRuntimeDir() if err != nil { return err @@ -284,17 +284,17 @@ func (r *ConmonOCIRuntime) UpdateContainerStatus(ctr *Container) error { outPipe, err := cmd.StdoutPipe() if err != nil { - return errors.Wrapf(err, "getting stdout pipe") + return fmt.Errorf("getting stdout pipe: %w", err) } errPipe, err := cmd.StderrPipe() if err != nil { - return errors.Wrapf(err, "getting stderr pipe") + return fmt.Errorf("getting stderr pipe: %w", err) } if err := cmd.Start(); err != nil { out, err2 := ioutil.ReadAll(errPipe) if err2 != nil { - return errors.Wrapf(err, "error getting container %s state", ctr.ID()) + return fmt.Errorf("error getting container %s state: %w", ctr.ID(), err) } if strings.Contains(string(out), "does not exist") || strings.Contains(string(out), "No such file") { if err := ctr.removeConmonFiles(); err != nil { @@ -305,7 +305,7 @@ func (r *ConmonOCIRuntime) UpdateContainerStatus(ctr *Container) error { ctr.state.State = define.ContainerStateExited return nil } - return errors.Wrapf(err, "error getting container %s state. stderr/out: %s", ctr.ID(), out) + return fmt.Errorf("error getting container %s state. stderr/out: %s: %w", ctr.ID(), out, err) } defer func() { _ = cmd.Wait() @@ -316,10 +316,10 @@ func (r *ConmonOCIRuntime) UpdateContainerStatus(ctr *Container) error { } out, err := ioutil.ReadAll(outPipe) if err != nil { - return errors.Wrapf(err, "error reading stdout: %s", ctr.ID()) + return fmt.Errorf("error reading stdout: %s: %w", ctr.ID(), err) } if err := json.NewDecoder(bytes.NewBuffer(out)).Decode(state); err != nil { - return errors.Wrapf(err, "error decoding container status for container %s", ctr.ID()) + return fmt.Errorf("error decoding container status for container %s: %w", ctr.ID(), err) } ctr.state.PID = state.Pid @@ -333,29 +333,17 @@ func (r *ConmonOCIRuntime) UpdateContainerStatus(ctr *Container) error { case "stopped": ctr.state.State = define.ContainerStateStopped default: - return errors.Wrapf(define.ErrInternal, "unrecognized status returned by runtime for container %s: %s", - ctr.ID(), state.Status) + return fmt.Errorf("unrecognized status returned by runtime for container %s: %s: %w", + ctr.ID(), state.Status, define.ErrInternal) } // Only grab exit status if we were not already stopped // If we were, it should already be in the database if ctr.state.State == define.ContainerStateStopped && oldState != define.ContainerStateStopped { - var fi os.FileInfo - chWait := make(chan error) - defer close(chWait) - - _, err := WaitForFile(exitFile, chWait, time.Second*5) - if err == nil { - fi, err = os.Stat(exitFile) - } - if err != nil { - ctr.state.ExitCode = -1 - ctr.state.FinishedTime = time.Now() - logrus.Errorf("No exit file for container %s found: %v", ctr.ID(), err) - return nil + if _, err := ctr.Wait(context.Background()); err != nil { + logrus.Errorf("Waiting for container %s to exit: %v", ctr.ID(), err) } - - return ctr.handleExitFile(exitFile, fi) + return nil } // Handle ContainerStateStopping - keep it unless the container @@ -414,7 +402,7 @@ func (r *ConmonOCIRuntime) KillContainer(ctr *Container, signal uint, all bool) if ctr.ensureState(define.ContainerStateStopped, define.ContainerStateExited) { return define.ErrCtrStateInvalid } - return errors.Wrapf(err, "error sending signal to container %s", ctr.ID()) + return fmt.Errorf("error sending signal to container %s: %w", ctr.ID(), err) } return nil @@ -471,7 +459,7 @@ func (r *ConmonOCIRuntime) StopContainer(ctr *Container, timeout uint, all bool) return nil } - return errors.Wrapf(err, "error sending SIGKILL to container %s", ctr.ID()) + return fmt.Errorf("error sending SIGKILL to container %s: %w", ctr.ID(), err) } // Give runtime a few seconds to make it happen @@ -528,7 +516,7 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, req *http.Request, w http. if streams != nil { if !streams.Stdin && !streams.Stdout && !streams.Stderr { - return errors.Wrapf(define.ErrInvalidArg, "must specify at least one stream to attach to") + return fmt.Errorf("must specify at least one stream to attach to: %w", define.ErrInvalidArg) } } @@ -541,7 +529,7 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, req *http.Request, w http. if streamAttach { newConn, err := openUnixSocket(attachSock) if err != nil { - return errors.Wrapf(err, "failed to connect to container's attach socket: %v", attachSock) + return fmt.Errorf("failed to connect to container's attach socket: %v: %w", attachSock, err) } conn = newConn defer func() { @@ -576,12 +564,12 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, req *http.Request, w http. // Alright, let's hijack. hijacker, ok := w.(http.Hijacker) if !ok { - return errors.Errorf("unable to hijack connection") + return fmt.Errorf("unable to hijack connection") } httpCon, httpBuf, err := hijacker.Hijack() if err != nil { - return errors.Wrapf(err, "error hijacking connection") + return fmt.Errorf("error hijacking connection: %w", err) } hijackDone <- true @@ -590,7 +578,7 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, req *http.Request, w http. // Force a flush after the header is written. if err := httpBuf.Flush(); err != nil { - return errors.Wrapf(err, "error flushing HTTP hijack header") + return fmt.Errorf("error flushing HTTP hijack header: %w", err) } defer func() { @@ -705,7 +693,7 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, req *http.Request, w http. // Next, STDIN. Avoid entirely if attachStdin unset. if attachStdin { go func() { - _, err := utils.CopyDetachable(conn, httpBuf, detach) + _, err := cutil.CopyDetachable(conn, httpBuf, detach) logrus.Debugf("STDIN copy completed") stdinChan <- err }() @@ -736,7 +724,8 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, req *http.Request, w http. // isRetryable returns whether the error was caused by a blocked syscall or the // specified operation on a non blocking file descriptor wasn't ready for completion. func isRetryable(err error) bool { - if errno, isErrno := errors.Cause(err).(syscall.Errno); isErrno { + var errno syscall.Errno + if errors.As(err, &errno) { return errno == syscall.EINTR || errno == syscall.EAGAIN } return false @@ -751,15 +740,15 @@ func openControlFile(ctr *Container, parentDir string) (*os.File, error) { return controlFile, nil } if !isRetryable(err) { - return nil, errors.Wrapf(err, "could not open ctl file for terminal resize for container %s", ctr.ID()) + return nil, fmt.Errorf("could not open ctl file for terminal resize for container %s: %w", ctr.ID(), err) } time.Sleep(time.Second / 10) } - return nil, errors.Errorf("timeout waiting for %q", controlPath) + return nil, fmt.Errorf("timeout waiting for %q", controlPath) } // AttachResize resizes the terminal used by the given container. -func (r *ConmonOCIRuntime) AttachResize(ctr *Container, newSize define.TerminalSize) error { +func (r *ConmonOCIRuntime) AttachResize(ctr *Container, newSize resize.TerminalSize) error { controlFile, err := openControlFile(ctr, ctr.bundlePath()) if err != nil { return err @@ -768,7 +757,7 @@ func (r *ConmonOCIRuntime) AttachResize(ctr *Container, newSize define.TerminalS logrus.Debugf("Received a resize event for container %s: %+v", ctr.ID(), newSize) if _, err = fmt.Fprintf(controlFile, "%d %d %d\n", 1, newSize.Height, newSize.Width); err != nil { - return errors.Wrapf(err, "failed to write to ctl file to resize terminal") + return fmt.Errorf("failed to write to ctl file to resize terminal: %w", err) } return nil @@ -876,7 +865,7 @@ func (r *ConmonOCIRuntime) CheckConmonRunning(ctr *Container) (bool, error) { if err == unix.ESRCH { return false, nil } - return false, errors.Wrapf(err, "error pinging container %s conmon with signal 0", ctr.ID()) + return false, fmt.Errorf("error pinging container %s conmon with signal 0: %w", ctr.ID(), err) } return true, nil } @@ -908,7 +897,7 @@ func (r *ConmonOCIRuntime) SupportsKVM() bool { // AttachSocketPath is the path to a single container's attach socket. func (r *ConmonOCIRuntime) AttachSocketPath(ctr *Container) (string, error) { if ctr == nil { - return "", errors.Wrapf(define.ErrInvalidArg, "must provide a valid container to get attach socket path") + return "", fmt.Errorf("must provide a valid container to get attach socket path: %w", define.ErrInvalidArg) } return filepath.Join(ctr.bundlePath(), "attach"), nil @@ -917,7 +906,7 @@ func (r *ConmonOCIRuntime) AttachSocketPath(ctr *Container) (string, error) { // ExitFilePath is the path to a container's exit file. func (r *ConmonOCIRuntime) ExitFilePath(ctr *Container) (string, error) { if ctr == nil { - return "", errors.Wrapf(define.ErrInvalidArg, "must provide a valid container to get exit file path") + return "", fmt.Errorf("must provide a valid container to get exit file path: %w", define.ErrInvalidArg) } return filepath.Join(r.exitsDir, ctr.ID()), nil } @@ -928,11 +917,11 @@ func (r *ConmonOCIRuntime) RuntimeInfo() (*define.ConmonInfo, *define.OCIRuntime conmonPackage := packageVersion(r.conmonPath) runtimeVersion, err := r.getOCIRuntimeVersion() if err != nil { - return nil, nil, errors.Wrapf(err, "error getting version of OCI runtime %s", r.name) + return nil, nil, fmt.Errorf("error getting version of OCI runtime %s: %w", r.name, err) } conmonVersion, err := r.getConmonVersion() if err != nil { - return nil, nil, errors.Wrapf(err, "error getting conmon version") + return nil, nil, fmt.Errorf("error getting conmon version: %w", err) } conmon := define.ConmonInfo{ @@ -1002,7 +991,7 @@ func waitPidStop(pid int, timeout time.Duration) error { return nil case <-time.After(timeout): close(chControl) - return errors.Errorf("given PIDs did not die within timeout") + return fmt.Errorf("given PIDs did not die within timeout") } } @@ -1018,7 +1007,7 @@ func (r *ConmonOCIRuntime) getLogTag(ctr *Container) (string, error) { } tmpl, err := template.New("container").Parse(logTag) if err != nil { - return "", errors.Wrapf(err, "template parsing error %s", logTag) + return "", fmt.Errorf("template parsing error %s: %w", logTag, err) } var b bytes.Buffer err = tmpl.Execute(&b, data) @@ -1039,13 +1028,13 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co parentSyncPipe, childSyncPipe, err := newPipe() if err != nil { - return 0, errors.Wrapf(err, "error creating socket pair") + return 0, fmt.Errorf("error creating socket pair: %w", err) } defer errorhandling.CloseQuiet(parentSyncPipe) childStartPipe, parentStartPipe, err := newPipe() if err != nil { - return 0, errors.Wrapf(err, "error creating socket pair for start pipe") + return 0, fmt.Errorf("error creating socket pair for start pipe: %w", err) } defer errorhandling.CloseQuiet(parentStartPipe) @@ -1166,7 +1155,6 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co }).Debugf("running conmon: %s", r.conmonPath) cmd := exec.Command(r.conmonPath, args...) - cmd.Dir = ctr.bundlePath() cmd.SysProcAttr = &syscall.SysProcAttr{ Setpgid: true, } @@ -1217,12 +1205,12 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co if havePortMapping { ctr.rootlessPortSyncR, ctr.rootlessPortSyncW, err = os.Pipe() if err != nil { - return 0, errors.Wrapf(err, "failed to create rootless port sync pipe") + return 0, fmt.Errorf("failed to create rootless port sync pipe: %w", err) } } ctr.rootlessSlirpSyncR, ctr.rootlessSlirpSyncW, err = os.Pipe() if err != nil { - return 0, errors.Wrapf(err, "failed to create rootless network sync pipe") + return 0, fmt.Errorf("failed to create rootless network sync pipe: %w", err) } } else { if ctr.rootlessSlirpSyncR != nil { @@ -1354,8 +1342,6 @@ func (r *ConmonOCIRuntime) sharedConmonArgs(ctr *Container, cuuid, bundlePath, p logDriverArg = define.NoLogging case define.PassthroughLogging: logDriverArg = define.PassthroughLogging - case define.JSONLogging: - fallthrough //lint:ignore ST1015 the default case has to be here default: //nolint:stylecheck,gocritic // No case here should happen except JSONLogging, but keep this here in case the options are extended @@ -1365,6 +1351,8 @@ func (r *ConmonOCIRuntime) sharedConmonArgs(ctr *Container, cuuid, bundlePath, p // to get here, either a user would specify `--log-driver ""`, or this came from another place in libpod // since the former case is obscure, and the latter case isn't an error, let's silently fallthrough fallthrough + case define.JSONLogging: + fallthrough case define.KubernetesLogging: logDriverArg = fmt.Sprintf("%s:%s", define.KubernetesLogging, logPath) } @@ -1451,9 +1439,14 @@ func (r *ConmonOCIRuntime) moveConmonToCgroupAndSignal(ctr *Container, cmd *exec // TODO: This should be a switch - we are not guaranteed that // there are only 2 valid cgroup managers cgroupParent := ctr.CgroupParent() + cgroupPath := filepath.Join(ctr.config.CgroupParent, "conmon") + Resource := ctr.Spec().Linux.Resources + cgroupResources, err := GetLimits(Resource) + if err != nil { + logrus.StandardLogger().Log(logLevel, "Could not get ctr resources") + } if ctr.CgroupManager() == config.SystemdCgroupsManager { unitName := createUnitName("libpod-conmon", ctr.ID()) - realCgroupParent := cgroupParent splitParent := strings.Split(cgroupParent, "/") if strings.HasSuffix(cgroupParent, ".slice") && len(splitParent) > 1 { @@ -1465,8 +1458,7 @@ func (r *ConmonOCIRuntime) moveConmonToCgroupAndSignal(ctr *Container, cmd *exec logrus.StandardLogger().Logf(logLevel, "Failed to add conmon to systemd sandbox cgroup: %v", err) } } else { - cgroupPath := filepath.Join(ctr.config.CgroupParent, "conmon") - control, err := cgroups.New(cgroupPath, &spec.LinuxResources{}) + control, err := cgroups.New(cgroupPath, &cgroupResources) if err != nil { logrus.StandardLogger().Logf(logLevel, "Failed to add conmon to cgroupfs sandbox cgroup: %v", err) } else if err := control.AddPid(cmd.Process.Pid); err != nil { @@ -1555,7 +1547,7 @@ func readConmonPipeData(runtimeName string, pipe *os.File, ociLog string) (int, } } } - return -1, errors.Wrapf(ss.err, "container create failed (no logs from conmon)") + return -1, fmt.Errorf("container create failed (no logs from conmon): %w", ss.err) } logrus.Debugf("Received: %d", ss.si.Data) if ss.si.Data < 0 { @@ -1572,11 +1564,11 @@ func readConmonPipeData(runtimeName string, pipe *os.File, ociLog string) (int, if ss.si.Message != "" { return ss.si.Data, getOCIRuntimeError(runtimeName, ss.si.Message) } - return ss.si.Data, errors.Wrapf(define.ErrInternal, "container create failed") + return ss.si.Data, fmt.Errorf("container create failed: %w", define.ErrInternal) } data = ss.si.Data case <-time.After(define.ContainerCreateTimeout): - return -1, errors.Wrapf(define.ErrInternal, "container creation timeout") + return -1, fmt.Errorf("container creation timeout: %w", define.ErrInternal) } return data, nil } @@ -1748,3 +1740,191 @@ func httpAttachNonTerminalCopy(container *net.UnixConn, http *bufio.ReadWriter, } } } + +// GetLimits converts spec resource limits to cgroup consumable limits +func GetLimits(resource *spec.LinuxResources) (runcconfig.Resources, error) { + if resource == nil { + resource = &spec.LinuxResources{} + } + final := &runcconfig.Resources{} + devs := []*devices.Rule{} + + // Devices + for _, entry := range resource.Devices { + if entry.Major == nil || entry.Minor == nil { + continue + } + runeType := 'a' + switch entry.Type { + case "b": + runeType = 'b' + case "c": + runeType = 'c' + } + + devs = append(devs, &devices.Rule{ + Type: devices.Type(runeType), + Major: *entry.Major, + Minor: *entry.Minor, + Permissions: devices.Permissions(entry.Access), + Allow: entry.Allow, + }) + } + final.Devices = devs + + // HugepageLimits + pageLimits := []*runcconfig.HugepageLimit{} + for _, entry := range resource.HugepageLimits { + pageLimits = append(pageLimits, &runcconfig.HugepageLimit{ + Pagesize: entry.Pagesize, + Limit: entry.Limit, + }) + } + final.HugetlbLimit = pageLimits + + // Networking + netPriorities := []*runcconfig.IfPrioMap{} + if resource.Network != nil { + for _, entry := range resource.Network.Priorities { + netPriorities = append(netPriorities, &runcconfig.IfPrioMap{ + Interface: entry.Name, + Priority: int64(entry.Priority), + }) + } + } + final.NetPrioIfpriomap = netPriorities + rdma := make(map[string]runcconfig.LinuxRdma) + for name, entry := range resource.Rdma { + rdma[name] = runcconfig.LinuxRdma{HcaHandles: entry.HcaHandles, HcaObjects: entry.HcaObjects} + } + final.Rdma = rdma + + // Memory + if resource.Memory != nil { + if resource.Memory.Limit != nil { + final.Memory = *resource.Memory.Limit + } + if resource.Memory.Reservation != nil { + final.MemoryReservation = *resource.Memory.Reservation + } + if resource.Memory.Swap != nil { + final.MemorySwap = *resource.Memory.Swap + } + if resource.Memory.Swappiness != nil { + final.MemorySwappiness = resource.Memory.Swappiness + } + } + + // CPU + if resource.CPU != nil { + if resource.CPU.Period != nil { + final.CpuPeriod = *resource.CPU.Period + } + if resource.CPU.Quota != nil { + final.CpuQuota = *resource.CPU.Quota + } + if resource.CPU.RealtimePeriod != nil { + final.CpuRtPeriod = *resource.CPU.RealtimePeriod + } + if resource.CPU.RealtimeRuntime != nil { + final.CpuRtRuntime = *resource.CPU.RealtimeRuntime + } + if resource.CPU.Shares != nil { + final.CpuShares = *resource.CPU.Shares + } + final.CpusetCpus = resource.CPU.Cpus + final.CpusetMems = resource.CPU.Mems + } + + // BlkIO + if resource.BlockIO != nil { + if len(resource.BlockIO.ThrottleReadBpsDevice) > 0 { + for _, entry := range resource.BlockIO.ThrottleReadBpsDevice { + throttle := &runcconfig.ThrottleDevice{} + dev := &runcconfig.BlockIODevice{ + Major: entry.Major, + Minor: entry.Minor, + } + throttle.BlockIODevice = *dev + throttle.Rate = entry.Rate + final.BlkioThrottleReadBpsDevice = append(final.BlkioThrottleReadBpsDevice, throttle) + } + } + if len(resource.BlockIO.ThrottleWriteBpsDevice) > 0 { + for _, entry := range resource.BlockIO.ThrottleWriteBpsDevice { + throttle := &runcconfig.ThrottleDevice{} + dev := &runcconfig.BlockIODevice{ + Major: entry.Major, + Minor: entry.Minor, + } + throttle.BlockIODevice = *dev + throttle.Rate = entry.Rate + final.BlkioThrottleWriteBpsDevice = append(final.BlkioThrottleWriteBpsDevice, throttle) + } + } + if len(resource.BlockIO.ThrottleReadIOPSDevice) > 0 { + for _, entry := range resource.BlockIO.ThrottleReadIOPSDevice { + throttle := &runcconfig.ThrottleDevice{} + dev := &runcconfig.BlockIODevice{ + Major: entry.Major, + Minor: entry.Minor, + } + throttle.BlockIODevice = *dev + throttle.Rate = entry.Rate + final.BlkioThrottleReadIOPSDevice = append(final.BlkioThrottleReadIOPSDevice, throttle) + } + } + if len(resource.BlockIO.ThrottleWriteIOPSDevice) > 0 { + for _, entry := range resource.BlockIO.ThrottleWriteIOPSDevice { + throttle := &runcconfig.ThrottleDevice{} + dev := &runcconfig.BlockIODevice{ + Major: entry.Major, + Minor: entry.Minor, + } + throttle.BlockIODevice = *dev + throttle.Rate = entry.Rate + final.BlkioThrottleWriteIOPSDevice = append(final.BlkioThrottleWriteIOPSDevice, throttle) + } + } + if resource.BlockIO.LeafWeight != nil { + final.BlkioLeafWeight = *resource.BlockIO.LeafWeight + } + if resource.BlockIO.Weight != nil { + final.BlkioWeight = *resource.BlockIO.Weight + } + if len(resource.BlockIO.WeightDevice) > 0 { + for _, entry := range resource.BlockIO.WeightDevice { + weight := &runcconfig.WeightDevice{} + dev := &runcconfig.BlockIODevice{ + Major: entry.Major, + Minor: entry.Minor, + } + if entry.Weight != nil { + weight.Weight = *entry.Weight + } + if entry.LeafWeight != nil { + weight.LeafWeight = *entry.LeafWeight + } + weight.BlockIODevice = *dev + final.BlkioWeightDevice = append(final.BlkioWeightDevice, weight) + } + } + } + + // Pids + if resource.Pids != nil { + final.PidsLimit = resource.Pids.Limit + } + + // Networking + if resource.Network != nil { + if resource.Network.ClassID != nil { + final.NetClsClassid = *resource.Network.ClassID + } + } + + // Unified state + final.Unified = resource.Unified + + return *final, nil +} |