mirror of
https://github.com/GoogleContainerTools/kaniko
synced 2026-10-18 01:05:28 +02:00
chore: update gcr and docker cred helpers (#2910)
This commit is contained in:
-46
@@ -1,46 +0,0 @@
|
||||
version = "unstable"
|
||||
generator = "gogoctrd"
|
||||
plugins = ["grpc"]
|
||||
|
||||
# Control protoc include paths. Below are usually some good defaults, but feel
|
||||
# free to try it without them if it works for your project.
|
||||
[includes]
|
||||
# Include paths that will be added before all others. Typically, you want to
|
||||
# treat the root of the project as an include, but this may not be necessary.
|
||||
# before = ["."]
|
||||
|
||||
# Paths that should be treated as include roots in relation to the vendor
|
||||
# directory. These will be calculated with the vendor directory nearest the
|
||||
# target package.
|
||||
# vendored = ["github.com/gogo/protobuf"]
|
||||
packages = ["github.com/gogo/protobuf"]
|
||||
|
||||
# Paths that will be added untouched to the end of the includes. We use
|
||||
# `/usr/local/include` to pickup the common install location of protobuf.
|
||||
# This is the default.
|
||||
after = ["/usr/local/include", "/usr/include"]
|
||||
|
||||
# This section maps protobuf imports to Go packages. These will become
|
||||
# `-M` directives in the call to the go protobuf generator.
|
||||
[packages]
|
||||
"gogoproto/gogo.proto" = "github.com/gogo/protobuf/gogoproto"
|
||||
"google/protobuf/any.proto" = "github.com/gogo/protobuf/types"
|
||||
"google/protobuf/descriptor.proto" = "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
|
||||
"google/protobuf/field_mask.proto" = "github.com/gogo/protobuf/types"
|
||||
"google/protobuf/timestamp.proto" = "github.com/gogo/protobuf/types"
|
||||
|
||||
# Aggregrate the API descriptors to lock down API changes.
|
||||
[[descriptors]]
|
||||
prefix = "github.com/containerd/cgroups/stats/v1"
|
||||
target = "stats/v1/metrics.pb.txt"
|
||||
ignore_files = [
|
||||
"google/protobuf/descriptor.proto",
|
||||
"gogoproto/gogo.proto"
|
||||
]
|
||||
[[descriptors]]
|
||||
prefix = "github.com/containerd/cgroups/v2/stats"
|
||||
target = "v2/stats/metrics.pb.txt"
|
||||
ignore_files = [
|
||||
"google/protobuf/descriptor.proto",
|
||||
"gogoproto/gogo.proto"
|
||||
]
|
||||
-3992
File diff suppressed because it is too large
Load Diff
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Generated
Vendored
+2
@@ -22,3 +22,5 @@ cgutil:
|
||||
|
||||
proto:
|
||||
protobuild --quiet ${PACKAGES}
|
||||
# Keep them Go-idiomatic and backward-compatible with the gogo/protobuf era.
|
||||
go-fix-acronym -w -a '(Cpu|Tcp|Rss)' $(shell find cgroup1/stats/ cgroup2/stats/ -name '*.pb.go')
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
version = "2"
|
||||
generators = ["go"]
|
||||
|
||||
# Control protoc include paths. Below are usually some good defaults, but feel
|
||||
# free to try it without them if it works for your project.
|
||||
[includes]
|
||||
# Include paths that will be added before all others. Typically, you want to
|
||||
# treat the root of the project as an include, but this may not be necessary.
|
||||
# before = ["."]
|
||||
|
||||
# Paths that will be added untouched to the end of the includes. We use
|
||||
# `/usr/local/include` to pickup the common install location of protobuf.
|
||||
# This is the default.
|
||||
after = ["/usr/local/include", "/usr/include"]
|
||||
|
||||
# Aggregrate the API descriptors to lock down API changes.
|
||||
[[descriptors]]
|
||||
prefix = "github.com/containerd/cgroups/cgroup1/stats"
|
||||
target = "cgroup1/stats/metrics.pb.txt"
|
||||
ignore_files = [
|
||||
"google/protobuf/descriptor.proto",
|
||||
]
|
||||
[[descriptors]]
|
||||
prefix = "github.com/containerd/cgroups/cgroup2/stats"
|
||||
target = "cgroup2/stats/metrics.pb.txt"
|
||||
ignore_files = [
|
||||
"google/protobuf/descriptor.proto",
|
||||
]
|
||||
|
||||
[parameters.go]
|
||||
paths = "source_relative"
|
||||
Generated
Vendored
+48
-14
@@ -25,7 +25,7 @@ uses the v1 implementation of cgroups.
|
||||
|
||||
```go
|
||||
shares := uint64(100)
|
||||
control, err := cgroups.New(cgroups.V1, cgroups.StaticPath("/test"), &specs.LinuxResources{
|
||||
control, err := cgroup1.New(cgroup1.StaticPath("/test"), &specs.LinuxResources{
|
||||
CPU: &specs.LinuxCPU{
|
||||
Shares: &shares,
|
||||
},
|
||||
@@ -37,7 +37,7 @@ defer control.Delete()
|
||||
|
||||
|
||||
```go
|
||||
control, err := cgroups.New(cgroups.Systemd, cgroups.Slice("system.slice", "runc-test"), &specs.LinuxResources{
|
||||
control, err := cgroup1.New(cgroup1.Systemd, cgroup1.Slice("system.slice", "runc-test"), &specs.LinuxResources{
|
||||
CPU: &specs.CPU{
|
||||
Shares: &shares,
|
||||
},
|
||||
@@ -48,13 +48,13 @@ control, err := cgroups.New(cgroups.Systemd, cgroups.Slice("system.slice", "runc
|
||||
### Load an existing cgroup
|
||||
|
||||
```go
|
||||
control, err = cgroups.Load(cgroups.V1, cgroups.StaticPath("/test"))
|
||||
control, err = cgroup1.Load(cgroup1.Default, cgroups.StaticPath("/test"))
|
||||
```
|
||||
|
||||
### Add a process to the cgroup
|
||||
|
||||
```go
|
||||
if err := control.Add(cgroups.Process{Pid:1234}); err != nil {
|
||||
if err := control.Add(cgroup1.Process{Pid:1234}); err != nil {
|
||||
}
|
||||
```
|
||||
|
||||
@@ -84,7 +84,7 @@ if err := control.Thaw(); err != nil {
|
||||
### List all processes in the cgroup or recursively
|
||||
|
||||
```go
|
||||
processes, err := control.Processes(cgroups.Devices, recursive)
|
||||
processes, err := control.Processes(cgroup1.Devices, recursive)
|
||||
```
|
||||
|
||||
### Get Stats on the cgroup
|
||||
@@ -95,7 +95,7 @@ stats, err := control.Stat()
|
||||
|
||||
By adding `cgroups.IgnoreNotExist` all non-existent files will be ignored, e.g. swap memory stats without swap enabled
|
||||
```go
|
||||
stats, err := control.Stat(cgroups.IgnoreNotExist)
|
||||
stats, err := control.Stat(cgroup1.IgnoreNotExist)
|
||||
```
|
||||
|
||||
### Move process across cgroups
|
||||
@@ -117,19 +117,19 @@ subCgroup, err := control.New("child", resources)
|
||||
This allows you to get notified by an eventfd for v1 memory cgroups events.
|
||||
|
||||
```go
|
||||
event := cgroups.MemoryThresholdEvent(50 * 1024 * 1024, false)
|
||||
event := cgroup1.MemoryThresholdEvent(50 * 1024 * 1024, false)
|
||||
efd, err := control.RegisterMemoryEvent(event)
|
||||
```
|
||||
|
||||
```go
|
||||
event := cgroups.MemoryPressureEvent(cgroups.MediumPressure, cgroups.DefaultMode)
|
||||
event := cgroup1.MemoryPressureEvent(cgroup1.MediumPressure, cgroup1.DefaultMode)
|
||||
efd, err := control.RegisterMemoryEvent(event)
|
||||
```
|
||||
|
||||
```go
|
||||
efd, err := control.OOMEventFD()
|
||||
// or by using RegisterMemoryEvent
|
||||
event := cgroups.OOMEvent()
|
||||
event := cgroup1.OOMEvent()
|
||||
efd, err := control.RegisterMemoryEvent(event)
|
||||
```
|
||||
|
||||
@@ -153,14 +153,14 @@ so the resulting slice would be located here on disk:
|
||||
|
||||
```go
|
||||
import (
|
||||
cgroupsv2 "github.com/containerd/cgroups/v2"
|
||||
"github.com/containerd/cgroups/v3/cgroup2"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
res := cgroupsv2.Resources{}
|
||||
res := cgroup2.Resources{}
|
||||
// dummy PID of -1 is used for creating a "general slice" to be used as a parent cgroup.
|
||||
// see https://github.com/containerd/cgroups/blob/1df78138f1e1e6ee593db155c6b369466f577651/v2/manager.go#L732-L735
|
||||
m, err := cgroupsv2.NewSystemd("/", "my-cgroup-abc.slice", -1, &res)
|
||||
m, err := cgroup2.NewSystemd("/", "my-cgroup-abc.slice", -1, &res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -169,7 +169,7 @@ if err != nil {
|
||||
### Load an existing cgroup
|
||||
|
||||
```go
|
||||
m, err := cgroupsv2.LoadSystemd("/", "my-cgroup-abc.slice")
|
||||
m, err := cgroup2.LoadSystemd("/", "my-cgroup-abc.slice")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -178,7 +178,7 @@ if err != nil {
|
||||
### Delete a cgroup
|
||||
|
||||
```go
|
||||
m, err := cgroupsv2.LoadSystemd("/", "my-cgroup-abc.slice")
|
||||
m, err := cgroup2.LoadSystemd("/", "my-cgroup-abc.slice")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -188,6 +188,40 @@ if err != nil {
|
||||
}
|
||||
```
|
||||
|
||||
### Kill all processes in a cgroup
|
||||
|
||||
```go
|
||||
m, err := cgroup2.LoadSystemd("/", "my-cgroup-abc.slice")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = m.Kill()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Get and set cgroup type
|
||||
```go
|
||||
m, err := cgroup2.LoadSystemd("/", "my-cgroup-abc.slice")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// https://www.kernel.org/doc/html/v5.0/admin-guide/cgroup-v2.html#threads
|
||||
cgType, err := m.GetType()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(cgType)
|
||||
|
||||
err = m.SetType(cgroup2.Threaded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
### Attention
|
||||
|
||||
All static path should not include `/sys/fs/cgroup/` prefix, it should start with your own cgroups name
|
||||
Generated
Vendored
+4
-4
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -25,7 +25,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
v1 "github.com/containerd/cgroups/stats/v1"
|
||||
v1 "github.com/containerd/cgroups/v3/cgroup1/stats"
|
||||
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
@@ -71,7 +72,7 @@ func (b *blkioController) Create(path string, resources *specs.LinuxResources) e
|
||||
}
|
||||
for _, t := range createBlkioSettings(resources.BlockIO) {
|
||||
if t.value != nil {
|
||||
if err := retryingWriteFile(
|
||||
if err := os.WriteFile(
|
||||
filepath.Join(b.Path(path), "blkio."+t.name),
|
||||
t.format(t.value),
|
||||
defaultFilePerm,
|
||||
@@ -330,7 +331,6 @@ type deviceKey struct {
|
||||
// keyed by major and minor number. Since devices may be mapped multiple times,
|
||||
// we err on taking the first occurrence.
|
||||
func getDevices(r io.Reader) (map[deviceKey]string, error) {
|
||||
|
||||
var (
|
||||
s = bufio.NewScanner(r)
|
||||
devices = make(map[deviceKey]string)
|
||||
Generated
Vendored
+40
-8
@@ -14,31 +14,34 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
v1 "github.com/containerd/cgroups/stats/v1"
|
||||
v1 "github.com/containerd/cgroups/v3/cgroup1/stats"
|
||||
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// New returns a new control via the cgroup cgroups interface
|
||||
func New(hierarchy Hierarchy, path Path, resources *specs.LinuxResources, opts ...InitOpts) (Cgroup, error) {
|
||||
func New(path Path, resources *specs.LinuxResources, opts ...InitOpts) (Cgroup, error) {
|
||||
config := newInitConfig()
|
||||
for _, o := range opts {
|
||||
if err := o(config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
subsystems, err := hierarchy()
|
||||
subsystems, err := config.hierarchy()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -68,7 +71,7 @@ func New(hierarchy Hierarchy, path Path, resources *specs.LinuxResources, opts .
|
||||
|
||||
// Load will load an existing cgroup and allow it to be controlled
|
||||
// All static path should not include `/sys/fs/cgroup/` prefix, it should start with your own cgroups name
|
||||
func Load(hierarchy Hierarchy, path Path, opts ...InitOpts) (Cgroup, error) {
|
||||
func Load(path Path, opts ...InitOpts) (Cgroup, error) {
|
||||
config := newInitConfig()
|
||||
for _, o := range opts {
|
||||
if err := o(config); err != nil {
|
||||
@@ -76,7 +79,7 @@ func Load(hierarchy Hierarchy, path Path, opts ...InitOpts) (Cgroup, error) {
|
||||
}
|
||||
}
|
||||
var activeSubsystems []Subsystem
|
||||
subsystems, err := hierarchy()
|
||||
subsystems, err := config.hierarchy()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -155,7 +158,7 @@ func (c *cgroup) subsystemsFilter(subsystems ...Name) []Subsystem {
|
||||
return c.subsystems
|
||||
}
|
||||
|
||||
var filteredSubsystems = []Subsystem{}
|
||||
filteredSubsystems := []Subsystem{}
|
||||
for _, s := range c.subsystems {
|
||||
for _, f := range subsystems {
|
||||
if s.Name() == f {
|
||||
@@ -193,6 +196,31 @@ func (c *cgroup) AddTask(process Process, subsystems ...Name) error {
|
||||
return c.add(process, cgroupTasks, subsystems...)
|
||||
}
|
||||
|
||||
// writeCgroupsProcs writes to the file, but retries on EINVAL.
|
||||
func writeCgroupProcs(path string, content []byte, perm fs.FileMode) error {
|
||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
_, err = f.Write(content)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
// If the process's associated task's state is TASK_NEW, the kernel
|
||||
// returns EINVAL. The function will retry on the error like runc.
|
||||
// https://github.com/torvalds/linux/blob/v6.0/kernel/sched/core.c#L10308-L10337
|
||||
// https://github.com/opencontainers/runc/pull/1950
|
||||
if !errors.Is(err, syscall.EINVAL) {
|
||||
return err
|
||||
}
|
||||
time.Sleep(30 * time.Millisecond)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *cgroup) add(process Process, pType procType, subsystems ...Name) error {
|
||||
if process.Pid <= 0 {
|
||||
return ErrInvalidPid
|
||||
@@ -207,7 +235,7 @@ func (c *cgroup) add(process Process, pType procType, subsystems ...Name) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = retryingWriteFile(
|
||||
err = writeCgroupProcs(
|
||||
filepath.Join(s.Path(p), pType),
|
||||
[]byte(strconv.Itoa(process.Pid)),
|
||||
defaultFilePerm,
|
||||
@@ -231,6 +259,10 @@ func (c *cgroup) Delete() error {
|
||||
// kernel prevents cgroups with running process from being removed, check the tree is empty
|
||||
procs, err := c.processes(s.Name(), true, cgroupProcs)
|
||||
if err != nil {
|
||||
// if the control group does not exist within a subsystem, then proceed to the next subsystem
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
if len(procs) > 0 {
|
||||
Generated
Vendored
+3
-3
@@ -14,12 +14,12 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
v1 "github.com/containerd/cgroups/stats/v1"
|
||||
v1 "github.com/containerd/cgroups/v3/cgroup1/stats"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
@@ -28,7 +28,7 @@ type procType = string
|
||||
const (
|
||||
cgroupProcs procType = "cgroup.procs"
|
||||
cgroupTasks procType = "tasks"
|
||||
defaultDirPerm = 0755
|
||||
defaultDirPerm = 0o755
|
||||
)
|
||||
|
||||
// defaultFilePerm is a var so that the test framework can change the filemode
|
||||
vendor/github.com/containerd/cgroups/cpu.go → vendor/github.com/containerd/cgroups/v3/cgroup1/cpu.go
Generated
Vendored
+3
-3
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
v1 "github.com/containerd/cgroups/stats/v1"
|
||||
v1 "github.com/containerd/cgroups/v3/cgroup1/stats"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
@@ -82,7 +82,7 @@ func (c *cpuController) Create(path string, resources *specs.LinuxResources) err
|
||||
value = []byte(strconv.FormatInt(*t.ivalue, 10))
|
||||
}
|
||||
if value != nil {
|
||||
if err := retryingWriteFile(
|
||||
if err := os.WriteFile(
|
||||
filepath.Join(c.Path(path), "cpu."+t.name),
|
||||
value,
|
||||
defaultFilePerm,
|
||||
Generated
Vendored
+2
-2
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
v1 "github.com/containerd/cgroups/stats/v1"
|
||||
v1 "github.com/containerd/cgroups/v3/cgroup1/stats"
|
||||
)
|
||||
|
||||
const nanosecondsInSecond = 1000000000
|
||||
Generated
Vendored
+4
-4
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -68,7 +68,7 @@ func (c *cpusetController) Create(path string, resources *specs.LinuxResources)
|
||||
},
|
||||
} {
|
||||
if t.value != "" {
|
||||
if err := retryingWriteFile(
|
||||
if err := os.WriteFile(
|
||||
filepath.Join(c.Path(path), "cpuset."+t.name),
|
||||
[]byte(t.value),
|
||||
defaultFilePerm,
|
||||
@@ -133,7 +133,7 @@ func (c *cpusetController) copyIfNeeded(current, parent string) error {
|
||||
return err
|
||||
}
|
||||
if isEmpty(currentCpus) {
|
||||
if err := retryingWriteFile(
|
||||
if err := os.WriteFile(
|
||||
filepath.Join(current, "cpuset.cpus"),
|
||||
parentCpus,
|
||||
defaultFilePerm,
|
||||
@@ -142,7 +142,7 @@ func (c *cpusetController) copyIfNeeded(current, parent string) error {
|
||||
}
|
||||
}
|
||||
if isEmpty(currentMems) {
|
||||
if err := retryingWriteFile(
|
||||
if err := os.WriteFile(
|
||||
filepath.Join(current, "cpuset.mems"),
|
||||
parentMems,
|
||||
defaultFilePerm,
|
||||
Generated
Vendored
+2
-2
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -60,7 +60,7 @@ func (d *devicesController) Create(path string, resources *specs.LinuxResources)
|
||||
if device.Type == "" {
|
||||
device.Type = "a"
|
||||
}
|
||||
if err := retryingWriteFile(
|
||||
if err := os.WriteFile(
|
||||
filepath.Join(d.Path(path), file),
|
||||
[]byte(deviceString(device)),
|
||||
defaultFilePerm,
|
||||
Generated
Vendored
+1
-1
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
Generated
Vendored
+2
-2
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -50,7 +50,7 @@ func (f *freezerController) Thaw(path string) error {
|
||||
}
|
||||
|
||||
func (f *freezerController) changeState(path string, state State) error {
|
||||
return retryingWriteFile(
|
||||
return os.WriteFile(
|
||||
filepath.Join(f.root, path, "freezer.state"),
|
||||
[]byte(strings.ToUpper(string(state))),
|
||||
defaultFilePerm,
|
||||
Generated
Vendored
+1
-1
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
// Hierarchy enables both unified and split hierarchy for cgroups
|
||||
type Hierarchy func() ([]Subsystem, error)
|
||||
Generated
Vendored
+3
-3
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
v1 "github.com/containerd/cgroups/stats/v1"
|
||||
v1 "github.com/containerd/cgroups/v3/cgroup1/stats"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
@@ -56,7 +56,7 @@ func (h *hugetlbController) Create(path string, resources *specs.LinuxResources)
|
||||
return err
|
||||
}
|
||||
for _, limit := range resources.HugepageLimits {
|
||||
if err := retryingWriteFile(
|
||||
if err := os.WriteFile(
|
||||
filepath.Join(h.Path(path), strings.Join([]string{"hugetlb", limit.Pagesize, "limit_in_bytes"}, ".")),
|
||||
[]byte(strconv.FormatUint(limit.Limit, 10)),
|
||||
defaultFilePerm,
|
||||
Generated
Vendored
+37
-37
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
v1 "github.com/containerd/cgroups/stats/v1"
|
||||
v1 "github.com/containerd/cgroups/v3/cgroup1/stats"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
@@ -43,7 +43,7 @@ type memoryThresholdEvent struct {
|
||||
swap bool
|
||||
}
|
||||
|
||||
// MemoryThresholdEvent returns a new memory threshold event to be used with RegisterMemoryEvent.
|
||||
// MemoryThresholdEvent returns a new [MemoryEvent] representing the memory threshold set.
|
||||
// If swap is true, the event will be registered using memory.memsw.usage_in_bytes
|
||||
func MemoryThresholdEvent(threshold uint64, swap bool) MemoryEvent {
|
||||
return &memoryThresholdEvent{
|
||||
@@ -83,7 +83,7 @@ type memoryPressureEvent struct {
|
||||
hierarchy EventNotificationMode
|
||||
}
|
||||
|
||||
// MemoryPressureEvent returns a new memory pressure event to be used with RegisterMemoryEvent.
|
||||
// MemoryPressureEvent returns a new [MemoryEvent] representing the memory pressure set.
|
||||
func MemoryPressureEvent(pressureLevel MemoryPressureLevel, hierarchy EventNotificationMode) MemoryEvent {
|
||||
return &memoryPressureEvent{
|
||||
pressureLevel,
|
||||
@@ -104,22 +104,22 @@ func (m *memoryPressureEvent) EventFile() string {
|
||||
type MemoryPressureLevel string
|
||||
|
||||
// The three memory pressure levels are as follows.
|
||||
// - The "low" level means that the system is reclaiming memory for new
|
||||
// allocations. Monitoring this reclaiming activity might be useful for
|
||||
// maintaining cache level. Upon notification, the program (typically
|
||||
// "Activity Manager") might analyze vmstat and act in advance (i.e.
|
||||
// prematurely shutdown unimportant services).
|
||||
// - The "medium" level means that the system is experiencing medium memory
|
||||
// pressure, the system might be making swap, paging out active file caches,
|
||||
// etc. Upon this event applications may decide to further analyze
|
||||
// vmstat/zoneinfo/memcg or internal memory usage statistics and free any
|
||||
// resources that can be easily reconstructed or re-read from a disk.
|
||||
// - The "critical" level means that the system is actively thrashing, it is
|
||||
// about to out of memory (OOM) or even the in-kernel OOM killer is on its
|
||||
// way to trigger. Applications should do whatever they can to help the
|
||||
// system. It might be too late to consult with vmstat or any other
|
||||
// statistics, so it is advisable to take an immediate action.
|
||||
// "https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt" Section 11
|
||||
// - The "low" level means that the system is reclaiming memory for new
|
||||
// allocations. Monitoring this reclaiming activity might be useful for
|
||||
// maintaining cache level. Upon notification, the program (typically
|
||||
// "Activity Manager") might analyze vmstat and act in advance (i.e.
|
||||
// prematurely shutdown unimportant services).
|
||||
// - The "medium" level means that the system is experiencing medium memory
|
||||
// pressure, the system might be making swap, paging out active file caches,
|
||||
// etc. Upon this event applications may decide to further analyze
|
||||
// vmstat/zoneinfo/memcg or internal memory usage statistics and free any
|
||||
// resources that can be easily reconstructed or re-read from a disk.
|
||||
// - The "critical" level means that the system is actively thrashing, it is
|
||||
// about to out of memory (OOM) or even the in-kernel OOM killer is on its
|
||||
// way to trigger. Applications should do whatever they can to help the
|
||||
// system. It might be too late to consult with vmstat or any other
|
||||
// statistics, so it is advisable to take an immediate action.
|
||||
// "https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt" Section 11
|
||||
const (
|
||||
LowPressure MemoryPressureLevel = "low"
|
||||
MediumPressure MemoryPressureLevel = "medium"
|
||||
@@ -131,21 +131,21 @@ const (
|
||||
type EventNotificationMode string
|
||||
|
||||
// There are three optional modes that specify different propagation behavior:
|
||||
// - "default": this is the default behavior specified above. This mode is the
|
||||
// same as omitting the optional mode parameter, preserved by backwards
|
||||
// compatibility.
|
||||
// - "hierarchy": events always propagate up to the root, similar to the default
|
||||
// behavior, except that propagation continues regardless of whether there are
|
||||
// event listeners at each level, with the "hierarchy" mode. In the above
|
||||
// example, groups A, B, and C will receive notification of memory pressure.
|
||||
// - "local": events are pass-through, i.e. they only receive notifications when
|
||||
// memory pressure is experienced in the memcg for which the notification is
|
||||
// registered. In the above example, group C will receive notification if
|
||||
// registered for "local" notification and the group experiences memory
|
||||
// pressure. However, group B will never receive notification, regardless if
|
||||
// there is an event listener for group C or not, if group B is registered for
|
||||
// local notification.
|
||||
// "https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt" Section 11
|
||||
// - "default": this is the default behavior specified above. This mode is the
|
||||
// same as omitting the optional mode parameter, preserved by backwards
|
||||
// compatibility.
|
||||
// - "hierarchy": events always propagate up to the root, similar to the default
|
||||
// behavior, except that propagation continues regardless of whether there are
|
||||
// event listeners at each level, with the "hierarchy" mode. In the above
|
||||
// example, groups A, B, and C will receive notification of memory pressure.
|
||||
// - "local": events are pass-through, i.e. they only receive notifications when
|
||||
// memory pressure is experienced in the memcg for which the notification is
|
||||
// registered. In the above example, group C will receive notification if
|
||||
// registered for "local" notification and the group experiences memory
|
||||
// pressure. However, group B will never receive notification, regardless if
|
||||
// there is an event listener for group C or not, if group B is registered for
|
||||
// local notification.
|
||||
// "https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt" Section 11
|
||||
const (
|
||||
DefaultMode EventNotificationMode = "default"
|
||||
LocalMode EventNotificationMode = "local"
|
||||
@@ -394,7 +394,7 @@ func (m *memoryController) parseOomControlStats(r io.Reader, stat *v1.MemoryOomC
|
||||
func (m *memoryController) set(path string, settings []memorySettings) error {
|
||||
for _, t := range settings {
|
||||
if t.value != nil {
|
||||
if err := retryingWriteFile(
|
||||
if err := os.WriteFile(
|
||||
filepath.Join(m.Path(path), "memory."+t.name),
|
||||
[]byte(strconv.FormatInt(*t.value, 10)),
|
||||
defaultFilePerm,
|
||||
@@ -472,7 +472,7 @@ func (m *memoryController) memoryEvent(path string, event MemoryEvent) (uintptr,
|
||||
defer evtFile.Close()
|
||||
data := fmt.Sprintf("%d %d %s", efd, evtFile.Fd(), event.Arg())
|
||||
evctlPath := filepath.Join(root, "cgroup.event_control")
|
||||
if err := retryingWriteFile(evctlPath, []byte(data), 0700); err != nil {
|
||||
if err := os.WriteFile(evctlPath, []byte(data), 0o700); err != nil {
|
||||
unix.Close(efd)
|
||||
return 0, err
|
||||
}
|
||||
Generated
Vendored
+1
-1
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
Generated
Vendored
+2
-2
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -47,7 +47,7 @@ func (n *netclsController) Create(path string, resources *specs.LinuxResources)
|
||||
return err
|
||||
}
|
||||
if resources.Network != nil && resources.Network.ClassID != nil && *resources.Network.ClassID > 0 {
|
||||
return retryingWriteFile(
|
||||
return os.WriteFile(
|
||||
filepath.Join(n.Path(path), "net_cls.classid"),
|
||||
[]byte(strconv.FormatUint(uint64(*resources.Network.ClassID), 10)),
|
||||
defaultFilePerm,
|
||||
Generated
Vendored
+2
-2
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -48,7 +48,7 @@ func (n *netprioController) Create(path string, resources *specs.LinuxResources)
|
||||
}
|
||||
if resources.Network != nil {
|
||||
for _, prio := range resources.Network.Priorities {
|
||||
if err := retryingWriteFile(
|
||||
if err := os.WriteFile(
|
||||
filepath.Join(n.Path(path), "net_prio.ifpriomap"),
|
||||
formatPrio(prio.Name, prio.Priority),
|
||||
defaultFilePerm,
|
||||
Generated
Vendored
+12
-1
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -36,11 +36,13 @@ type InitOpts func(*InitConfig) error
|
||||
type InitConfig struct {
|
||||
// InitCheck can be used to check initialization errors from the subsystem
|
||||
InitCheck InitCheck
|
||||
hierarchy Hierarchy
|
||||
}
|
||||
|
||||
func newInitConfig() *InitConfig {
|
||||
return &InitConfig{
|
||||
InitCheck: RequireDevices,
|
||||
hierarchy: Default,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,3 +61,12 @@ func RequireDevices(s Subsystem, _ Path, _ error) error {
|
||||
}
|
||||
return ErrIgnoreSubsystem
|
||||
}
|
||||
|
||||
// WithHiearchy sets a list of cgroup subsystems.
|
||||
// The default list is coming from /proc/self/mountinfo.
|
||||
func WithHiearchy(h Hierarchy) InitOpts {
|
||||
return func(c *InitConfig) error {
|
||||
c.hierarchy = h
|
||||
return nil
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+1
-1
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
Generated
Vendored
+1
-1
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
Generated
Vendored
+4
-11
@@ -14,15 +14,14 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
v1 "github.com/containerd/cgroups/stats/v1"
|
||||
v1 "github.com/containerd/cgroups/v3/cgroup1/stats"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
@@ -49,7 +48,7 @@ func (p *pidsController) Create(path string, resources *specs.LinuxResources) er
|
||||
return err
|
||||
}
|
||||
if resources.Pids != nil && resources.Pids.Limit > 0 {
|
||||
return retryingWriteFile(
|
||||
return os.WriteFile(
|
||||
filepath.Join(p.Path(path), "pids.max"),
|
||||
[]byte(strconv.FormatInt(resources.Pids.Limit, 10)),
|
||||
defaultFilePerm,
|
||||
@@ -67,16 +66,10 @@ func (p *pidsController) Stat(path string, stats *v1.Metrics) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var max uint64
|
||||
maxData, err := os.ReadFile(filepath.Join(p.Path(path), "pids.max"))
|
||||
max, err := readUint(filepath.Join(p.Path(path), "pids.max"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maxS := strings.TrimSpace(string(maxData)); maxS != "max" {
|
||||
if max, err = parseUint(maxS, 10, 64); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
stats.Pids = &v1.PidsStat{
|
||||
Current: current,
|
||||
Limit: max,
|
||||
Generated
Vendored
+3
-4
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"math"
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
v1 "github.com/containerd/cgroups/stats/v1"
|
||||
v1 "github.com/containerd/cgroups/v3/cgroup1/stats"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
@@ -67,7 +67,7 @@ func (p *rdmaController) Create(path string, resources *specs.LinuxResources) er
|
||||
for device, limit := range resources.Rdma {
|
||||
if device != "" && (limit.HcaHandles != nil || limit.HcaObjects != nil) {
|
||||
limit := limit
|
||||
return retryingWriteFile(
|
||||
return os.WriteFile(
|
||||
filepath.Join(p.Path(path), "rdma.max"),
|
||||
[]byte(createCmdString(device, &limit)),
|
||||
defaultFilePerm,
|
||||
@@ -124,7 +124,6 @@ func toRdmaEntry(strEntries []string) []*v1.RdmaEntry {
|
||||
}
|
||||
|
||||
func (p *rdmaController) Stat(path string, stats *v1.Metrics) error {
|
||||
|
||||
currentData, err := os.ReadFile(filepath.Join(p.Path(path), "rdma.current"))
|
||||
if err != nil {
|
||||
return err
|
||||
Generated
Vendored
+1
-1
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
// State is a type that represents the state of the current cgroup
|
||||
type State string
|
||||
Generated
Vendored
+1959
File diff suppressed because it is too large
Load Diff
+771
@@ -0,0 +1,771 @@
|
||||
file {
|
||||
name: "github.com/containerd/cgroups/cgroup1/stats/metrics.proto"
|
||||
package: "io.containerd.cgroups.v1"
|
||||
message_type {
|
||||
name: "Metrics"
|
||||
field {
|
||||
name: "hugetlb"
|
||||
number: 1
|
||||
label: LABEL_REPEATED
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.HugetlbStat"
|
||||
json_name: "hugetlb"
|
||||
}
|
||||
field {
|
||||
name: "pids"
|
||||
number: 2
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.PidsStat"
|
||||
json_name: "pids"
|
||||
}
|
||||
field {
|
||||
name: "cpu"
|
||||
number: 3
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.CPUStat"
|
||||
json_name: "cpu"
|
||||
}
|
||||
field {
|
||||
name: "memory"
|
||||
number: 4
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.MemoryStat"
|
||||
json_name: "memory"
|
||||
}
|
||||
field {
|
||||
name: "blkio"
|
||||
number: 5
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.BlkIOStat"
|
||||
json_name: "blkio"
|
||||
}
|
||||
field {
|
||||
name: "rdma"
|
||||
number: 6
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.RdmaStat"
|
||||
json_name: "rdma"
|
||||
}
|
||||
field {
|
||||
name: "network"
|
||||
number: 7
|
||||
label: LABEL_REPEATED
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.NetworkStat"
|
||||
json_name: "network"
|
||||
}
|
||||
field {
|
||||
name: "cgroup_stats"
|
||||
number: 8
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.CgroupStats"
|
||||
json_name: "cgroupStats"
|
||||
}
|
||||
field {
|
||||
name: "memory_oom_control"
|
||||
number: 9
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.MemoryOomControl"
|
||||
json_name: "memoryOomControl"
|
||||
}
|
||||
}
|
||||
message_type {
|
||||
name: "HugetlbStat"
|
||||
field {
|
||||
name: "usage"
|
||||
number: 1
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "usage"
|
||||
}
|
||||
field {
|
||||
name: "max"
|
||||
number: 2
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "max"
|
||||
}
|
||||
field {
|
||||
name: "failcnt"
|
||||
number: 3
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "failcnt"
|
||||
}
|
||||
field {
|
||||
name: "pagesize"
|
||||
number: 4
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_STRING
|
||||
json_name: "pagesize"
|
||||
}
|
||||
}
|
||||
message_type {
|
||||
name: "PidsStat"
|
||||
field {
|
||||
name: "current"
|
||||
number: 1
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "current"
|
||||
}
|
||||
field {
|
||||
name: "limit"
|
||||
number: 2
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "limit"
|
||||
}
|
||||
}
|
||||
message_type {
|
||||
name: "CPUStat"
|
||||
field {
|
||||
name: "usage"
|
||||
number: 1
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.CPUUsage"
|
||||
json_name: "usage"
|
||||
}
|
||||
field {
|
||||
name: "throttling"
|
||||
number: 2
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.Throttle"
|
||||
json_name: "throttling"
|
||||
}
|
||||
}
|
||||
message_type {
|
||||
name: "CPUUsage"
|
||||
field {
|
||||
name: "total"
|
||||
number: 1
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "total"
|
||||
}
|
||||
field {
|
||||
name: "kernel"
|
||||
number: 2
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "kernel"
|
||||
}
|
||||
field {
|
||||
name: "user"
|
||||
number: 3
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "user"
|
||||
}
|
||||
field {
|
||||
name: "per_cpu"
|
||||
number: 4
|
||||
label: LABEL_REPEATED
|
||||
type: TYPE_UINT64
|
||||
json_name: "perCpu"
|
||||
}
|
||||
}
|
||||
message_type {
|
||||
name: "Throttle"
|
||||
field {
|
||||
name: "periods"
|
||||
number: 1
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "periods"
|
||||
}
|
||||
field {
|
||||
name: "throttled_periods"
|
||||
number: 2
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "throttledPeriods"
|
||||
}
|
||||
field {
|
||||
name: "throttled_time"
|
||||
number: 3
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "throttledTime"
|
||||
}
|
||||
}
|
||||
message_type {
|
||||
name: "MemoryStat"
|
||||
field {
|
||||
name: "cache"
|
||||
number: 1
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "cache"
|
||||
}
|
||||
field {
|
||||
name: "rss"
|
||||
number: 2
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "rss"
|
||||
}
|
||||
field {
|
||||
name: "rss_huge"
|
||||
number: 3
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "rssHuge"
|
||||
}
|
||||
field {
|
||||
name: "mapped_file"
|
||||
number: 4
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "mappedFile"
|
||||
}
|
||||
field {
|
||||
name: "dirty"
|
||||
number: 5
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "dirty"
|
||||
}
|
||||
field {
|
||||
name: "writeback"
|
||||
number: 6
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "writeback"
|
||||
}
|
||||
field {
|
||||
name: "pg_pg_in"
|
||||
number: 7
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "pgPgIn"
|
||||
}
|
||||
field {
|
||||
name: "pg_pg_out"
|
||||
number: 8
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "pgPgOut"
|
||||
}
|
||||
field {
|
||||
name: "pg_fault"
|
||||
number: 9
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "pgFault"
|
||||
}
|
||||
field {
|
||||
name: "pg_maj_fault"
|
||||
number: 10
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "pgMajFault"
|
||||
}
|
||||
field {
|
||||
name: "inactive_anon"
|
||||
number: 11
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "inactiveAnon"
|
||||
}
|
||||
field {
|
||||
name: "active_anon"
|
||||
number: 12
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "activeAnon"
|
||||
}
|
||||
field {
|
||||
name: "inactive_file"
|
||||
number: 13
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "inactiveFile"
|
||||
}
|
||||
field {
|
||||
name: "active_file"
|
||||
number: 14
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "activeFile"
|
||||
}
|
||||
field {
|
||||
name: "unevictable"
|
||||
number: 15
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "unevictable"
|
||||
}
|
||||
field {
|
||||
name: "hierarchical_memory_limit"
|
||||
number: 16
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "hierarchicalMemoryLimit"
|
||||
}
|
||||
field {
|
||||
name: "hierarchical_swap_limit"
|
||||
number: 17
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "hierarchicalSwapLimit"
|
||||
}
|
||||
field {
|
||||
name: "total_cache"
|
||||
number: 18
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "totalCache"
|
||||
}
|
||||
field {
|
||||
name: "total_rss"
|
||||
number: 19
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "totalRss"
|
||||
}
|
||||
field {
|
||||
name: "total_rss_huge"
|
||||
number: 20
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "totalRssHuge"
|
||||
}
|
||||
field {
|
||||
name: "total_mapped_file"
|
||||
number: 21
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "totalMappedFile"
|
||||
}
|
||||
field {
|
||||
name: "total_dirty"
|
||||
number: 22
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "totalDirty"
|
||||
}
|
||||
field {
|
||||
name: "total_writeback"
|
||||
number: 23
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "totalWriteback"
|
||||
}
|
||||
field {
|
||||
name: "total_pg_pg_in"
|
||||
number: 24
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "totalPgPgIn"
|
||||
}
|
||||
field {
|
||||
name: "total_pg_pg_out"
|
||||
number: 25
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "totalPgPgOut"
|
||||
}
|
||||
field {
|
||||
name: "total_pg_fault"
|
||||
number: 26
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "totalPgFault"
|
||||
}
|
||||
field {
|
||||
name: "total_pg_maj_fault"
|
||||
number: 27
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "totalPgMajFault"
|
||||
}
|
||||
field {
|
||||
name: "total_inactive_anon"
|
||||
number: 28
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "totalInactiveAnon"
|
||||
}
|
||||
field {
|
||||
name: "total_active_anon"
|
||||
number: 29
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "totalActiveAnon"
|
||||
}
|
||||
field {
|
||||
name: "total_inactive_file"
|
||||
number: 30
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "totalInactiveFile"
|
||||
}
|
||||
field {
|
||||
name: "total_active_file"
|
||||
number: 31
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "totalActiveFile"
|
||||
}
|
||||
field {
|
||||
name: "total_unevictable"
|
||||
number: 32
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "totalUnevictable"
|
||||
}
|
||||
field {
|
||||
name: "usage"
|
||||
number: 33
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.MemoryEntry"
|
||||
json_name: "usage"
|
||||
}
|
||||
field {
|
||||
name: "swap"
|
||||
number: 34
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.MemoryEntry"
|
||||
json_name: "swap"
|
||||
}
|
||||
field {
|
||||
name: "kernel"
|
||||
number: 35
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.MemoryEntry"
|
||||
json_name: "kernel"
|
||||
}
|
||||
field {
|
||||
name: "kernel_tcp"
|
||||
number: 36
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.MemoryEntry"
|
||||
json_name: "kernelTcp"
|
||||
}
|
||||
}
|
||||
message_type {
|
||||
name: "MemoryEntry"
|
||||
field {
|
||||
name: "limit"
|
||||
number: 1
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "limit"
|
||||
}
|
||||
field {
|
||||
name: "usage"
|
||||
number: 2
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "usage"
|
||||
}
|
||||
field {
|
||||
name: "max"
|
||||
number: 3
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "max"
|
||||
}
|
||||
field {
|
||||
name: "failcnt"
|
||||
number: 4
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "failcnt"
|
||||
}
|
||||
}
|
||||
message_type {
|
||||
name: "MemoryOomControl"
|
||||
field {
|
||||
name: "oom_kill_disable"
|
||||
number: 1
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "oomKillDisable"
|
||||
}
|
||||
field {
|
||||
name: "under_oom"
|
||||
number: 2
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "underOom"
|
||||
}
|
||||
field {
|
||||
name: "oom_kill"
|
||||
number: 3
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "oomKill"
|
||||
}
|
||||
}
|
||||
message_type {
|
||||
name: "BlkIOStat"
|
||||
field {
|
||||
name: "io_service_bytes_recursive"
|
||||
number: 1
|
||||
label: LABEL_REPEATED
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.BlkIOEntry"
|
||||
json_name: "ioServiceBytesRecursive"
|
||||
}
|
||||
field {
|
||||
name: "io_serviced_recursive"
|
||||
number: 2
|
||||
label: LABEL_REPEATED
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.BlkIOEntry"
|
||||
json_name: "ioServicedRecursive"
|
||||
}
|
||||
field {
|
||||
name: "io_queued_recursive"
|
||||
number: 3
|
||||
label: LABEL_REPEATED
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.BlkIOEntry"
|
||||
json_name: "ioQueuedRecursive"
|
||||
}
|
||||
field {
|
||||
name: "io_service_time_recursive"
|
||||
number: 4
|
||||
label: LABEL_REPEATED
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.BlkIOEntry"
|
||||
json_name: "ioServiceTimeRecursive"
|
||||
}
|
||||
field {
|
||||
name: "io_wait_time_recursive"
|
||||
number: 5
|
||||
label: LABEL_REPEATED
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.BlkIOEntry"
|
||||
json_name: "ioWaitTimeRecursive"
|
||||
}
|
||||
field {
|
||||
name: "io_merged_recursive"
|
||||
number: 6
|
||||
label: LABEL_REPEATED
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.BlkIOEntry"
|
||||
json_name: "ioMergedRecursive"
|
||||
}
|
||||
field {
|
||||
name: "io_time_recursive"
|
||||
number: 7
|
||||
label: LABEL_REPEATED
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.BlkIOEntry"
|
||||
json_name: "ioTimeRecursive"
|
||||
}
|
||||
field {
|
||||
name: "sectors_recursive"
|
||||
number: 8
|
||||
label: LABEL_REPEATED
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.BlkIOEntry"
|
||||
json_name: "sectorsRecursive"
|
||||
}
|
||||
}
|
||||
message_type {
|
||||
name: "BlkIOEntry"
|
||||
field {
|
||||
name: "op"
|
||||
number: 1
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_STRING
|
||||
json_name: "op"
|
||||
}
|
||||
field {
|
||||
name: "device"
|
||||
number: 2
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_STRING
|
||||
json_name: "device"
|
||||
}
|
||||
field {
|
||||
name: "major"
|
||||
number: 3
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "major"
|
||||
}
|
||||
field {
|
||||
name: "minor"
|
||||
number: 4
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "minor"
|
||||
}
|
||||
field {
|
||||
name: "value"
|
||||
number: 5
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "value"
|
||||
}
|
||||
}
|
||||
message_type {
|
||||
name: "RdmaStat"
|
||||
field {
|
||||
name: "current"
|
||||
number: 1
|
||||
label: LABEL_REPEATED
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.RdmaEntry"
|
||||
json_name: "current"
|
||||
}
|
||||
field {
|
||||
name: "limit"
|
||||
number: 2
|
||||
label: LABEL_REPEATED
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v1.RdmaEntry"
|
||||
json_name: "limit"
|
||||
}
|
||||
}
|
||||
message_type {
|
||||
name: "RdmaEntry"
|
||||
field {
|
||||
name: "device"
|
||||
number: 1
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_STRING
|
||||
json_name: "device"
|
||||
}
|
||||
field {
|
||||
name: "hca_handles"
|
||||
number: 2
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT32
|
||||
json_name: "hcaHandles"
|
||||
}
|
||||
field {
|
||||
name: "hca_objects"
|
||||
number: 3
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT32
|
||||
json_name: "hcaObjects"
|
||||
}
|
||||
}
|
||||
message_type {
|
||||
name: "NetworkStat"
|
||||
field {
|
||||
name: "name"
|
||||
number: 1
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_STRING
|
||||
json_name: "name"
|
||||
}
|
||||
field {
|
||||
name: "rx_bytes"
|
||||
number: 2
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "rxBytes"
|
||||
}
|
||||
field {
|
||||
name: "rx_packets"
|
||||
number: 3
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "rxPackets"
|
||||
}
|
||||
field {
|
||||
name: "rx_errors"
|
||||
number: 4
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "rxErrors"
|
||||
}
|
||||
field {
|
||||
name: "rx_dropped"
|
||||
number: 5
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "rxDropped"
|
||||
}
|
||||
field {
|
||||
name: "tx_bytes"
|
||||
number: 6
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "txBytes"
|
||||
}
|
||||
field {
|
||||
name: "tx_packets"
|
||||
number: 7
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "txPackets"
|
||||
}
|
||||
field {
|
||||
name: "tx_errors"
|
||||
number: 8
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "txErrors"
|
||||
}
|
||||
field {
|
||||
name: "tx_dropped"
|
||||
number: 9
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "txDropped"
|
||||
}
|
||||
}
|
||||
message_type {
|
||||
name: "CgroupStats"
|
||||
field {
|
||||
name: "nr_sleeping"
|
||||
number: 1
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "nrSleeping"
|
||||
}
|
||||
field {
|
||||
name: "nr_running"
|
||||
number: 2
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "nrRunning"
|
||||
}
|
||||
field {
|
||||
name: "nr_stopped"
|
||||
number: 3
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "nrStopped"
|
||||
}
|
||||
field {
|
||||
name: "nr_uninterruptible"
|
||||
number: 4
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "nrUninterruptible"
|
||||
}
|
||||
field {
|
||||
name: "nr_io_wait"
|
||||
number: 5
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_UINT64
|
||||
json_name: "nrIoWait"
|
||||
}
|
||||
}
|
||||
options {
|
||||
go_package: "github.com/containerd/cgroups/cgroup1/stats"
|
||||
}
|
||||
syntax: "proto3"
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package io.containerd.cgroups.v1;
|
||||
|
||||
option go_package = "github.com/containerd/cgroups/cgroup1/stats";
|
||||
|
||||
message Metrics {
|
||||
repeated HugetlbStat hugetlb = 1;
|
||||
PidsStat pids = 2;
|
||||
CPUStat cpu = 3;
|
||||
MemoryStat memory = 4;
|
||||
BlkIOStat blkio = 5;
|
||||
RdmaStat rdma = 6;
|
||||
repeated NetworkStat network = 7;
|
||||
CgroupStats cgroup_stats = 8;
|
||||
MemoryOomControl memory_oom_control = 9;
|
||||
}
|
||||
|
||||
message HugetlbStat {
|
||||
uint64 usage = 1;
|
||||
uint64 max = 2;
|
||||
uint64 failcnt = 3;
|
||||
string pagesize = 4;
|
||||
}
|
||||
|
||||
message PidsStat {
|
||||
uint64 current = 1;
|
||||
uint64 limit = 2;
|
||||
}
|
||||
|
||||
message CPUStat {
|
||||
CPUUsage usage = 1;
|
||||
Throttle throttling = 2;
|
||||
}
|
||||
|
||||
message CPUUsage {
|
||||
// values in nanoseconds
|
||||
uint64 total = 1;
|
||||
uint64 kernel = 2;
|
||||
uint64 user = 3;
|
||||
repeated uint64 per_cpu = 4;
|
||||
|
||||
}
|
||||
|
||||
message Throttle {
|
||||
uint64 periods = 1;
|
||||
uint64 throttled_periods = 2;
|
||||
uint64 throttled_time = 3;
|
||||
}
|
||||
|
||||
message MemoryStat {
|
||||
uint64 cache = 1;
|
||||
uint64 rss = 2;
|
||||
uint64 rss_huge = 3;
|
||||
uint64 mapped_file = 4;
|
||||
uint64 dirty = 5;
|
||||
uint64 writeback = 6;
|
||||
uint64 pg_pg_in = 7;
|
||||
uint64 pg_pg_out = 8;
|
||||
uint64 pg_fault = 9;
|
||||
uint64 pg_maj_fault = 10;
|
||||
uint64 inactive_anon = 11;
|
||||
uint64 active_anon = 12;
|
||||
uint64 inactive_file = 13;
|
||||
uint64 active_file = 14;
|
||||
uint64 unevictable = 15;
|
||||
uint64 hierarchical_memory_limit = 16;
|
||||
uint64 hierarchical_swap_limit = 17;
|
||||
uint64 total_cache = 18;
|
||||
uint64 total_rss = 19;
|
||||
uint64 total_rss_huge = 20;
|
||||
uint64 total_mapped_file = 21;
|
||||
uint64 total_dirty = 22;
|
||||
uint64 total_writeback = 23;
|
||||
uint64 total_pg_pg_in = 24;
|
||||
uint64 total_pg_pg_out = 25;
|
||||
uint64 total_pg_fault = 26;
|
||||
uint64 total_pg_maj_fault = 27;
|
||||
uint64 total_inactive_anon = 28;
|
||||
uint64 total_active_anon = 29;
|
||||
uint64 total_inactive_file = 30;
|
||||
uint64 total_active_file = 31;
|
||||
uint64 total_unevictable = 32;
|
||||
MemoryEntry usage = 33;
|
||||
MemoryEntry swap = 34;
|
||||
MemoryEntry kernel = 35;
|
||||
MemoryEntry kernel_tcp = 36;
|
||||
|
||||
}
|
||||
|
||||
message MemoryEntry {
|
||||
uint64 limit = 1;
|
||||
uint64 usage = 2;
|
||||
uint64 max = 3;
|
||||
uint64 failcnt = 4;
|
||||
}
|
||||
|
||||
message MemoryOomControl {
|
||||
uint64 oom_kill_disable = 1;
|
||||
uint64 under_oom = 2;
|
||||
uint64 oom_kill = 3;
|
||||
}
|
||||
|
||||
message BlkIOStat {
|
||||
repeated BlkIOEntry io_service_bytes_recursive = 1;
|
||||
repeated BlkIOEntry io_serviced_recursive = 2;
|
||||
repeated BlkIOEntry io_queued_recursive = 3;
|
||||
repeated BlkIOEntry io_service_time_recursive = 4;
|
||||
repeated BlkIOEntry io_wait_time_recursive = 5;
|
||||
repeated BlkIOEntry io_merged_recursive = 6;
|
||||
repeated BlkIOEntry io_time_recursive = 7;
|
||||
repeated BlkIOEntry sectors_recursive = 8;
|
||||
}
|
||||
|
||||
message BlkIOEntry {
|
||||
string op = 1;
|
||||
string device = 2;
|
||||
uint64 major = 3;
|
||||
uint64 minor = 4;
|
||||
uint64 value = 5;
|
||||
}
|
||||
|
||||
message RdmaStat {
|
||||
repeated RdmaEntry current = 1;
|
||||
repeated RdmaEntry limit = 2;
|
||||
}
|
||||
|
||||
message RdmaEntry {
|
||||
string device = 1;
|
||||
uint32 hca_handles = 2;
|
||||
uint32 hca_objects = 3;
|
||||
}
|
||||
|
||||
message NetworkStat {
|
||||
string name = 1;
|
||||
uint64 rx_bytes = 2;
|
||||
uint64 rx_packets = 3;
|
||||
uint64 rx_errors = 4;
|
||||
uint64 rx_dropped = 5;
|
||||
uint64 tx_bytes = 6;
|
||||
uint64 tx_packets = 7;
|
||||
uint64 tx_errors = 8;
|
||||
uint64 tx_dropped = 9;
|
||||
}
|
||||
|
||||
// CgroupStats exports per-cgroup statistics.
|
||||
message CgroupStats {
|
||||
// number of tasks sleeping
|
||||
uint64 nr_sleeping = 1;
|
||||
// number of tasks running
|
||||
uint64 nr_running = 2;
|
||||
// number of tasks in stopped state
|
||||
uint64 nr_stopped = 3;
|
||||
// number of tasks in uninterruptible state
|
||||
uint64 nr_uninterruptible = 4;
|
||||
// number of tasks waiting on IO
|
||||
uint64 nr_io_wait = 5;
|
||||
}
|
||||
Generated
Vendored
+4
-3
@@ -14,13 +14,14 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
v1 "github.com/containerd/cgroups/stats/v1"
|
||||
"github.com/containerd/cgroups/v3"
|
||||
v1 "github.com/containerd/cgroups/v3/cgroup1/stats"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
@@ -59,7 +60,7 @@ func Subsystems() []Name {
|
||||
Blkio,
|
||||
Rdma,
|
||||
}
|
||||
if !RunningInUserNS() {
|
||||
if !cgroups.RunningInUserNS() {
|
||||
n = append(n, Devices)
|
||||
}
|
||||
if _, err := os.Stat("/sys/kernel/mm/hugepages"); err == nil {
|
||||
Generated
Vendored
+3
-4
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
|
||||
const (
|
||||
SystemdDbus Name = "systemd"
|
||||
defaultSlice = "system.slice"
|
||||
defaultSlice Name = "system.slice"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -56,7 +56,7 @@ func Systemd() ([]Subsystem, error) {
|
||||
|
||||
func Slice(slice, name string) Path {
|
||||
if slice == "" {
|
||||
slice = defaultSlice
|
||||
slice = string(defaultSlice)
|
||||
}
|
||||
return func(subsystem Name) (string, error) {
|
||||
return filepath.Join(slice, name), nil
|
||||
@@ -70,7 +70,6 @@ func NewSystemd(root string) (*SystemdController, error) {
|
||||
}
|
||||
|
||||
type SystemdController struct {
|
||||
mu sync.Mutex
|
||||
root string
|
||||
}
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
func getClockTicks() uint64 {
|
||||
// The value comes from `C.sysconf(C._SC_CLK_TCK)`, and
|
||||
Generated
Vendored
+28
-139
@@ -14,106 +14,23 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/cgroups/v3"
|
||||
units "github.com/docker/go-units"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
nsOnce sync.Once
|
||||
inUserNS bool
|
||||
checkMode sync.Once
|
||||
cgMode CGMode
|
||||
)
|
||||
|
||||
const unifiedMountpoint = "/sys/fs/cgroup"
|
||||
|
||||
// CGMode is the cgroups mode of the host system
|
||||
type CGMode int
|
||||
|
||||
const (
|
||||
// Unavailable cgroup mountpoint
|
||||
Unavailable CGMode = iota
|
||||
// Legacy cgroups v1
|
||||
Legacy
|
||||
// Hybrid with cgroups v1 and v2 controllers mounted
|
||||
Hybrid
|
||||
// Unified with only cgroups v2 mounted
|
||||
Unified
|
||||
)
|
||||
|
||||
// Mode returns the cgroups mode running on the host
|
||||
func Mode() CGMode {
|
||||
checkMode.Do(func() {
|
||||
var st unix.Statfs_t
|
||||
if err := unix.Statfs(unifiedMountpoint, &st); err != nil {
|
||||
cgMode = Unavailable
|
||||
return
|
||||
}
|
||||
switch st.Type {
|
||||
case unix.CGROUP2_SUPER_MAGIC:
|
||||
cgMode = Unified
|
||||
default:
|
||||
cgMode = Legacy
|
||||
if err := unix.Statfs(filepath.Join(unifiedMountpoint, "unified"), &st); err != nil {
|
||||
return
|
||||
}
|
||||
if st.Type == unix.CGROUP2_SUPER_MAGIC {
|
||||
cgMode = Hybrid
|
||||
}
|
||||
}
|
||||
})
|
||||
return cgMode
|
||||
}
|
||||
|
||||
// RunningInUserNS detects whether we are currently running in a user namespace.
|
||||
// Copied from github.com/lxc/lxd/shared/util.go
|
||||
func RunningInUserNS() bool {
|
||||
nsOnce.Do(func() {
|
||||
file, err := os.Open("/proc/self/uid_map")
|
||||
if err != nil {
|
||||
// This kernel-provided file only exists if user namespaces are supported
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
buf := bufio.NewReader(file)
|
||||
l, _, err := buf.ReadLine()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
line := string(l)
|
||||
var a, b, c int64
|
||||
fmt.Sscanf(line, "%d %d %d", &a, &b, &c)
|
||||
|
||||
/*
|
||||
* We assume we are in the initial user namespace if we have a full
|
||||
* range - 4294967295 uids starting at uid 0.
|
||||
*/
|
||||
if a == 0 && b == 0 && c == 4294967295 {
|
||||
return
|
||||
}
|
||||
inUserNS = true
|
||||
})
|
||||
return inUserNS
|
||||
}
|
||||
|
||||
// defaults returns all known groups
|
||||
func defaults(root string) ([]Subsystem, error) {
|
||||
h, err := NewHugetlb(root)
|
||||
@@ -136,7 +53,7 @@ func defaults(root string) ([]Subsystem, error) {
|
||||
}
|
||||
// only add the devices cgroup if we are not in a user namespace
|
||||
// because modifications are not allowed
|
||||
if !RunningInUserNS() {
|
||||
if !cgroups.RunningInUserNS() {
|
||||
s = append(s, NewDevices(root))
|
||||
}
|
||||
// add the hugetlb cgroup if error wasn't due to missing hugetlb
|
||||
@@ -215,11 +132,25 @@ func hugePageSizes() ([]string, error) {
|
||||
}
|
||||
|
||||
func readUint(path string) (uint64, error) {
|
||||
v, err := os.ReadFile(path)
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return parseUint(strings.TrimSpace(string(v)), 10, 64)
|
||||
defer f.Close()
|
||||
|
||||
// We should only need 20 bytes for the max uint64, but for a nice power of 2
|
||||
// lets use 32.
|
||||
b := make([]byte, 32)
|
||||
n, err := f.Read(b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
s := string(bytes.TrimSpace(b[:n]))
|
||||
if s == "max" {
|
||||
// Return 0 for the max value to maintain backward compatibility.
|
||||
return 0, nil
|
||||
}
|
||||
return parseUint(s, 10, 64)
|
||||
}
|
||||
|
||||
func parseUint(s string, base, bitSize int) (uint64, error) {
|
||||
@@ -256,12 +187,14 @@ func parseKV(raw string) (string, uint64, error) {
|
||||
|
||||
// ParseCgroupFile parses the given cgroup file, typically /proc/self/cgroup
|
||||
// or /proc/<pid>/cgroup, into a map of subsystems to cgroup paths, e.g.
|
||||
// "cpu": "/user.slice/user-1000.slice"
|
||||
// "pids": "/user.slice/user-1000.slice"
|
||||
//
|
||||
// "cpu": "/user.slice/user-1000.slice"
|
||||
// "pids": "/user.slice/user-1000.slice"
|
||||
//
|
||||
// etc.
|
||||
//
|
||||
// The resulting map does not have an element for cgroup v2 unified hierarchy.
|
||||
// Use ParseCgroupFileUnified to get the unified path.
|
||||
// Use [cgroups.ParseCgroupFileUnified] to get the unified path.
|
||||
func ParseCgroupFile(path string) (map[string]string, error) {
|
||||
x, _, err := ParseCgroupFileUnified(path)
|
||||
return x, err
|
||||
@@ -269,41 +202,10 @@ func ParseCgroupFile(path string) (map[string]string, error) {
|
||||
|
||||
// ParseCgroupFileUnified returns legacy subsystem paths as the first value,
|
||||
// and returns the unified path as the second value.
|
||||
//
|
||||
// Deprecated: use [cgroups.ParseCgroupFileUnified] instead .
|
||||
func ParseCgroupFileUnified(path string) (map[string]string, string, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
defer f.Close()
|
||||
return parseCgroupFromReaderUnified(f)
|
||||
}
|
||||
|
||||
func parseCgroupFromReaderUnified(r io.Reader) (map[string]string, string, error) {
|
||||
var (
|
||||
cgroups = make(map[string]string)
|
||||
unified = ""
|
||||
s = bufio.NewScanner(r)
|
||||
)
|
||||
for s.Scan() {
|
||||
var (
|
||||
text = s.Text()
|
||||
parts = strings.SplitN(text, ":", 3)
|
||||
)
|
||||
if len(parts) < 3 {
|
||||
return nil, unified, fmt.Errorf("invalid cgroup entry: %q", text)
|
||||
}
|
||||
for _, subs := range strings.Split(parts[1], ",") {
|
||||
if subs == "" {
|
||||
unified = parts[2]
|
||||
} else {
|
||||
cgroups[subs] = parts[2]
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := s.Err(); err != nil {
|
||||
return nil, unified, err
|
||||
}
|
||||
return cgroups, unified, nil
|
||||
return cgroups.ParseCgroupFileUnified(path)
|
||||
}
|
||||
|
||||
func getCgroupDestination(subsystem string) (string, error) {
|
||||
@@ -376,16 +278,3 @@ func cleanPath(path string) string {
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func retryingWriteFile(path string, data []byte, mode os.FileMode) error {
|
||||
// Retry writes on EINTR; see:
|
||||
// https://github.com/golang/go/issues/38033
|
||||
for {
|
||||
err := os.WriteFile(path, data, mode)
|
||||
if err == nil {
|
||||
return nil
|
||||
} else if !errors.Is(err, syscall.EINTR) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+4
-4
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
package cgroup1
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -24,8 +24,8 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// V1 returns all the groups in the default cgroups mountpoint in a single hierarchy
|
||||
func V1() ([]Subsystem, error) {
|
||||
// Default returns all the groups in the default cgroups mountpoint in a single hierarchy
|
||||
func Default() ([]Subsystem, error) {
|
||||
root, err := v1MountPoint()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -45,7 +45,7 @@ func V1() ([]Subsystem, error) {
|
||||
}
|
||||
|
||||
// v1MountPoint returns the mount point where the cgroup
|
||||
// mountpoints are mounted in a single hiearchy
|
||||
// mountpoints are mounted in a single hierarchy
|
||||
func v1MountPoint() (string, error) {
|
||||
f, err := os.Open("/proc/self/mountinfo")
|
||||
if err != nil {
|
||||
Generated
Vendored
+1
-1
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
package cgroup2
|
||||
|
||||
import (
|
||||
"math"
|
||||
Generated
Vendored
+3
-3
@@ -24,7 +24,7 @@
|
||||
// This particular Go implementation based on runc version
|
||||
// https://github.com/opencontainers/runc/blob/master/libcontainer/cgroups/ebpf/devicefilter/devicefilter.go
|
||||
|
||||
package v2
|
||||
package cgroup2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -167,7 +167,7 @@ func (p *program) appendDevice(dev specs.LinuxDeviceCgroup) error {
|
||||
}
|
||||
p.insts = append(p.insts, acceptBlock(dev.Allow)...)
|
||||
// set blockSym to the first instruction we added in this iteration
|
||||
p.insts[prevBlockLastIdx+1] = p.insts[prevBlockLastIdx+1].Sym(blockSym)
|
||||
p.insts[prevBlockLastIdx+1] = p.insts[prevBlockLastIdx+1].WithSymbol(blockSym)
|
||||
p.blockID++
|
||||
return nil
|
||||
}
|
||||
@@ -180,7 +180,7 @@ func (p *program) finalize() (asm.Instructions, error) {
|
||||
blockSym := fmt.Sprintf("block-%d", p.blockID)
|
||||
p.insts = append(p.insts,
|
||||
// R0 <- 0
|
||||
asm.Mov.Imm32(asm.R0, 0).Sym(blockSym),
|
||||
asm.Mov.Imm32(asm.R0, 0).WithSymbol(blockSym),
|
||||
asm.Return(),
|
||||
)
|
||||
p.blockID = -1
|
||||
Generated
Vendored
+1
-1
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
package cgroup2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
Generated
Vendored
+1
-1
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
package cgroup2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
Generated
Vendored
+1
-1
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
package cgroup2
|
||||
|
||||
import "strings"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
package cgroup2
|
||||
|
||||
import "fmt"
|
||||
|
||||
Generated
Vendored
+219
-193
@@ -14,23 +14,21 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
package cgroup2
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/cgroups/v2/stats"
|
||||
"github.com/containerd/cgroups/v3/cgroup2/stats"
|
||||
|
||||
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
|
||||
"github.com/godbus/dbus/v5"
|
||||
@@ -42,13 +40,13 @@ import (
|
||||
const (
|
||||
subtreeControl = "cgroup.subtree_control"
|
||||
controllersFile = "cgroup.controllers"
|
||||
killFile = "cgroup.kill"
|
||||
typeFile = "cgroup.type"
|
||||
defaultCgroup2Path = "/sys/fs/cgroup"
|
||||
defaultSlice = "system.slice"
|
||||
)
|
||||
|
||||
var (
|
||||
canDelegate bool
|
||||
)
|
||||
var canDelegate bool
|
||||
|
||||
type Event struct {
|
||||
Low uint64
|
||||
@@ -98,7 +96,9 @@ func (r *Resources) Values() (o []Value) {
|
||||
func (r *Resources) EnabledControllers() (c []string) {
|
||||
if r.CPU != nil {
|
||||
c = append(c, "cpu")
|
||||
c = append(c, "cpuset")
|
||||
if r.CPU.Cpus != "" || r.CPU.Mems != "" {
|
||||
c = append(c, "cpuset")
|
||||
}
|
||||
}
|
||||
if r.Memory != nil {
|
||||
c = append(c, "memory")
|
||||
@@ -144,20 +144,11 @@ func (c *Value) write(path string, perm os.FileMode) error {
|
||||
return ErrInvalidFormat
|
||||
}
|
||||
|
||||
// Retry writes on EINTR; see:
|
||||
// https://github.com/golang/go/issues/38033
|
||||
for {
|
||||
err := os.WriteFile(
|
||||
filepath.Join(path, c.filename),
|
||||
data,
|
||||
perm,
|
||||
)
|
||||
if err == nil {
|
||||
return nil
|
||||
} else if !errors.Is(err, syscall.EINTR) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return os.WriteFile(
|
||||
filepath.Join(path, c.filename),
|
||||
data,
|
||||
perm,
|
||||
)
|
||||
}
|
||||
|
||||
func writeValues(path string, values []Value) error {
|
||||
@@ -196,13 +187,35 @@ func NewManager(mountpoint string, group string, resources *Resources) (*Manager
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func LoadManager(mountpoint string, group string) (*Manager, error) {
|
||||
type InitConfig struct {
|
||||
mountpoint string
|
||||
}
|
||||
|
||||
type InitOpts func(c *InitConfig) error
|
||||
|
||||
// WithMountpoint sets the unified mountpoint. The default path is /sys/fs/cgroup.
|
||||
func WithMountpoint(path string) InitOpts {
|
||||
return func(c *InitConfig) error {
|
||||
c.mountpoint = path
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Load a cgroup.
|
||||
func Load(group string, opts ...InitOpts) (*Manager, error) {
|
||||
c := InitConfig{mountpoint: defaultCgroup2Path}
|
||||
for _, opt := range opts {
|
||||
if err := opt(&c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := VerifyGroupPath(group); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path := filepath.Join(mountpoint, group)
|
||||
path := filepath.Join(c.mountpoint, group)
|
||||
return &Manager{
|
||||
unifiedMountpoint: mountpoint,
|
||||
unifiedMountpoint: c.mountpoint,
|
||||
path: path,
|
||||
}, nil
|
||||
}
|
||||
@@ -224,6 +237,35 @@ func setResources(path string, resources *Resources) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CgroupType represents the types a cgroup can be.
|
||||
type CgroupType string
|
||||
|
||||
const (
|
||||
Domain CgroupType = "domain"
|
||||
Threaded CgroupType = "threaded"
|
||||
)
|
||||
|
||||
func (c *Manager) GetType() (CgroupType, error) {
|
||||
val, err := os.ReadFile(filepath.Join(c.path, typeFile))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
trimmed := strings.TrimSpace(string(val))
|
||||
return CgroupType(trimmed), nil
|
||||
}
|
||||
|
||||
func (c *Manager) SetType(cgType CgroupType) error {
|
||||
// NOTE: We could abort if cgType != Threaded here as currently
|
||||
// it's not possible to revert back to domain, but not sure
|
||||
// it's worth being that opinionated, especially if that may
|
||||
// ever change.
|
||||
v := Value{
|
||||
filename: typeFile,
|
||||
value: string(cgType),
|
||||
}
|
||||
return writeValues(c.path, []Value{v})
|
||||
}
|
||||
|
||||
func (c *Manager) RootControllers() ([]string, error) {
|
||||
b, err := os.ReadFile(filepath.Join(c.unifiedMountpoint, controllersFile))
|
||||
if err != nil {
|
||||
@@ -344,6 +386,86 @@ func (c *Manager) AddThread(tid uint64) error {
|
||||
return writeValues(c.path, []Value{v})
|
||||
}
|
||||
|
||||
// Kill will try to forcibly exit all of the processes in the cgroup. This is
|
||||
// equivalent to sending a SIGKILL to every process. On kernels 5.14 and greater
|
||||
// this will use the cgroup.kill file, on anything that doesn't have the cgroup.kill
|
||||
// file, a manual process of freezing -> sending a SIGKILL to every process -> thawing
|
||||
// will be used.
|
||||
func (c *Manager) Kill() error {
|
||||
v := Value{
|
||||
filename: killFile,
|
||||
value: "1",
|
||||
}
|
||||
err := writeValues(c.path, []Value{v})
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
logrus.Warnf("falling back to slower kill implementation: %s", err)
|
||||
// Fallback to slow method.
|
||||
return c.fallbackKill()
|
||||
}
|
||||
|
||||
// fallbackKill is a slower fallback to the more modern (kernels 5.14+)
|
||||
// approach of writing to the cgroup.kill file. This is heavily pulled
|
||||
// from runc's same approach (in signalAllProcesses), with the only differences
|
||||
// being this is just tailored to the API exposed in this library, and we don't
|
||||
// need to care about signals other than SIGKILL.
|
||||
//
|
||||
// https://github.com/opencontainers/runc/blob/8da0a0b5675764feaaaaad466f6567a9983fcd08/libcontainer/init_linux.go#L523-L529
|
||||
func (c *Manager) fallbackKill() error {
|
||||
if err := c.Freeze(); err != nil {
|
||||
logrus.Warn(err)
|
||||
}
|
||||
pids, err := c.Procs(true)
|
||||
if err != nil {
|
||||
if err := c.Thaw(); err != nil {
|
||||
logrus.Warn(err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
var procs []*os.Process
|
||||
for _, pid := range pids {
|
||||
p, err := os.FindProcess(int(pid))
|
||||
if err != nil {
|
||||
logrus.Warn(err)
|
||||
continue
|
||||
}
|
||||
procs = append(procs, p)
|
||||
if err := p.Signal(unix.SIGKILL); err != nil {
|
||||
logrus.Warn(err)
|
||||
}
|
||||
}
|
||||
if err := c.Thaw(); err != nil {
|
||||
logrus.Warn(err)
|
||||
}
|
||||
|
||||
subreaper, err := getSubreaper()
|
||||
if err != nil {
|
||||
// The error here means that PR_GET_CHILD_SUBREAPER is not
|
||||
// supported because this code might run on a kernel older
|
||||
// than 3.4. We don't want to throw an error in that case,
|
||||
// and we simplify things, considering there is no subreaper
|
||||
// set.
|
||||
subreaper = 0
|
||||
}
|
||||
|
||||
for _, p := range procs {
|
||||
// In case a subreaper has been setup, this code must not
|
||||
// wait for the process. Otherwise, we cannot be sure the
|
||||
// current process will be reaped by the subreaper, while
|
||||
// the subreaper might be waiting for this process in order
|
||||
// to retrieve its exit code.
|
||||
if subreaper == 0 {
|
||||
if _, err := p.Wait(); err != nil {
|
||||
if !errors.Is(err, unix.ECHILD) {
|
||||
logrus.Warnf("wait on pid %d failed: %s", p.Pid, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Manager) Delete() error {
|
||||
// kernel prevents cgroups with running process from being removed, check the tree is empty
|
||||
processes, err := c.Procs(true)
|
||||
@@ -398,17 +520,15 @@ func (c *Manager) MoveTo(destination *Manager) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var singleValueFiles = []string{
|
||||
"pids.current",
|
||||
"pids.max",
|
||||
}
|
||||
|
||||
func (c *Manager) Stat() (*stats.Metrics, error) {
|
||||
controllers, err := c.Controllers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make(map[string]interface{})
|
||||
// Sizing this avoids an allocation to increase the map at runtime;
|
||||
// currently the default bucket size is 8 and we put 40+ elements
|
||||
// in it so we'd always end up allocating.
|
||||
out := make(map[string]uint64, 50)
|
||||
for _, controller := range controllers {
|
||||
switch controller {
|
||||
case "cpu", "memory":
|
||||
@@ -420,66 +540,58 @@ func (c *Manager) Stat() (*stats.Metrics, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, name := range singleValueFiles {
|
||||
if err := readSingleFile(c.path, name, out); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
memoryEvents := make(map[string]interface{})
|
||||
memoryEvents := make(map[string]uint64)
|
||||
if err := readKVStatsFile(c.path, "memory.events", memoryEvents); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var metrics stats.Metrics
|
||||
|
||||
var metrics stats.Metrics
|
||||
metrics.Pids = &stats.PidsStat{
|
||||
Current: getPidValue("pids.current", out),
|
||||
Limit: getPidValue("pids.max", out),
|
||||
Current: getStatFileContentUint64(filepath.Join(c.path, "pids.current")),
|
||||
Limit: getStatFileContentUint64(filepath.Join(c.path, "pids.max")),
|
||||
}
|
||||
metrics.CPU = &stats.CPUStat{
|
||||
UsageUsec: getUint64Value("usage_usec", out),
|
||||
UserUsec: getUint64Value("user_usec", out),
|
||||
SystemUsec: getUint64Value("system_usec", out),
|
||||
NrPeriods: getUint64Value("nr_periods", out),
|
||||
NrThrottled: getUint64Value("nr_throttled", out),
|
||||
ThrottledUsec: getUint64Value("throttled_usec", out),
|
||||
UsageUsec: out["usage_usec"],
|
||||
UserUsec: out["user_usec"],
|
||||
SystemUsec: out["system_usec"],
|
||||
NrPeriods: out["nr_periods"],
|
||||
NrThrottled: out["nr_throttled"],
|
||||
ThrottledUsec: out["throttled_usec"],
|
||||
}
|
||||
metrics.Memory = &stats.MemoryStat{
|
||||
Anon: getUint64Value("anon", out),
|
||||
File: getUint64Value("file", out),
|
||||
KernelStack: getUint64Value("kernel_stack", out),
|
||||
Slab: getUint64Value("slab", out),
|
||||
Sock: getUint64Value("sock", out),
|
||||
Shmem: getUint64Value("shmem", out),
|
||||
FileMapped: getUint64Value("file_mapped", out),
|
||||
FileDirty: getUint64Value("file_dirty", out),
|
||||
FileWriteback: getUint64Value("file_writeback", out),
|
||||
AnonThp: getUint64Value("anon_thp", out),
|
||||
InactiveAnon: getUint64Value("inactive_anon", out),
|
||||
ActiveAnon: getUint64Value("active_anon", out),
|
||||
InactiveFile: getUint64Value("inactive_file", out),
|
||||
ActiveFile: getUint64Value("active_file", out),
|
||||
Unevictable: getUint64Value("unevictable", out),
|
||||
SlabReclaimable: getUint64Value("slab_reclaimable", out),
|
||||
SlabUnreclaimable: getUint64Value("slab_unreclaimable", out),
|
||||
Pgfault: getUint64Value("pgfault", out),
|
||||
Pgmajfault: getUint64Value("pgmajfault", out),
|
||||
WorkingsetRefault: getUint64Value("workingset_refault", out),
|
||||
WorkingsetActivate: getUint64Value("workingset_activate", out),
|
||||
WorkingsetNodereclaim: getUint64Value("workingset_nodereclaim", out),
|
||||
Pgrefill: getUint64Value("pgrefill", out),
|
||||
Pgscan: getUint64Value("pgscan", out),
|
||||
Pgsteal: getUint64Value("pgsteal", out),
|
||||
Pgactivate: getUint64Value("pgactivate", out),
|
||||
Pgdeactivate: getUint64Value("pgdeactivate", out),
|
||||
Pglazyfree: getUint64Value("pglazyfree", out),
|
||||
Pglazyfreed: getUint64Value("pglazyfreed", out),
|
||||
ThpFaultAlloc: getUint64Value("thp_fault_alloc", out),
|
||||
ThpCollapseAlloc: getUint64Value("thp_collapse_alloc", out),
|
||||
Anon: out["anon"],
|
||||
File: out["file"],
|
||||
KernelStack: out["kernel_stack"],
|
||||
Slab: out["slab"],
|
||||
Sock: out["sock"],
|
||||
Shmem: out["shmem"],
|
||||
FileMapped: out["file_mapped"],
|
||||
FileDirty: out["file_dirty"],
|
||||
FileWriteback: out["file_writeback"],
|
||||
AnonThp: out["anon_thp"],
|
||||
InactiveAnon: out["inactive_anon"],
|
||||
ActiveAnon: out["active_anon"],
|
||||
InactiveFile: out["inactive_file"],
|
||||
ActiveFile: out["active_file"],
|
||||
Unevictable: out["unevictable"],
|
||||
SlabReclaimable: out["slab_reclaimable"],
|
||||
SlabUnreclaimable: out["slab_unreclaimable"],
|
||||
Pgfault: out["pgfault"],
|
||||
Pgmajfault: out["pgmajfault"],
|
||||
WorkingsetRefault: out["workingset_refault"],
|
||||
WorkingsetActivate: out["workingset_activate"],
|
||||
WorkingsetNodereclaim: out["workingset_nodereclaim"],
|
||||
Pgrefill: out["pgrefill"],
|
||||
Pgscan: out["pgscan"],
|
||||
Pgsteal: out["pgsteal"],
|
||||
Pgactivate: out["pgactivate"],
|
||||
Pgdeactivate: out["pgdeactivate"],
|
||||
Pglazyfree: out["pglazyfree"],
|
||||
Pglazyfreed: out["pglazyfreed"],
|
||||
ThpFaultAlloc: out["thp_fault_alloc"],
|
||||
ThpCollapseAlloc: out["thp_collapse_alloc"],
|
||||
Usage: getStatFileContentUint64(filepath.Join(c.path, "memory.current")),
|
||||
UsageLimit: getStatFileContentUint64(filepath.Join(c.path, "memory.max")),
|
||||
SwapUsage: getStatFileContentUint64(filepath.Join(c.path, "memory.swap.current")),
|
||||
@@ -487,11 +599,11 @@ func (c *Manager) Stat() (*stats.Metrics, error) {
|
||||
}
|
||||
if len(memoryEvents) > 0 {
|
||||
metrics.MemoryEvents = &stats.MemoryEvents{
|
||||
Low: getUint64Value("low", memoryEvents),
|
||||
High: getUint64Value("high", memoryEvents),
|
||||
Max: getUint64Value("max", memoryEvents),
|
||||
Oom: getUint64Value("oom", memoryEvents),
|
||||
OomKill: getUint64Value("oom_kill", memoryEvents),
|
||||
Low: memoryEvents["low"],
|
||||
High: memoryEvents["high"],
|
||||
Max: memoryEvents["max"],
|
||||
Oom: memoryEvents["oom"],
|
||||
OomKill: memoryEvents["oom_kill"],
|
||||
}
|
||||
}
|
||||
metrics.Io = &stats.IOStat{Usage: readIoStats(c.path)}
|
||||
@@ -504,56 +616,7 @@ func (c *Manager) Stat() (*stats.Metrics, error) {
|
||||
return &metrics, nil
|
||||
}
|
||||
|
||||
func getUint64Value(key string, out map[string]interface{}) uint64 {
|
||||
v, ok := out[key]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
switch t := v.(type) {
|
||||
case uint64:
|
||||
return t
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func getPidValue(key string, out map[string]interface{}) uint64 {
|
||||
v, ok := out[key]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
switch t := v.(type) {
|
||||
case uint64:
|
||||
return t
|
||||
case string:
|
||||
if t == "max" {
|
||||
return math.MaxUint64
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func readSingleFile(path string, file string, out map[string]interface{}) error {
|
||||
f, err := os.Open(filepath.Join(path, file))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
data, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s := strings.TrimSpace(string(data))
|
||||
v, err := parseUint(s, 10, 64)
|
||||
if err != nil {
|
||||
// if we cannot parse as a uint, parse as a string
|
||||
out[file] = s
|
||||
return nil
|
||||
}
|
||||
out[file] = v
|
||||
return nil
|
||||
}
|
||||
|
||||
func readKVStatsFile(path string, file string, out map[string]interface{}) error {
|
||||
func readKVStatsFile(path string, file string, out map[string]uint64) error {
|
||||
f, err := os.Open(filepath.Join(path, file))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -598,16 +661,12 @@ func (c *Manager) freeze(path string, state State) error {
|
||||
|
||||
func (c *Manager) isCgroupEmpty() bool {
|
||||
// In case of any error we return true so that we exit and don't leak resources
|
||||
out := make(map[string]interface{})
|
||||
out := make(map[string]uint64)
|
||||
if err := readKVStatsFile(c.path, "cgroup.events", out); err != nil {
|
||||
return true
|
||||
}
|
||||
if v, ok := out["populated"]; ok {
|
||||
populated, ok := v.(uint64)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
return populated == 0
|
||||
return v == 0
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -615,19 +674,19 @@ func (c *Manager) isCgroupEmpty() bool {
|
||||
// MemoryEventFD returns inotify file descriptor and 'memory.events' inotify watch descriptor
|
||||
func (c *Manager) MemoryEventFD() (int, uint32, error) {
|
||||
fpath := filepath.Join(c.path, "memory.events")
|
||||
fd, err := syscall.InotifyInit()
|
||||
fd, err := unix.InotifyInit()
|
||||
if err != nil {
|
||||
return 0, 0, errors.New("failed to create inotify fd")
|
||||
}
|
||||
wd, err := syscall.InotifyAddWatch(fd, fpath, unix.IN_MODIFY)
|
||||
wd, err := unix.InotifyAddWatch(fd, fpath, unix.IN_MODIFY)
|
||||
if err != nil {
|
||||
syscall.Close(fd)
|
||||
unix.Close(fd)
|
||||
return 0, 0, fmt.Errorf("failed to add inotify watch for %q: %w", fpath, err)
|
||||
}
|
||||
// monitor to detect process exit/cgroup deletion
|
||||
evpath := filepath.Join(c.path, "cgroup.events")
|
||||
if _, err = syscall.InotifyAddWatch(fd, evpath, unix.IN_MODIFY); err != nil {
|
||||
syscall.Close(fd)
|
||||
if _, err = unix.InotifyAddWatch(fd, evpath, unix.IN_MODIFY); err != nil {
|
||||
unix.Close(fd)
|
||||
return 0, 0, fmt.Errorf("failed to add inotify watch for %q: %w", evpath, err)
|
||||
}
|
||||
|
||||
@@ -642,41 +701,6 @@ func (c *Manager) EventChan() (<-chan Event, <-chan error) {
|
||||
return ec, errCh
|
||||
}
|
||||
|
||||
func parseMemoryEvents(out map[string]interface{}) (Event, error) {
|
||||
e := Event{}
|
||||
if v, ok := out["high"]; ok {
|
||||
e.High, ok = v.(uint64)
|
||||
if !ok {
|
||||
return Event{}, fmt.Errorf("cannot convert high to uint64: %+v", v)
|
||||
}
|
||||
}
|
||||
if v, ok := out["low"]; ok {
|
||||
e.Low, ok = v.(uint64)
|
||||
if !ok {
|
||||
return Event{}, fmt.Errorf("cannot convert low to uint64: %+v", v)
|
||||
}
|
||||
}
|
||||
if v, ok := out["max"]; ok {
|
||||
e.Max, ok = v.(uint64)
|
||||
if !ok {
|
||||
return Event{}, fmt.Errorf("cannot convert max to uint64: %+v", v)
|
||||
}
|
||||
}
|
||||
if v, ok := out["oom"]; ok {
|
||||
e.OOM, ok = v.(uint64)
|
||||
if !ok {
|
||||
return Event{}, fmt.Errorf("cannot convert oom to uint64: %+v", v)
|
||||
}
|
||||
}
|
||||
if v, ok := out["oom_kill"]; ok {
|
||||
e.OOMKill, ok = v.(uint64)
|
||||
if !ok {
|
||||
return Event{}, fmt.Errorf("cannot convert oom_kill to uint64: %+v", v)
|
||||
}
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func (c *Manager) waitForEvents(ec chan<- Event, errCh chan<- error) {
|
||||
defer close(errCh)
|
||||
|
||||
@@ -685,17 +709,17 @@ func (c *Manager) waitForEvents(ec chan<- Event, errCh chan<- error) {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
defer unix.Close(fd)
|
||||
|
||||
for {
|
||||
buffer := make([]byte, syscall.SizeofInotifyEvent*10)
|
||||
bytesRead, err := syscall.Read(fd, buffer)
|
||||
buffer := make([]byte, unix.SizeofInotifyEvent*10)
|
||||
bytesRead, err := unix.Read(fd, buffer)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
if bytesRead >= syscall.SizeofInotifyEvent {
|
||||
out := make(map[string]interface{})
|
||||
if bytesRead >= unix.SizeofInotifyEvent {
|
||||
out := make(map[string]uint64)
|
||||
if err := readKVStatsFile(c.path, "memory.events", out); err != nil {
|
||||
// When cgroup is deleted read may return -ENODEV instead of -ENOENT from open.
|
||||
if _, statErr := os.Lstat(filepath.Join(c.path, "memory.events")); !os.IsNotExist(statErr) {
|
||||
@@ -703,12 +727,13 @@ func (c *Manager) waitForEvents(ec chan<- Event, errCh chan<- error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
e, err := parseMemoryEvents(out)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
ec <- Event{
|
||||
Low: out["low"],
|
||||
High: out["high"],
|
||||
Max: out["max"],
|
||||
OOM: out["oom"],
|
||||
OOMKill: out["oom_kill"],
|
||||
}
|
||||
ec <- e
|
||||
if c.isCgroupEmpty() {
|
||||
return
|
||||
}
|
||||
@@ -724,7 +749,7 @@ func setDevices(path string, devices []specs.LinuxDeviceCgroup) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dirFD, err := unix.Open(path, unix.O_DIRECTORY|unix.O_RDONLY|unix.O_CLOEXEC, 0600)
|
||||
dirFD, err := unix.Open(path, unix.O_DIRECTORY|unix.O_RDONLY|unix.O_CLOEXEC, 0o600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot get dir FD for %s", path)
|
||||
}
|
||||
@@ -741,7 +766,8 @@ func setDevices(path string, devices []specs.LinuxDeviceCgroup) error {
|
||||
// the reason this is necessary is because the "-" character has a special meaning in
|
||||
// systemd slice. For example, when creating a slice called "my-group-112233.slice",
|
||||
// systemd will create a hierarchy like this:
|
||||
// /sys/fs/cgroup/my.slice/my-group.slice/my-group-112233.slice
|
||||
//
|
||||
// /sys/fs/cgroup/my.slice/my-group.slice/my-group-112233.slice
|
||||
func getSystemdFullPath(slice, group string) string {
|
||||
return filepath.Join(defaultCgroup2Path, dashesToPath(slice), dashesToPath(group))
|
||||
}
|
||||
Generated
Vendored
+1
-1
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
package cgroup2
|
||||
|
||||
type Memory struct {
|
||||
Swap *int64
|
||||
Generated
Vendored
+1
-1
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
package cgroup2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
Generated
Vendored
+1
-1
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
package cgroup2
|
||||
|
||||
import "strconv"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
package cgroup2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
Generated
Vendored
+1
-1
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
package cgroup2
|
||||
|
||||
import (
|
||||
"os"
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package stats
|
||||
+1333
File diff suppressed because it is too large
Load Diff
Generated
Vendored
+4
-5
@@ -1,7 +1,6 @@
|
||||
file {
|
||||
name: "github.com/containerd/cgroups/v2/stats/metrics.proto"
|
||||
name: "github.com/containerd/cgroups/cgroup2/stats/metrics.proto"
|
||||
package: "io.containerd.cgroups.v2"
|
||||
dependency: "gogoproto/gogo.proto"
|
||||
message_type {
|
||||
name: "Metrics"
|
||||
field {
|
||||
@@ -18,9 +17,6 @@ file {
|
||||
label: LABEL_OPTIONAL
|
||||
type: TYPE_MESSAGE
|
||||
type_name: ".io.containerd.cgroups.v2.CPUStat"
|
||||
options {
|
||||
65004: "CPU"
|
||||
}
|
||||
json_name: "cpu"
|
||||
}
|
||||
field {
|
||||
@@ -535,5 +531,8 @@ file {
|
||||
json_name: "pagesize"
|
||||
}
|
||||
}
|
||||
options {
|
||||
go_package: "github.com/containerd/cgroups/cgroup2/stats"
|
||||
}
|
||||
syntax: "proto3"
|
||||
}
|
||||
Generated
Vendored
+2
-2
@@ -2,11 +2,11 @@ syntax = "proto3";
|
||||
|
||||
package io.containerd.cgroups.v2;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
option go_package = "github.com/containerd/cgroups/cgroup2/stats";
|
||||
|
||||
message Metrics {
|
||||
PidsStat pids = 1;
|
||||
CPUStat cpu = 2 [(gogoproto.customname) = "CPU"];
|
||||
CPUStat cpu = 2;
|
||||
MemoryStat memory = 4;
|
||||
RdmaStat rdma = 5;
|
||||
IOStat io = 6;
|
||||
Generated
Vendored
+118
-66
@@ -14,10 +14,11 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
package cgroup2
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
@@ -25,19 +26,22 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/containerd/cgroups/v2/stats"
|
||||
"github.com/containerd/cgroups/v3/cgroup2/stats"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
cgroupProcs = "cgroup.procs"
|
||||
cgroupThreads = "cgroup.threads"
|
||||
defaultDirPerm = 0755
|
||||
defaultDirPerm = 0o755
|
||||
)
|
||||
|
||||
// defaultFilePerm is a var so that the test framework can change the filemode
|
||||
@@ -90,19 +94,13 @@ func parseCgroupProcsFile(path string) ([]uint64, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func parseKV(raw string) (string, interface{}, error) {
|
||||
func parseKV(raw string) (string, uint64, error) {
|
||||
parts := strings.Fields(raw)
|
||||
switch len(parts) {
|
||||
case 2:
|
||||
v, err := parseUint(parts[1], 10, 64)
|
||||
if err != nil {
|
||||
// if we cannot parse as a uint, parse as a string
|
||||
return parts[0], parts[1], nil
|
||||
}
|
||||
return parts[0], v, nil
|
||||
default:
|
||||
if len(parts) != 2 {
|
||||
return "", 0, ErrInvalidFormat
|
||||
}
|
||||
v, err := parseUint(parts[1], 10, 64)
|
||||
return parts[0], v, err
|
||||
}
|
||||
|
||||
func parseUint(s string, base, bitSize int) (uint64, error) {
|
||||
@@ -134,9 +132,7 @@ func parseCgroupFile(path string) (string, error) {
|
||||
}
|
||||
|
||||
func parseCgroupFromReader(r io.Reader) (string, error) {
|
||||
var (
|
||||
s = bufio.NewScanner(r)
|
||||
)
|
||||
s := bufio.NewScanner(r)
|
||||
for s.Scan() {
|
||||
var (
|
||||
text = s.Text()
|
||||
@@ -242,18 +238,28 @@ func ToResources(spec *specs.LinuxResources) *Resources {
|
||||
|
||||
// Gets uint64 parsed content of single value cgroup stat file
|
||||
func getStatFileContentUint64(filePath string) uint64 {
|
||||
contents, err := os.ReadFile(filePath)
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
trimmed := strings.TrimSpace(string(contents))
|
||||
defer f.Close()
|
||||
|
||||
// We expect an unsigned 64 bit integer, or a "max" string
|
||||
// in some cases.
|
||||
buf := make([]byte, 32)
|
||||
n, err := f.Read(buf)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
trimmed := strings.TrimSpace(string(buf[:n]))
|
||||
if trimmed == "max" {
|
||||
return math.MaxUint64
|
||||
}
|
||||
|
||||
res, err := parseUint(trimmed, 10, 64)
|
||||
if err != nil {
|
||||
logrus.Errorf("unable to parse %q as a uint from Cgroup file %q", string(contents), filePath)
|
||||
logrus.Errorf("unable to parse %q as a uint from Cgroup file %q", trimmed, filePath)
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -383,54 +389,100 @@ func systemdUnitFromPath(path string) string {
|
||||
}
|
||||
|
||||
func readHugeTlbStats(path string) []*stats.HugeTlbStat {
|
||||
var usage = []*stats.HugeTlbStat{}
|
||||
var keyUsage = make(map[string]*stats.HugeTlbStat)
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return usage
|
||||
}
|
||||
files, err := f.Readdir(-1)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
return usage
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if strings.Contains(file.Name(), "hugetlb") &&
|
||||
(strings.HasSuffix(file.Name(), "max") || strings.HasSuffix(file.Name(), "current")) {
|
||||
var hugeTlb *stats.HugeTlbStat
|
||||
var ok bool
|
||||
fileName := strings.Split(file.Name(), ".")
|
||||
pageSize := fileName[1]
|
||||
if hugeTlb, ok = keyUsage[pageSize]; !ok {
|
||||
hugeTlb = &stats.HugeTlbStat{}
|
||||
}
|
||||
hugeTlb.Pagesize = pageSize
|
||||
out, err := os.ReadFile(filepath.Join(path, file.Name()))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
var value uint64
|
||||
stringVal := strings.TrimSpace(string(out))
|
||||
if stringVal == "max" {
|
||||
value = math.MaxUint64
|
||||
} else {
|
||||
value, err = strconv.ParseUint(stringVal, 10, 64)
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
switch fileName[2] {
|
||||
case "max":
|
||||
hugeTlb.Max = value
|
||||
case "current":
|
||||
hugeTlb.Current = value
|
||||
}
|
||||
keyUsage[pageSize] = hugeTlb
|
||||
hpSizes := hugePageSizes()
|
||||
usage := make([]*stats.HugeTlbStat, len(hpSizes))
|
||||
for idx, pagesize := range hpSizes {
|
||||
usage[idx] = &stats.HugeTlbStat{
|
||||
Max: getStatFileContentUint64(filepath.Join(path, "hugetlb."+pagesize+".max")),
|
||||
Current: getStatFileContentUint64(filepath.Join(path, "hugetlb."+pagesize+".current")),
|
||||
Pagesize: pagesize,
|
||||
}
|
||||
}
|
||||
for _, entry := range keyUsage {
|
||||
usage = append(usage, entry)
|
||||
}
|
||||
return usage
|
||||
}
|
||||
|
||||
var (
|
||||
hPageSizes []string
|
||||
initHPSOnce sync.Once
|
||||
)
|
||||
|
||||
// The following idea and implementation is taken pretty much line for line from
|
||||
// runc. Because the hugetlb files are well known, and the only variable thrown in
|
||||
// the mix is what huge page sizes you have on your host, this lends itself well
|
||||
// to doing the work to find the files present once, and then re-using this. This
|
||||
// saves a os.Readdirnames(0) call to search for hugeltb files on every `manager.Stat`
|
||||
// call.
|
||||
// https://github.com/opencontainers/runc/blob/3a2c0c2565644d8a7e0f1dd594a060b21fa96cf1/libcontainer/cgroups/utils.go#L301
|
||||
func hugePageSizes() []string {
|
||||
initHPSOnce.Do(func() {
|
||||
dir, err := os.OpenFile("/sys/kernel/mm/hugepages", unix.O_DIRECTORY|unix.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
files, err := dir.Readdirnames(0)
|
||||
dir.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
hPageSizes, err = getHugePageSizeFromFilenames(files)
|
||||
if err != nil {
|
||||
logrus.Warnf("hugePageSizes: %s", err)
|
||||
}
|
||||
})
|
||||
|
||||
return hPageSizes
|
||||
}
|
||||
|
||||
func getHugePageSizeFromFilenames(fileNames []string) ([]string, error) {
|
||||
pageSizes := make([]string, 0, len(fileNames))
|
||||
var warn error
|
||||
|
||||
for _, file := range fileNames {
|
||||
// example: hugepages-1048576kB
|
||||
val := strings.TrimPrefix(file, "hugepages-")
|
||||
if len(val) == len(file) {
|
||||
// Unexpected file name: no prefix found, ignore it.
|
||||
continue
|
||||
}
|
||||
// In all known versions of Linux up to 6.3 the suffix is always
|
||||
// "kB". If we find something else, produce an error but keep going.
|
||||
eLen := len(val) - 2
|
||||
val = strings.TrimSuffix(val, "kB")
|
||||
if len(val) != eLen {
|
||||
// Highly unlikely.
|
||||
if warn == nil {
|
||||
warn = errors.New(file + `: invalid suffix (expected "kB")`)
|
||||
}
|
||||
continue
|
||||
}
|
||||
size, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
// Highly unlikely.
|
||||
if warn == nil {
|
||||
warn = fmt.Errorf("%s: %w", file, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
// Model after https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/mm/hugetlb_cgroup.c?id=eff48ddeab782e35e58ccc8853f7386bbae9dec4#n574
|
||||
// but in our case the size is in KB already.
|
||||
if size >= (1 << 20) {
|
||||
val = strconv.Itoa(size>>20) + "GB"
|
||||
} else if size >= (1 << 10) {
|
||||
val = strconv.Itoa(size>>10) + "MB"
|
||||
} else {
|
||||
val += "KB"
|
||||
}
|
||||
pageSizes = append(pageSizes, val)
|
||||
}
|
||||
|
||||
return pageSizes, warn
|
||||
}
|
||||
|
||||
func getSubreaper() (int, error) {
|
||||
var i uintptr
|
||||
if err := unix.Prctl(unix.PR_GET_CHILD_SUBREAPER, uintptr(unsafe.Pointer(&i)), 0, 0, 0); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return int(i), nil
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cgroups
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
nsOnce sync.Once
|
||||
inUserNS bool
|
||||
checkMode sync.Once
|
||||
cgMode CGMode
|
||||
)
|
||||
|
||||
const unifiedMountpoint = "/sys/fs/cgroup"
|
||||
|
||||
// CGMode is the cgroups mode of the host system
|
||||
type CGMode int
|
||||
|
||||
const (
|
||||
// Unavailable cgroup mountpoint
|
||||
Unavailable CGMode = iota
|
||||
// Legacy cgroups v1
|
||||
Legacy
|
||||
// Hybrid with cgroups v1 and v2 controllers mounted
|
||||
Hybrid
|
||||
// Unified with only cgroups v2 mounted
|
||||
Unified
|
||||
)
|
||||
|
||||
// Mode returns the cgroups mode running on the host
|
||||
func Mode() CGMode {
|
||||
checkMode.Do(func() {
|
||||
var st unix.Statfs_t
|
||||
if err := unix.Statfs(unifiedMountpoint, &st); err != nil {
|
||||
cgMode = Unavailable
|
||||
return
|
||||
}
|
||||
switch st.Type {
|
||||
case unix.CGROUP2_SUPER_MAGIC:
|
||||
cgMode = Unified
|
||||
default:
|
||||
cgMode = Legacy
|
||||
if err := unix.Statfs(filepath.Join(unifiedMountpoint, "unified"), &st); err != nil {
|
||||
return
|
||||
}
|
||||
if st.Type == unix.CGROUP2_SUPER_MAGIC {
|
||||
cgMode = Hybrid
|
||||
}
|
||||
}
|
||||
})
|
||||
return cgMode
|
||||
}
|
||||
|
||||
// RunningInUserNS detects whether we are currently running in a user namespace.
|
||||
// Copied from github.com/lxc/lxd/shared/util.go
|
||||
func RunningInUserNS() bool {
|
||||
nsOnce.Do(func() {
|
||||
file, err := os.Open("/proc/self/uid_map")
|
||||
if err != nil {
|
||||
// This kernel-provided file only exists if user namespaces are supported
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
buf := bufio.NewReader(file)
|
||||
l, _, err := buf.ReadLine()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
line := string(l)
|
||||
var a, b, c int64
|
||||
fmt.Sscanf(line, "%d %d %d", &a, &b, &c)
|
||||
|
||||
/*
|
||||
* We assume we are in the initial user namespace if we have a full
|
||||
* range - 4294967295 uids starting at uid 0.
|
||||
*/
|
||||
if a == 0 && b == 0 && c == 4294967295 {
|
||||
return
|
||||
}
|
||||
inUserNS = true
|
||||
})
|
||||
return inUserNS
|
||||
}
|
||||
|
||||
// ParseCgroupFileUnified returns legacy subsystem paths as the first value,
|
||||
// and returns the unified path as the second value.
|
||||
func ParseCgroupFileUnified(path string) (map[string]string, string, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
defer f.Close()
|
||||
return ParseCgroupFromReaderUnified(f)
|
||||
}
|
||||
|
||||
// ParseCgroupFromReaderUnified returns legacy subsystem paths as the first value,
|
||||
// and returns the unified path as the second value.
|
||||
func ParseCgroupFromReaderUnified(r io.Reader) (map[string]string, string, error) {
|
||||
var (
|
||||
cgroups = make(map[string]string)
|
||||
unified = ""
|
||||
s = bufio.NewScanner(r)
|
||||
)
|
||||
for s.Scan() {
|
||||
var (
|
||||
text = s.Text()
|
||||
parts = strings.SplitN(text, ":", 3)
|
||||
)
|
||||
if len(parts) < 3 {
|
||||
return nil, unified, fmt.Errorf("invalid cgroup entry: %q", text)
|
||||
}
|
||||
for _, subs := range strings.Split(parts[1], ",") {
|
||||
if subs == "" {
|
||||
unified = parts[2]
|
||||
} else {
|
||||
cgroups[subs] = parts[2]
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := s.Err(); err != nil {
|
||||
return nil, unified, err
|
||||
}
|
||||
return cgroups, unified, nil
|
||||
}
|
||||
Reference in New Issue
Block a user