// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. package cgroup import ( "context" "path/filepath" "sync" v2 "github.com/containerd/cgroups/v2" "github.com/gitpod-io/gitpod/common-go/log" ) type ProcLimiterV2 struct { limits *v2.Resources cond *sync.Cond } func NewProcLimiterV2(processes int64) (*ProcLimiterV2, error) { return &ProcLimiterV2{ limits: &v2.Resources{ Pids: &v2.Pids{ Max: processes, }, }, cond: sync.NewCond(&sync.Mutex{}), }, nil } func (c *ProcLimiterV2) Name() string { return "proc-limiter-v2" } func (c *ProcLimiterV2) Type() Version { return Version2 } func (c *ProcLimiterV2) Apply(ctx context.Context, opts *PluginOptions) error { update := make(chan struct{}, 1) go func() { defer close(update) for { c.cond.L.Lock() c.cond.Wait() c.cond.L.Unlock() if ctx.Err() != nil { return } update <- struct{}{} } }() go func() { log.WithField("cgroupPath", opts.CgroupPath).Debug("starting proc limiting") _, err := v2.NewManager(opts.BasePath, filepath.Join("/", opts.CgroupPath), c.limits) if err != nil { log.WithError(err).WithField("basePath", opts.BasePath).WithField("cgroupPath", opts.CgroupPath).WithField("limits", c.limits).Error("cannot write proc limits") } for { select { case <-update: _, err := v2.NewManager(opts.BasePath, filepath.Join("/", opts.CgroupPath), c.limits) if err != nil { log.WithError(err).WithField("basePath", opts.BasePath).WithField("cgroupPath", opts.CgroupPath).WithField("limits", c.limits).Error("cannot write proc limits") } case <-ctx.Done(): return } } }() return nil } func (c *ProcLimiterV2) Update(processes int64) { c.cond.L.Lock() defer c.cond.L.Unlock() c.limits = &v2.Resources{ Pids: &v2.Pids{ Max: processes, }, } log.WithField("limits", c.limits.Pids).Info("updating proc cgroups v2 limits") c.cond.Broadcast() }