diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b805685..c019a7b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,15 +19,15 @@ jobs: - name: Run tests in Go container (1000m) run: | - docker run --rm -v=$(pwd):/app -w=/app -m=1000m golang:1.22 go test -v ./... -expected=1048576000 + docker run --rm -v=$(pwd):/app -w=/app -m=1000m golang:1.22 go test -v ./... -expected=1048576000 -cgroup-version 1 - name: Run tests in Go container (4321m) run: | - docker run --rm -v=$(pwd):/app -w=/app -m=4321m golang:1.22 go test -v ./... -expected=4530896896 + docker run --rm -v=$(pwd):/app -w=/app -m=4321m golang:1.22 go test -v ./... -expected=4530896896 -cgroup-version 1 - name: Run tests in Go container (system memory limit) run: | - docker run --rm -v=$(pwd):/app -w=/app golang:1.22 go test -v ./... -expected-system=$(($(awk '/MemTotal/ {print $2}' /proc/meminfo) * 1024)) + docker run --rm -v=$(pwd):/app -w=/app golang:1.22 go test -v ./... -expected-system=$(($(awk '/MemTotal/ {print $2}' /proc/meminfo) * 1024)) -cgroup-version 1 test-ubuntu-22_04: runs-on: ubuntu-22.04 @@ -45,12 +45,12 @@ jobs: - name: Run tests in Go container (1000m) run: | - docker run --rm -v=$(pwd):/app -w=/app -m=1000m golang:1.22 go test -v ./... -expected=1048576000 + docker run --rm -v=$(pwd):/app -w=/app -m=1000m golang:1.22 go test -v ./... -expected=1048576000 -cgroup-version 2 - name: Run tests in Go container (4321m) run: | - docker run --rm -v=$(pwd):/app -w=/app -m=4321m golang:1.22 go test -v ./... -expected=4530896896 + docker run --rm -v=$(pwd):/app -w=/app -m=4321m golang:1.22 go test -v ./... -expected=4530896896 -cgroup-version 2 - name: Run tests in Go container (system memory limit) run: | - docker run --rm -v=$(pwd):/app -w=/app golang:1.22 go test -v ./... -expected-system=$(($(awk '/MemTotal/ {print $2}' /proc/meminfo) * 1024)) + docker run --rm -v=$(pwd):/app -w=/app golang:1.22 go test -v ./... -expected-system=$(($(awk '/MemTotal/ {print $2}' /proc/meminfo) * 1024)) -cgroup-version 2 diff --git a/examples/dynamic/go.mod b/examples/dynamic/go.mod index 5c34371..ee10e91 100644 --- a/examples/dynamic/go.mod +++ b/examples/dynamic/go.mod @@ -6,20 +6,6 @@ toolchain go1.23.3 require github.com/KimMachineGun/automemlimit v0.0.0 -require ( - github.com/cilium/ebpf v0.16.0 // indirect - github.com/containerd/cgroups/v3 v3.0.4 // indirect - github.com/containerd/log v0.1.0 // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/docker/go-units v0.5.0 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect - github.com/moby/sys/userns v0.1.0 // indirect - github.com/opencontainers/runtime-spec v1.2.0 // indirect - github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect - golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect - golang.org/x/sys v0.27.0 // indirect - google.golang.org/protobuf v1.35.2 // indirect -) +require github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect replace github.com/KimMachineGun/automemlimit => ../../ diff --git a/examples/dynamic/go.sum b/examples/dynamic/go.sum index bbe612a..a9db5de 100644 --- a/examples/dynamic/go.sum +++ b/examples/dynamic/go.sum @@ -1,65 +1,2 @@ -github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok= -github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= -github.com/containerd/cgroups/v3 v3.0.4 h1:2fs7l3P0Qxb1nKWuJNFiwhp2CqiKzho71DQkDrHJIo4= -github.com/containerd/cgroups/v3 v3.0.4/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins= -github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= -github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= -github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= -github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM= -github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= -github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= -github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= -github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= -github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= -github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= -github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/examples/gosigar/go.mod b/examples/gosigar/go.mod index 7b3f678..de653c7 100644 --- a/examples/gosigar/go.mod +++ b/examples/gosigar/go.mod @@ -10,20 +10,13 @@ require ( ) require ( - github.com/cilium/ebpf v0.16.0 // indirect - github.com/containerd/cgroups/v3 v3.0.4 // indirect - github.com/containerd/log v0.1.0 // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/docker/go-units v0.5.0 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect - github.com/moby/sys/userns v0.1.0 // indirect - github.com/opencontainers/runtime-spec v1.2.0 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect - golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect + github.com/stretchr/testify v1.8.4 // indirect + golang.org/x/net v0.31.0 // indirect golang.org/x/sys v0.27.0 // indirect - google.golang.org/protobuf v1.35.2 // indirect + golang.org/x/tools v0.27.0 // indirect ) replace github.com/KimMachineGun/automemlimit => ../../ diff --git a/examples/gosigar/go.sum b/examples/gosigar/go.sum index 08bfd48..d093f80 100644 --- a/examples/gosigar/go.sum +++ b/examples/gosigar/go.sum @@ -1,83 +1,34 @@ -github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok= -github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= github.com/cloudfoundry/gosigar v1.3.30 h1:ZQPPt8RY72T8V+OZqPAi1qzkqH6UPhrAY8lfmDklNuI= github.com/cloudfoundry/gosigar v1.3.30/go.mod h1:v1aji1eOWmI6/v9T9Gd9ef1a2FEi9m9/25UnfHO0org= -github.com/containerd/cgroups/v3 v3.0.4 h1:2fs7l3P0Qxb1nKWuJNFiwhp2CqiKzho71DQkDrHJIo4= -github.com/containerd/cgroups/v3 v3.0.4/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins= -github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= -github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= -github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= -github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM= -github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= -github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= -github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= -github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= -github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= -github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA= github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= -github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= -github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/examples/logger/go.mod b/examples/logger/go.mod index 679e0e9..ed30fc0 100644 --- a/examples/logger/go.mod +++ b/examples/logger/go.mod @@ -6,20 +6,6 @@ toolchain go1.23.3 require github.com/KimMachineGun/automemlimit v0.0.0 -require ( - github.com/cilium/ebpf v0.16.0 // indirect - github.com/containerd/cgroups/v3 v3.0.4 // indirect - github.com/containerd/log v0.1.0 // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/docker/go-units v0.5.0 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect - github.com/moby/sys/userns v0.1.0 // indirect - github.com/opencontainers/runtime-spec v1.2.0 // indirect - github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect - golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect - golang.org/x/sys v0.27.0 // indirect - google.golang.org/protobuf v1.35.2 // indirect -) +require github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect replace github.com/KimMachineGun/automemlimit => ../../ diff --git a/examples/logger/go.sum b/examples/logger/go.sum index bbe612a..a9db5de 100644 --- a/examples/logger/go.sum +++ b/examples/logger/go.sum @@ -1,65 +1,2 @@ -github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok= -github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= -github.com/containerd/cgroups/v3 v3.0.4 h1:2fs7l3P0Qxb1nKWuJNFiwhp2CqiKzho71DQkDrHJIo4= -github.com/containerd/cgroups/v3 v3.0.4/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins= -github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= -github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= -github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= -github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM= -github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= -github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= -github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= -github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= -github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= -github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= -github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/examples/system/go.mod b/examples/system/go.mod index 545cb51..ee28d4a 100644 --- a/examples/system/go.mod +++ b/examples/system/go.mod @@ -6,20 +6,6 @@ toolchain go1.23.3 require github.com/KimMachineGun/automemlimit v0.0.0 -require ( - github.com/cilium/ebpf v0.16.0 // indirect - github.com/containerd/cgroups/v3 v3.0.4 // indirect - github.com/containerd/log v0.1.0 // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/docker/go-units v0.5.0 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect - github.com/moby/sys/userns v0.1.0 // indirect - github.com/opencontainers/runtime-spec v1.2.0 // indirect - github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect - golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect - golang.org/x/sys v0.27.0 // indirect - google.golang.org/protobuf v1.35.2 // indirect -) +require github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect replace github.com/KimMachineGun/automemlimit => ../../ diff --git a/examples/system/go.sum b/examples/system/go.sum index bbe612a..a9db5de 100644 --- a/examples/system/go.sum +++ b/examples/system/go.sum @@ -1,65 +1,2 @@ -github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok= -github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= -github.com/containerd/cgroups/v3 v3.0.4 h1:2fs7l3P0Qxb1nKWuJNFiwhp2CqiKzho71DQkDrHJIo4= -github.com/containerd/cgroups/v3 v3.0.4/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins= -github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= -github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= -github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= -github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM= -github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= -github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= -github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= -github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= -github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= -github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= -github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/go.mod b/go.mod index 57ec15c..5fe1d8a 100644 --- a/go.mod +++ b/go.mod @@ -4,21 +4,4 @@ go 1.22.0 toolchain go1.23.3 -require ( - github.com/containerd/cgroups/v3 v3.0.4 - github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 -) - -require ( - github.com/cilium/ebpf v0.16.0 // indirect - github.com/containerd/log v0.1.0 // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/docker/go-units v0.5.0 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect - github.com/moby/sys/userns v0.1.0 // indirect - github.com/opencontainers/runtime-spec v1.2.0 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect - golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect - golang.org/x/sys v0.27.0 // indirect - google.golang.org/protobuf v1.35.2 // indirect -) +require github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 diff --git a/go.sum b/go.sum index bbe612a..a9db5de 100644 --- a/go.sum +++ b/go.sum @@ -1,65 +1,2 @@ -github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok= -github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= -github.com/containerd/cgroups/v3 v3.0.4 h1:2fs7l3P0Qxb1nKWuJNFiwhp2CqiKzho71DQkDrHJIo4= -github.com/containerd/cgroups/v3 v3.0.4/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins= -github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= -github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= -github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= -github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM= -github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= -github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= -github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= -github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= -github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= -github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= -github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/memlimit/cgroups.go b/memlimit/cgroups.go index 979bd39..809cc6d 100644 --- a/memlimit/cgroups.go +++ b/memlimit/cgroups.go @@ -1,7 +1,16 @@ package memlimit import ( + "bufio" "errors" + "fmt" + "io" + "math" + "os" + "path/filepath" + "slices" + "strconv" + "strings" ) var ( @@ -10,3 +19,394 @@ var ( // ErrCgroupsNotSupported is returned when the system does not support cgroups. ErrCgroupsNotSupported = errors.New("cgroups is not supported on this system") ) + +// fromCgroup retrieves the memory limit from the cgroup. +// The versionDetector function is used to detect the cgroup version from the mountinfo. +func fromCgroup(versionDetector func(mis []mountInfo) (bool, bool)) (uint64, error) { + mf, err := os.Open("/proc/self/mountinfo") + if err != nil { + return 0, fmt.Errorf("failed to open /proc/self/mountinfo: %w", err) + } + defer mf.Close() + + mis, err := parseMountInfo(mf) + if err != nil { + return 0, fmt.Errorf("failed to parse mountinfo: %w", err) + } + + v1, v2 := versionDetector(mis) + if !(v1 || v2) { + return 0, ErrNoCgroup + } + + cf, err := os.Open("/proc/self/cgroup") + if err != nil { + return 0, fmt.Errorf("failed to open /proc/self/cgroup: %w", err) + } + defer cf.Close() + + chs, err := parseCgroupFile(cf) + if err != nil { + return 0, fmt.Errorf("failed to parse cgroup file: %w", err) + } + + if v2 { + limit, err := getMemoryLimitV2(chs, mis) + if err == nil { + return limit, nil + } else if !v1 { + return 0, err + } + } + + return getMemoryLimitV1(chs, mis) +} + +// detectCgroupVersion detects the cgroup version from the mountinfo. +func detectCgroupVersion(mis []mountInfo) (bool, bool) { + var v1, v2 bool + for _, mi := range mis { + switch mi.FilesystemType { + case "cgroup": + v1 = true + case "cgroup2": + v2 = true + } + } + return v1, v2 +} + +// getMemoryLimitV2 retrieves the memory limit from the cgroup v2 controller. +func getMemoryLimitV2(chs []cgroupHierarchy, mis []mountInfo) (uint64, error) { + // find the cgroup v2 path for the memory controller. + // in cgroup v2, the paths are unified and the controller list is empty. + idx := slices.IndexFunc(chs, func(ch cgroupHierarchy) bool { + return ch.HierarchyID == "0" && ch.ControllerList == "" + }) + if idx == -1 { + return 0, errors.New("cgroup v2 path not found") + } + relPath := chs[idx].CgroupPath + + // find the mountpoint for the cgroup v2 controller. + idx = slices.IndexFunc(mis, func(mi mountInfo) bool { + return mi.FilesystemType == "cgroup2" + }) + if idx == -1 { + return 0, errors.New("cgroup v2 mountpoint not found") + } + root, mountPoint := mis[idx].Root, mis[idx].MountPoint + + // resolve the actual cgroup path + cgroupPath, err := resolveCgroupPath(mountPoint, root, relPath) + if err != nil { + return 0, err + } + + // retrieve the memory limit from the memory.max file + return readMemoryLimitV2FromPath(filepath.Join(cgroupPath, "memory.max")) +} + +// readMemoryLimitV2FromPath reads the memory limit for cgroup v2 from the given path. +// this function expects the path to be memory.max file. +func readMemoryLimitV2FromPath(path string) (uint64, error) { + b, err := os.ReadFile(path) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return 0, ErrNoLimit + } + return 0, fmt.Errorf("failed to read memory.max: %w", err) + } + + slimit := strings.TrimSpace(string(b)) + if slimit == "max" { + return 0, ErrNoLimit + } + + limit, err := strconv.ParseUint(slimit, 10, 64) + if err != nil { + return 0, fmt.Errorf("failed to parse memory.max value: %w", err) + } + + return limit, nil +} + +// getMemoryLimitV1 retrieves the memory limit from the cgroup v1 controller. +func getMemoryLimitV1(chs []cgroupHierarchy, mis []mountInfo) (uint64, error) { + // find the cgroup v1 path for the memory controller. + idx := slices.IndexFunc(chs, func(ch cgroupHierarchy) bool { + return slices.Contains(strings.Split(ch.ControllerList, ","), "memory") + }) + if idx == -1 { + return 0, errors.New("cgroup v1 path for memory controller not found") + } + relPath := chs[idx].CgroupPath + + // find the mountpoint for the cgroup v1 controller. + idx = slices.IndexFunc(mis, func(mi mountInfo) bool { + return mi.FilesystemType == "cgroup" && slices.Contains(strings.Split(mi.SuperOptions, ","), "memory") + }) + if idx == -1 { + return 0, errors.New("cgroup v1 mountpoint for memory controller not found") + } + root, mountPoint := mis[idx].Root, mis[idx].MountPoint + + // resolve the actual cgroup path + cgroupPath, err := resolveCgroupPath(mountPoint, root, relPath) + if err != nil { + return 0, err + } + + // retrieve the memory limit from the memory.stats and memory.limit_in_bytes files. + return readMemoryLimitV1FromPath(cgroupPath) +} + +// getCgroupV1NoLimit returns the maximum value that is used to represent no limit in cgroup v1. +// the max memory limit is max int64, but it should be multiple of the page size. +func getCgroupV1NoLimit() uint64 { + ps := uint64(os.Getpagesize()) + return math.MaxInt64 / ps * ps +} + +// readMemoryLimitV1FromPath reads the memory limit for cgroup v1 from the given path. +// this function expects the path to be the cgroup directory. +func readMemoryLimitV1FromPath(cgroupPath string) (uint64, error) { + // read hierarchical_memory_limit and memory.limit_in_bytes files. + // but if hierarchical_memory_limit is not available, then use the max value as a fallback. + hml, err := readHierarchicalMemoryLimit(filepath.Join(cgroupPath, "memory.stats")) + if err != nil && !errors.Is(err, os.ErrNotExist) { + return 0, fmt.Errorf("failed to read hierarchical_memory_limit: %w", err) + } else if hml == 0 { + hml = math.MaxUint64 + } + + // read memory.limit_in_bytes file. + b, err := os.ReadFile(filepath.Join(cgroupPath, "memory.limit_in_bytes")) + if err != nil && !errors.Is(err, os.ErrNotExist) { + return 0, fmt.Errorf("failed to read memory.limit_in_bytes: %w", err) + } + lib, err := strconv.ParseUint(strings.TrimSpace(string(b)), 10, 64) + if err != nil { + return 0, fmt.Errorf("failed to parse memory.limit_in_bytes value: %w", err) + } else if lib == 0 { + hml = math.MaxUint64 + } + + // use the minimum value between hierarchical_memory_limit and memory.limit_in_bytes. + // if the limit is the maximum value, then it is considered as no limit. + limit := min(hml, lib) + if limit >= getCgroupV1NoLimit() { + return 0, ErrNoLimit + } + + return limit, nil +} + +// readHierarchicalMemoryLimit extracts hierarchical_memory_limit from memory.stats. +// this function expects the path to be memory.stats file. +func readHierarchicalMemoryLimit(path string) (uint64, error) { + file, err := os.Open(path) + if err != nil { + return 0, err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + + fields := strings.Split(line, " ") + if len(fields) < 2 { + return 0, fmt.Errorf("failed to parse memory.stats %q: not enough fields", line) + } + + if fields[0] == "hierarchical_memory_limit" { + if len(fields) > 2 { + return 0, fmt.Errorf("failed to parse memory.stats %q: too many fields for hierarchical_memory_limit", line) + } + return strconv.ParseUint(fields[1], 10, 64) + } + } + if err := scanner.Err(); err != nil { + return 0, err + } + + return 0, nil +} + +// https://www.man7.org/linux/man-pages/man5/proc_pid_mountinfo.5.html +// 731 771 0:59 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw +// +// 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue +// (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11) +// +// (1) mount ID: a unique ID for the mount (may be reused after umount(2)). +// (2) parent ID: the ID of the parent mount (or of self for the root of this mount namespace's mount tree). +// (3) major:minor: the value of st_dev for files on this filesystem (see stat(2)). +// (4) root: the pathname of the directory in the filesystem which forms the root of this mount. +// (5) mount point: the pathname of the mount point relative to the process's root directory. +// (6) mount options: per-mount options (see mount(2)). +// (7) optional fields: zero or more fields of the form "tag[:value]"; see below. +// (8) separator: the end of the optional fields is marked by a single hyphen. +// (9) filesystem type: the filesystem type in the form "type[.subtype]". +// (10) mount source: filesystem-specific information or "none". +// (11) super options: per-superblock options (see mount(2)). +type mountInfo struct { + Root string + MountPoint string + FilesystemType string + SuperOptions string +} + +// parseMountInfoLine parses a line from the mountinfo file. +func parseMountInfoLine(line string) (mountInfo, error) { + if line == "" { + return mountInfo{}, errors.New("empty line") + } + + fieldss := strings.SplitN(line, " - ", 2) + if len(fieldss) != 2 { + return mountInfo{}, fmt.Errorf("invalid separator") + } + + fields1 := strings.Split(fieldss[0], " ") + if len(fields1) < 6 { + return mountInfo{}, fmt.Errorf("not enough fields before separator: %v", fields1) + } else if len(fields1) > 7 { + return mountInfo{}, fmt.Errorf("too many fields before separator: %v", fields1) + } else if len(fields1) == 6 { + fields1 = append(fields1, "") + } + + fields2 := strings.Split(fieldss[1], " ") + if len(fields2) < 3 { + return mountInfo{}, fmt.Errorf("not enough fields after separator: %v", fields2) + } else if len(fields2) > 3 { + return mountInfo{}, fmt.Errorf("too many fields after separator: %v", fields2) + } + + return mountInfo{ + Root: fields1[3], + MountPoint: fields1[4], + FilesystemType: fields2[0], + SuperOptions: fields2[2], + }, nil +} + +// parseMountInfo parses the mountinfo file. +func parseMountInfo(r io.Reader) ([]mountInfo, error) { + var ( + s = bufio.NewScanner(r) + mis []mountInfo + ) + for s.Scan() { + line := s.Text() + + mi, err := parseMountInfoLine(line) + if err != nil { + return nil, fmt.Errorf("failed to parse mountinfo file %q: %w", line, err) + } + + mis = append(mis, mi) + } + if err := s.Err(); err != nil { + return nil, err + } + + return mis, nil +} + +// https://www.man7.org/linux/man-pages/man7/cgroups.7.html +// +// 5:cpuacct,cpu,cpuset:/daemons +// (1) (2) (3) +// +// (1) hierarchy ID: +// +// cgroups version 1 hierarchies, this field +// contains a unique hierarchy ID number that can be +// matched to a hierarchy ID in /proc/cgroups. For the +// cgroups version 2 hierarchy, this field contains the +// value 0. +// +// (2) controller list: +// +// For cgroups version 1 hierarchies, this field +// contains a comma-separated list of the controllers +// bound to the hierarchy. For the cgroups version 2 +// hierarchy, this field is empty. +// +// (3) cgroup path: +// +// This field contains the pathname of the control group +// in the hierarchy to which the process belongs. This +// pathname is relative to the mount point of the +// hierarchy. +type cgroupHierarchy struct { + HierarchyID string + ControllerList string + CgroupPath string +} + +// parseCgroupHierarchyLine parses a line from the cgroup file. +func parseCgroupHierarchyLine(line string) (cgroupHierarchy, error) { + if line == "" { + return cgroupHierarchy{}, errors.New("empty line") + } + + fields := strings.Split(line, ":") + if len(fields) < 3 { + return cgroupHierarchy{}, fmt.Errorf("not enough fields: %v", fields) + } else if len(fields) > 3 { + return cgroupHierarchy{}, fmt.Errorf("too many fields: %v", fields) + } + + return cgroupHierarchy{ + HierarchyID: fields[0], + ControllerList: fields[1], + CgroupPath: fields[2], + }, nil +} + +// parseCgroupFile parses the cgroup file. +func parseCgroupFile(r io.Reader) ([]cgroupHierarchy, error) { + var ( + s = bufio.NewScanner(r) + chs []cgroupHierarchy + ) + for s.Scan() { + line := s.Text() + + ch, err := parseCgroupHierarchyLine(line) + if err != nil { + return nil, fmt.Errorf("failed to parse cgroup file %q: %w", line, err) + } + + chs = append(chs, ch) + } + if err := s.Err(); err != nil { + return nil, err + } + + return chs, nil +} + +// resolveCgroupPath resolves the actual cgroup path from the mountpoint, root, and cgroupRelPath. +func resolveCgroupPath(mountpoint, root, cgroupRelPath string) (string, error) { + rel, err := filepath.Rel(root, cgroupRelPath) + if err != nil { + return "", err + } + + // if the relative path is ".", then the cgroupRelPath is the root itself. + if rel == "." { + return mountpoint, nil + } + + // if the relative path starts with "..", then it is outside the root. + if strings.HasPrefix(rel, "..") { + return "", fmt.Errorf("invalid cgroup path: %s is not under root %s", cgroupRelPath, root) + } + + return filepath.Join(mountpoint, rel), nil +} diff --git a/memlimit/cgroups_linux.go b/memlimit/cgroups_linux.go index 98a9548..fd2c7e4 100644 --- a/memlimit/cgroups_linux.go +++ b/memlimit/cgroups_linux.go @@ -3,96 +3,30 @@ package memlimit -import ( - "math" - "os" - "path/filepath" - - "github.com/containerd/cgroups/v3" - "github.com/containerd/cgroups/v3/cgroup1" - "github.com/containerd/cgroups/v3/cgroup2" -) - -const ( - cgroupMountPoint = "/sys/fs/cgroup" -) - -// FromCgroup returns the memory limit based on the cgroups version on this system. +// FromCgroup retrieves the memory limit from the cgroup. func FromCgroup() (uint64, error) { - switch cgroups.Mode() { - case cgroups.Legacy: - return FromCgroupV1() - case cgroups.Hybrid: - return FromCgroupHybrid() - case cgroups.Unified: - return FromCgroupV2() - } - return 0, ErrNoCgroup + return fromCgroup(detectCgroupVersion) } -// FromCgroupV1 returns the memory limit from the cgroup v1. +// FromCgroupV1 retrieves the memory limit from the cgroup v1 controller. +// After v1.0.0, this function could be removed and FromCgroup should be used instead. func FromCgroupV1() (uint64, error) { - cg, err := cgroup1.Load(cgroup1.RootPath, cgroup1.WithHiearchy( - cgroup1.SingleSubsystem(cgroup1.Default, cgroup1.Memory), - )) - if err != nil { - return 0, err - } - - metrics, err := cg.Stat(cgroup1.IgnoreNotExist) - if err != nil { - return 0, err - } - - if limit := metrics.GetMemory().GetHierarchicalMemoryLimit(); limit != 0 && limit != getCgroupV1NoLimit() { - return limit, nil - } - - return 0, ErrNoLimit -} - -func getCgroupV1NoLimit() uint64 { - ps := uint64(os.Getpagesize()) - return math.MaxInt64 / ps * ps + return fromCgroup(func(_ []mountInfo) (bool, bool) { + return true, false + }) } -// FromCgroupHybrid returns the memory limit from the cgroup v1 or v2. -// It checks the cgroup v2 first, and if it fails, it falls back to cgroup v1. +// FromCgroupHybrid retrieves the memory limit from the cgroup v2 and v1 controller sequentially, +// basically, it is equivalent to FromCgroup. +// After v1.0.0, this function could be removed and FromCgroup should be used instead. func FromCgroupHybrid() (uint64, error) { - limit, err := fromCgroupV2(filepath.Join(cgroupMountPoint, "unified")) - if err == nil { - return limit, nil - } else if err != ErrNoLimit { - return 0, err - } - - return FromCgroupV1() + return FromCgroup() } -// FromCgroupV2 returns the memory limit from the cgroup v2. +// FromCgroupV2 retrieves the memory limit from the cgroup v2 controller. +// After v1.0.0, this function could be removed and FromCgroup should be used instead. func FromCgroupV2() (uint64, error) { - return fromCgroupV2(cgroupMountPoint) -} - -func fromCgroupV2(mountPoint string) (uint64, error) { - path, err := cgroup2.NestedGroupPath("") - if err != nil { - return 0, err - } - - m, err := cgroup2.Load(path, cgroup2.WithMountpoint(mountPoint)) - if err != nil { - return 0, err - } - - stats, err := m.Stat() - if err != nil { - return 0, err - } - - if limit := stats.GetMemory().GetUsageLimit(); limit != 0 && limit != math.MaxUint64 { - return limit, nil - } - - return 0, ErrNoLimit + return fromCgroup(func(_ []mountInfo) (bool, bool) { + return false, true + }) } diff --git a/memlimit/cgroups_linux_test.go b/memlimit/cgroups_linux_test.go new file mode 100644 index 0000000..edd529f --- /dev/null +++ b/memlimit/cgroups_linux_test.go @@ -0,0 +1,70 @@ +//go:build linux +// +build linux + +package memlimit + +import ( + "testing" +) + +func TestFromCgroup(t *testing.T) { + if expected == 0 { + t.Skip() + } + + limit, err := FromCgroup() + if cgVersion == 0 && err != ErrNoCgroup { + t.Fatalf("FromCgroup() error = %v, wantErr %v", err, ErrNoCgroup) + } + + if err != nil { + t.Fatalf("FromCgroup() error = %v, wantErr %v", err, nil) + } + if limit != expected { + t.Fatalf("FromCgroup() got = %v, want %v", limit, expected) + } +} + +func TestFromCgroupHybrid(t *testing.T) { + if expected == 0 { + t.Skip() + } + + limit, err := FromCgroupHybrid() + if cgVersion == 0 && err != ErrNoCgroup { + t.Fatalf("FromCgroupHybrid() error = %v, wantErr %v", err, ErrNoCgroup) + } + + if err != nil { + t.Fatalf("FromCgroupHybrid() error = %v, wantErr %v", err, nil) + } + if limit != expected { + t.Fatalf("FromCgroupHybrid() got = %v, want %v", limit, expected) + } +} + +func TestFromCgroupV1(t *testing.T) { + if expected == 0 || cgVersion != 1 { + t.Skip() + } + limit, err := FromCgroupV1() + if err != nil { + t.Fatalf("FromCgroupV1() error = %v, wantErr %v", err, nil) + } + if limit != expected { + t.Fatalf("FromCgroupV1() got = %v, want %v", limit, expected) + } +} + +func TestFromCgroupV2(t *testing.T) { + if expected == 0 || cgVersion != 2 { + t.Skip() + } + limit, err := FromCgroupV2() + if err != nil { + t.Fatalf("FromCgroupV2() error = %v, wantErr %v", err, nil) + } + if limit != expected { + t.Fatalf("FromCgroupV2() got = %v, want %v", limit, expected) + } +} diff --git a/memlimit/cgroups_test.go b/memlimit/cgroups_test.go index 537b764..56e3122 100644 --- a/memlimit/cgroups_test.go +++ b/memlimit/cgroups_test.go @@ -1,67 +1,245 @@ -//go:build linux -// +build linux - package memlimit import ( + "reflect" "testing" - - "github.com/containerd/cgroups/v3" ) -func TestFromCgroup(t *testing.T) { - if expected == 0 { - t.Skip() +func TestParseMountInfoLine(t *testing.T) { + tests := []struct { + name string + input string + want mountInfo + wantErr string + }{ + { + name: "valid line with optional field", + input: "36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue", + want: mountInfo{ + Root: "/mnt1", + MountPoint: "/mnt2", + FilesystemType: "ext3", + SuperOptions: "rw,errors=continue", + }, + }, + { + name: "valid line without optional field", + input: "731 771 0:59 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw", + want: mountInfo{ + Root: "/sysrq-trigger", + MountPoint: "/proc/sysrq-trigger", + FilesystemType: "proc", + SuperOptions: "rw", + }, + }, + { + name: "valid line with minimal fields (no optional fields)", + input: "25 1 0:22 / /dev rw - devtmpfs udev rw", + want: mountInfo{ + Root: "/", + MountPoint: "/dev", + FilesystemType: "devtmpfs", + SuperOptions: "rw", + }, + }, + { + name: "no separator", + input: "36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 ext3 /dev/root rw,errors=continue", + wantErr: `invalid separator`, + }, + { + name: "not enough fields on left side", + input: "36 35 98:0 /mnt1 /mnt2 - ext3 /dev/root rw,errors=continue", + wantErr: `not enough fields before separator: [36 35 98:0 /mnt1 /mnt2]`, + }, + { + name: "not enough fields on right side", + input: "36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3", + wantErr: `not enough fields after separator: [ext3]`, + }, + { + name: "too many fields on left side", + input: "36 35 98:0 /mnt1 /mnt2 rw,noatime extra master:1 - ext3 /dev/root rw,errors=continue", + wantErr: `too many fields before separator: [36 35 98:0 /mnt1 /mnt2 rw,noatime extra master:1]`, + }, + { + name: "too many fields on right side", + input: "36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw extra", + wantErr: `too many fields after separator: [ext3 /dev/root rw extra]`, + }, + { + name: "empty line", + input: "", + wantErr: `empty line`, + }, + { + name: "6 fields on left side (no optional field), should add empty optional field", + input: "100 1 8:2 / /data rw - ext4 /dev/sda2 rw,relatime", + want: mountInfo{ + Root: "/", + MountPoint: "/data", + FilesystemType: "ext4", + SuperOptions: "rw,relatime", + }, + }, } - limit, err := FromCgroup() - if cgVersion == cgroups.Unavailable && err != ErrNoCgroup { - t.Fatalf("FromCgroup() error = %v, wantErr %v", err, ErrNoCgroup) - } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseMountInfoLine(tt.input) + if tt.wantErr != "" { + if err == nil { + t.Fatalf("expected an error containing %q, got nil", tt.wantErr) + } + if err.Error() != tt.wantErr { + t.Fatalf("expected error containing %q, got %q", tt.wantErr, err.Error()) + } + return + } - if err != nil { - t.Fatalf("FromCgroup() error = %v, wantErr %v", err, nil) - } - if limit != expected { - t.Fatalf("FromCgroup() got = %v, want %v", limit, expected) - } -} + if err != nil { + t.Fatalf("unexpected error: %v", err) + } -func TestFromCgroupV1(t *testing.T) { - if expected == 0 || cgVersion != cgroups.Legacy { - t.Skip() - } - limit, err := FromCgroupV1() - if err != nil { - t.Fatalf("FromCgroupV1() error = %v, wantErr %v", err, nil) - } - if limit != expected { - t.Fatalf("FromCgroupV1() got = %v, want %v", limit, expected) + if !reflect.DeepEqual(got, tt.want) { + t.Fatalf("expected %+v, got %+v", tt.want, got) + } + }) } } -func TestFromCgroupHybrid(t *testing.T) { - if expected == 0 || cgVersion != cgroups.Hybrid { - t.Skip() - } - limit, err := FromCgroupHybrid() - if err != nil { - t.Fatalf("FromCgroupHybrid() error = %v, wantErr %v", err, nil) +func TestParseCgroupHierarchyLine(t *testing.T) { + tests := []struct { + name string + input string + want cgroupHierarchy + wantErr string + }{ + { + name: "valid line with multiple controllers", + input: "5:cpuacct,cpu,cpuset:/daemons", + want: cgroupHierarchy{ + HierarchyID: "5", + ControllerList: "cpuacct,cpu,cpuset", + CgroupPath: "/daemons", + }, + }, + { + name: "valid line with no controllers (cgroup v2)", + input: "0::/system.slice/docker.service", + want: cgroupHierarchy{ + HierarchyID: "0", + ControllerList: "", + CgroupPath: "/system.slice/docker.service", + }, + }, + { + name: "invalid line - only two fields", + input: "5:cpuacct,cpu,cpuset", + wantErr: "not enough fields: [5 cpuacct,cpu,cpuset]", + }, + { + name: "invalid line - too many fields", + input: "5:cpuacct,cpu:cpuset:/daemons:extra", + wantErr: "too many fields: [5 cpuacct,cpu cpuset /daemons extra]", + }, + { + name: "empty line", + input: "", + wantErr: "empty line", + }, + { + name: "line with empty controller list but valid fields", + input: "2::/my_cgroup", + want: cgroupHierarchy{ + HierarchyID: "2", + ControllerList: "", + CgroupPath: "/my_cgroup", + }, + }, } - if limit != expected { - t.Fatalf("FromCgroupHybrid() got = %v, want %v", limit, expected) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseCgroupHierarchyLine(tt.input) + if tt.wantErr != "" { + if err == nil { + t.Fatalf("expected an error containing %q, got nil", tt.wantErr) + } + if err.Error() != tt.wantErr { + t.Fatalf("expected error containing %q, got %q", tt.wantErr, err.Error()) + } + return + } + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if !reflect.DeepEqual(got, tt.want) { + t.Fatalf("expected %+v, got %+v", tt.want, got) + } + }) } } -func TestFromCgroupV2(t *testing.T) { - if expected == 0 || cgVersion != cgroups.Unified { - t.Skip() +func TestResolveCgroupPath(t *testing.T) { + tests := []struct { + name string + mountpoint string + root string + cgroupRelPath string + want string + wantErr string + }{ + { + name: "exact match with both root and cgroupRelPath as '/'", + mountpoint: "/fake/mount", + root: "/", + cgroupRelPath: "/", + want: "/fake/mount", + }, + { + name: "exact match with a non-root path", + mountpoint: "/fake/mount", + root: "/container0", + cgroupRelPath: "/container0", + want: "/fake/mount", + }, + { + name: "valid subpath under root", + mountpoint: "/fake/mount", + root: "/container0", + cgroupRelPath: "/container0/group1", + want: "/fake/mount/group1", + }, + { + name: "invalid cgroup path outside root", + mountpoint: "/fake/mount", + root: "/container0", + cgroupRelPath: "/other_container", + wantErr: "invalid cgroup path: /other_container is not under root /container0", + }, } - limit, err := FromCgroupV2() - if err != nil { - t.Fatalf("FromCgroupV2() error = %v, wantErr %v", err, nil) - } - if limit != expected { - t.Fatalf("FromCgroupV2() got = %v, want %v", limit, expected) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := resolveCgroupPath(tt.mountpoint, tt.root, tt.cgroupRelPath) + if tt.wantErr != "" { + if err == nil { + t.Fatalf("expected an error containing %q, got nil", tt.wantErr) + } + if err.Error() != tt.wantErr { + t.Fatalf("expected error containing %q, got %q", tt.wantErr, err.Error()) + } + return + } + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if got != tt.want { + t.Fatalf("expected path %q, got %q", tt.want, got) + } + }) } } diff --git a/memlimit/memlimit_common_test.go b/memlimit/memlimit_common_test.go deleted file mode 100644 index e65aa00..0000000 --- a/memlimit/memlimit_common_test.go +++ /dev/null @@ -1,299 +0,0 @@ -package memlimit - -import ( - "fmt" - "math" - "runtime/debug" - "sync/atomic" - "testing" - "time" -) - -func TestLimit(t *testing.T) { - type args struct { - limit uint64 - } - tests := []struct { - name string - args args - want uint64 - wantErr error - }{ - { - name: "0bytes", - args: args{ - limit: 0, - }, - want: 0, - wantErr: nil, - }, - { - name: "1kib", - args: args{ - limit: 1024, - }, - want: 1024, - wantErr: nil, - }, - { - name: "1mib", - args: args{ - limit: 1024 * 1024, - }, - want: 1024 * 1024, - wantErr: nil, - }, - { - name: "1gib", - args: args{ - limit: 1024 * 1024 * 1024, - }, - want: 1024 * 1024 * 1024, - wantErr: nil, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := Limit(tt.args.limit)() - if err != tt.wantErr { - t.Errorf("Limit() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("Limit() got = %v, want %v", got, tt.want) - } - }) - } -} - -func TestSetGoMemLimitWithProvider(t *testing.T) { - type args struct { - provider Provider - ratio float64 - } - tests := []struct { - name string - args args - want int64 - wantErr error - gomemlimit int64 - }{ - { - name: "Limit_0.5", - args: args{ - provider: Limit(1024 * 1024 * 1024), - ratio: 0.5, - }, - want: 536870912, - wantErr: nil, - gomemlimit: 536870912, - }, - { - name: "Limit_0.9", - args: args{ - provider: Limit(1024 * 1024 * 1024), - ratio: 0.9, - }, - want: 966367641, - wantErr: nil, - gomemlimit: 966367641, - }, - { - name: "Limit_0.9_math.MaxUint64", - args: args{ - provider: Limit(math.MaxUint64), - ratio: 0.9, - }, - want: math.MaxInt64, - wantErr: nil, - gomemlimit: math.MaxInt64, - }, - { - name: "Limit_0.9_math.MaxUint64", - args: args{ - provider: Limit(math.MaxUint64), - ratio: 0.9, - }, - want: math.MaxInt64, - wantErr: nil, - gomemlimit: math.MaxInt64, - }, - { - name: "Limit_0.45_math.MaxUint64", - args: args{ - provider: Limit(math.MaxUint64), - ratio: 0.45, - }, - want: 8301034833169298432, - wantErr: nil, - gomemlimit: 8301034833169298432, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Cleanup(func() { - debug.SetMemoryLimit(math.MaxInt64) - }) - got, err := SetGoMemLimitWithProvider(tt.args.provider, tt.args.ratio) - if err != tt.wantErr { - t.Errorf("SetGoMemLimitWithProvider() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("SetGoMemLimitWithProvider() got = %v, want %v", got, tt.want) - } - if debug.SetMemoryLimit(-1) != tt.gomemlimit { - t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", debug.SetMemoryLimit(-1), tt.gomemlimit) - } - }) - } -} - -func TestSetGoMemLimitWithOpts(t *testing.T) { - tests := []struct { - name string - opts []Option - want int64 - wantErr error - gomemlimit int64 - }{ - { - name: "unknown error", - opts: []Option{ - WithProvider(func() (uint64, error) { - return 0, fmt.Errorf("unknown error") - }), - }, - want: 0, - wantErr: fmt.Errorf("failed to set GOMEMLIMIT: unknown error"), - gomemlimit: math.MaxInt64, - }, - { - name: "ErrNoLimit", - opts: []Option{ - WithProvider(func() (uint64, error) { - return 0, ErrNoLimit - }), - }, - want: 0, - wantErr: nil, - gomemlimit: math.MaxInt64, - }, - { - name: "wrapped ErrNoLimit", - opts: []Option{ - WithProvider(func() (uint64, error) { - return 0, fmt.Errorf("wrapped: %w", ErrNoLimit) - }), - }, - want: 0, - wantErr: nil, - gomemlimit: math.MaxInt64, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := SetGoMemLimitWithOpts(tt.opts...) - if tt.wantErr != nil && err.Error() != tt.wantErr.Error() { - t.Errorf("SetGoMemLimitWithOpts() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("SetGoMemLimitWithOpts() got = %v, want %v", got, tt.want) - } - if debug.SetMemoryLimit(-1) != tt.gomemlimit { - t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", debug.SetMemoryLimit(-1), tt.gomemlimit) - } - }) - } -} - -func TestSetGoMemLimitWithOpts_rollbackOnPanic(t *testing.T) { - t.Cleanup(func() { - debug.SetMemoryLimit(math.MaxInt64) - }) - - limit := int64(987654321) - _ = debug.SetMemoryLimit(987654321) - _, err := SetGoMemLimitWithOpts( - WithProvider(func() (uint64, error) { - debug.SetMemoryLimit(123456789) - panic("panic") - }), - WithRatio(1), - ) - if err == nil { - t.Error("SetGoMemLimtWithOpts() error = nil, want panic") - } - - curr := debug.SetMemoryLimit(-1) - if curr != limit { - t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", curr, limit) - } -} - -func TestSetGoMemLimitWithOpts_WithRefreshInterval(t *testing.T) { - t.Cleanup(func() { - debug.SetMemoryLimit(math.MaxInt64) - }) - - var limit atomic.Int64 - output, err := SetGoMemLimitWithOpts( - WithProvider(func() (uint64, error) { - l := limit.Load() - if l == 0 { - return 0, ErrNoLimit - } - return uint64(l), nil - }), - WithRatio(1), - WithRefreshInterval(10*time.Millisecond), - ) - if err != nil { - t.Errorf("SetGoMemLimitWithOpts() error = %v", err) - } else if output != limit.Load() { - t.Errorf("SetGoMemLimitWithOpts() got = %v, want %v", output, limit.Load()) - } - - // 1. no limit - curr := debug.SetMemoryLimit(-1) - if curr != math.MaxInt64 { - t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", curr, limit.Load()) - } - - // 2. max limit - limit.Add(math.MaxInt64) - time.Sleep(100 * time.Millisecond) - - curr = debug.SetMemoryLimit(-1) - if curr != math.MaxInt64 { - t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", curr, math.MaxInt64) - } - - // 3. adjust limit - limit.Add(-1024) - time.Sleep(100 * time.Millisecond) - - curr = debug.SetMemoryLimit(-1) - if curr != math.MaxInt64-1024 { - t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", curr, math.MaxInt64-1024) - } - - // 4. no limit again (don't change the limit) - limit.Store(0) - time.Sleep(100 * time.Millisecond) - - curr = debug.SetMemoryLimit(-1) - if curr != math.MaxInt64-1024 { - t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", curr, math.MaxInt64-1024) - } - - // 5. new limit - limit.Store(math.MaxInt32) - time.Sleep(100 * time.Millisecond) - - curr = debug.SetMemoryLimit(-1) - if curr != math.MaxInt32 { - t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", curr, math.MaxInt32) - } -} diff --git a/memlimit/memlimit_linux_test.go b/memlimit/memlimit_linux_test.go new file mode 100644 index 0000000..8b989ab --- /dev/null +++ b/memlimit/memlimit_linux_test.go @@ -0,0 +1,233 @@ +//go:build linux +// +build linux + +package memlimit + +import ( + "flag" + "math" + "os" + "runtime/debug" + "testing" +) + +var ( + cgVersion uint64 + expected uint64 + expectedSystem uint64 +) + +func TestMain(m *testing.M) { + flag.Uint64Var(&expected, "expected", 0, "Expected cgroup's memory limit") + flag.Uint64Var(&expectedSystem, "expected-system", 0, "Expected system memory limit") + flag.Uint64Var(&cgVersion, "cgroup-version", 0, "Cgroup version") + flag.Parse() + + os.Exit(m.Run()) +} + +func TestSetGoMemLimit(t *testing.T) { + type args struct { + ratio float64 + } + tests := []struct { + name string + args args + want int64 + wantErr error + gomemlimit int64 + skip bool + }{ + { + name: "0.5", + args: args{ + ratio: 0.5, + }, + want: int64(float64(expected) * 0.5), + wantErr: nil, + gomemlimit: int64(float64(expected) * 0.5), + skip: expected == 0 || cgVersion == 0, + }, + { + name: "0.9", + args: args{ + ratio: 0.9, + }, + want: int64(float64(expected) * 0.9), + wantErr: nil, + gomemlimit: int64(float64(expected) * 0.9), + skip: expected == 0 || cgVersion == 0, + }, + { + name: "Unavailable", + args: args{ + ratio: 0.9, + }, + want: 0, + wantErr: ErrCgroupsNotSupported, + gomemlimit: math.MaxInt64, + skip: cgVersion != 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.skip { + t.Skip() + } + t.Cleanup(func() { + debug.SetMemoryLimit(math.MaxInt64) + }) + got, err := SetGoMemLimit(tt.args.ratio) + if err != tt.wantErr { + t.Errorf("SetGoMemLimit() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("SetGoMemLimit() got = %v, want %v", got, tt.want) + } + if debug.SetMemoryLimit(-1) != tt.gomemlimit { + t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", debug.SetMemoryLimit(-1), tt.gomemlimit) + } + }) + } +} + +func TestSetGoMemLimitWithProvider_WithCgroupProvider(t *testing.T) { + type args struct { + provider Provider + ratio float64 + } + tests := []struct { + name string + args args + want int64 + wantErr error + gomemlimit int64 + skip bool + }{ + { + name: "FromCgroup", + args: args{ + provider: FromCgroup, + ratio: 0.9, + }, + want: int64(float64(expected) * 0.9), + wantErr: nil, + gomemlimit: int64(float64(expected) * 0.9), + skip: expected == 0 || cgVersion == 0, + }, + { + name: "FromCgroup_Unavailable", + args: args{ + provider: FromCgroup, + ratio: 0.9, + }, + want: 0, + wantErr: ErrNoCgroup, + gomemlimit: math.MaxInt64, + skip: expected == 0 || cgVersion != 0, + }, + { + name: "FromCgroupV1", + args: args{ + provider: FromCgroupV1, + ratio: 0.9, + }, + want: int64(float64(expected) * 0.9), + wantErr: nil, + gomemlimit: int64(float64(expected) * 0.9), + skip: expected == 0 || cgVersion != 1, + }, + { + name: "FromCgroupHybrid", + args: args{ + provider: FromCgroupHybrid, + ratio: 0.9, + }, + want: int64(float64(expected) * 0.9), + wantErr: nil, + gomemlimit: int64(float64(expected) * 0.9), + skip: expected == 0 || cgVersion != 1, + }, + { + name: "FromCgroupV2", + args: args{ + provider: FromCgroupV2, + ratio: 0.9, + }, + want: int64(float64(expected) * 0.9), + wantErr: nil, + gomemlimit: int64(float64(expected) * 0.9), + skip: expected == 0 || cgVersion != 2, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.skip { + t.Skip() + } + t.Cleanup(func() { + debug.SetMemoryLimit(math.MaxInt64) + }) + got, err := SetGoMemLimitWithProvider(tt.args.provider, tt.args.ratio) + if err != tt.wantErr { + t.Errorf("SetGoMemLimitWithProvider() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("SetGoMemLimitWithProvider() got = %v, want %v", got, tt.want) + } + if debug.SetMemoryLimit(-1) != tt.gomemlimit { + t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", debug.SetMemoryLimit(-1), tt.gomemlimit) + } + }) + } +} + +func TestSetGoMemLimitWithProvider_WithSystemProvider(t *testing.T) { + type args struct { + provider Provider + ratio float64 + } + tests := []struct { + name string + args args + want int64 + wantErr error + gomemlimit int64 + skip bool + }{ + { + name: "FromSystem", + args: args{ + provider: FromSystem, + ratio: 0.9, + }, + want: int64(float64(expectedSystem) * 0.9), + wantErr: nil, + gomemlimit: int64(float64(expectedSystem) * 0.9), + skip: expectedSystem == 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.skip { + t.Skip() + } + t.Cleanup(func() { + debug.SetMemoryLimit(math.MaxInt64) + }) + got, err := SetGoMemLimitWithProvider(tt.args.provider, tt.args.ratio) + if err != tt.wantErr { + t.Errorf("SetGoMemLimitWithProvider() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("SetGoMemLimitWithProvider() got = %v, want %v", got, tt.want) + } + if debug.SetMemoryLimit(-1) != tt.gomemlimit { + t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", debug.SetMemoryLimit(-1), tt.gomemlimit) + } + }) + } +} diff --git a/memlimit/memlimit_test.go b/memlimit/memlimit_test.go index 21d7f22..ab41d4e 100644 --- a/memlimit/memlimit_test.go +++ b/memlimit/memlimit_test.go @@ -1,103 +1,72 @@ -//go:build linux -// +build linux - package memlimit import ( - "flag" - "log" + "fmt" "math" - "os" "runtime/debug" + "sync/atomic" "testing" - - "github.com/containerd/cgroups/v3" -) - -var ( - cgVersion cgroups.CGMode - expected uint64 - expectedSystem uint64 + "time" ) -func TestMain(m *testing.M) { - flag.Uint64Var(&expected, "expected", 0, "Expected cgroup's memory limit") - flag.Uint64Var(&expectedSystem, "expected-system", 0, "Expected system memory limit") - flag.Parse() - - cgVersion = cgroups.Mode() - log.Println("Cgroups version:", cgVersion) - - os.Exit(m.Run()) -} - -func TestSetGoMemLimit(t *testing.T) { +func TestLimit(t *testing.T) { type args struct { - ratio float64 + limit uint64 } tests := []struct { - name string - args args - want int64 - wantErr error - gomemlimit int64 - skip bool + name string + args args + want uint64 + wantErr error }{ { - name: "0.5", + name: "0bytes", args: args{ - ratio: 0.5, + limit: 0, }, - want: int64(float64(expected) * 0.5), - wantErr: nil, - gomemlimit: int64(float64(expected) * 0.5), - skip: expected == 0 || cgVersion == cgroups.Unavailable, + want: 0, + wantErr: nil, }, { - name: "0.9", + name: "1kib", args: args{ - ratio: 0.9, + limit: 1024, }, - want: int64(float64(expected) * 0.9), - wantErr: nil, - gomemlimit: int64(float64(expected) * 0.9), - skip: expected == 0 || cgVersion == cgroups.Unavailable, + want: 1024, + wantErr: nil, }, { - name: "Unavailable", + name: "1mib", args: args{ - ratio: 0.9, + limit: 1024 * 1024, }, - want: 0, - wantErr: ErrCgroupsNotSupported, - gomemlimit: math.MaxInt64, - skip: cgVersion != cgroups.Unavailable, + want: 1024 * 1024, + wantErr: nil, + }, + { + name: "1gib", + args: args{ + limit: 1024 * 1024 * 1024, + }, + want: 1024 * 1024 * 1024, + wantErr: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if tt.skip { - t.Skip() - } - t.Cleanup(func() { - debug.SetMemoryLimit(math.MaxInt64) - }) - got, err := SetGoMemLimit(tt.args.ratio) + got, err := Limit(tt.args.limit)() if err != tt.wantErr { - t.Errorf("SetGoMemLimit() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("Limit() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.want { - t.Errorf("SetGoMemLimit() got = %v, want %v", got, tt.want) - } - if debug.SetMemoryLimit(-1) != tt.gomemlimit { - t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", debug.SetMemoryLimit(-1), tt.gomemlimit) + t.Errorf("Limit() got = %v, want %v", got, tt.want) } }) } } -func TestSetGoMemLimitWithProvider_WithCgroupProvider(t *testing.T) { +func TestSetGoMemLimitWithProvider(t *testing.T) { type args struct { provider Provider ratio float64 @@ -108,69 +77,60 @@ func TestSetGoMemLimitWithProvider_WithCgroupProvider(t *testing.T) { want int64 wantErr error gomemlimit int64 - skip bool }{ { - name: "FromCgroup", + name: "Limit_0.5", args: args{ - provider: FromCgroup, - ratio: 0.9, + provider: Limit(1024 * 1024 * 1024), + ratio: 0.5, }, - want: int64(float64(expected) * 0.9), + want: 536870912, wantErr: nil, - gomemlimit: int64(float64(expected) * 0.9), - skip: expected == 0 || cgVersion == cgroups.Unavailable, + gomemlimit: 536870912, }, { - name: "FromCgroup_Unavaliable", + name: "Limit_0.9", args: args{ - provider: FromCgroup, + provider: Limit(1024 * 1024 * 1024), ratio: 0.9, }, - want: 0, - wantErr: ErrNoCgroup, - gomemlimit: math.MaxInt64, - skip: expected == 0 || cgVersion != cgroups.Unavailable, + want: 966367641, + wantErr: nil, + gomemlimit: 966367641, }, { - name: "FromCgroupV1", + name: "Limit_0.9_math.MaxUint64", args: args{ - provider: FromCgroupV1, + provider: Limit(math.MaxUint64), ratio: 0.9, }, - want: int64(float64(expected) * 0.9), + want: math.MaxInt64, wantErr: nil, - gomemlimit: int64(float64(expected) * 0.9), - skip: expected == 0 || cgVersion != cgroups.Legacy, + gomemlimit: math.MaxInt64, }, { - name: "FromCgroupHybrid", + name: "Limit_0.9_math.MaxUint64", args: args{ - provider: FromCgroupHybrid, + provider: Limit(math.MaxUint64), ratio: 0.9, }, - want: int64(float64(expected) * 0.9), + want: math.MaxInt64, wantErr: nil, - gomemlimit: int64(float64(expected) * 0.9), - skip: expected == 0 || cgVersion != cgroups.Hybrid, + gomemlimit: math.MaxInt64, }, { - name: "FromCgroupV2", + name: "Limit_0.45_math.MaxUint64", args: args{ - provider: FromCgroupV2, - ratio: 0.9, + provider: Limit(math.MaxUint64), + ratio: 0.45, }, - want: int64(float64(expected) * 0.9), + want: 8301034833169298432, wantErr: nil, - gomemlimit: int64(float64(expected) * 0.9), - skip: expected == 0 || cgVersion != cgroups.Unified, + gomemlimit: 8301034833169298432, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if tt.skip { - t.Skip() - } t.Cleanup(func() { debug.SetMemoryLimit(math.MaxInt64) }) @@ -189,46 +149,57 @@ func TestSetGoMemLimitWithProvider_WithCgroupProvider(t *testing.T) { } } -func TestSetGoMemLimitWithProvider_WithSystemProvider(t *testing.T) { - type args struct { - provider Provider - ratio float64 - } +func TestSetGoMemLimitWithOpts(t *testing.T) { tests := []struct { name string - args args + opts []Option want int64 wantErr error gomemlimit int64 - skip bool }{ { - name: "FromSystem", - args: args{ - provider: FromSystem, - ratio: 0.9, + name: "unknown error", + opts: []Option{ + WithProvider(func() (uint64, error) { + return 0, fmt.Errorf("unknown error") + }), }, - want: int64(float64(expectedSystem) * 0.9), + want: 0, + wantErr: fmt.Errorf("failed to set GOMEMLIMIT: unknown error"), + gomemlimit: math.MaxInt64, + }, + { + name: "ErrNoLimit", + opts: []Option{ + WithProvider(func() (uint64, error) { + return 0, ErrNoLimit + }), + }, + want: 0, wantErr: nil, - gomemlimit: int64(float64(expectedSystem) * 0.9), - skip: expectedSystem == 0, + gomemlimit: math.MaxInt64, + }, + { + name: "wrapped ErrNoLimit", + opts: []Option{ + WithProvider(func() (uint64, error) { + return 0, fmt.Errorf("wrapped: %w", ErrNoLimit) + }), + }, + want: 0, + wantErr: nil, + gomemlimit: math.MaxInt64, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if tt.skip { - t.Skip() - } - t.Cleanup(func() { - debug.SetMemoryLimit(math.MaxInt64) - }) - got, err := SetGoMemLimitWithProvider(tt.args.provider, tt.args.ratio) - if err != tt.wantErr { - t.Errorf("SetGoMemLimitWithProvider() error = %v, wantErr %v", err, tt.wantErr) + got, err := SetGoMemLimitWithOpts(tt.opts...) + if tt.wantErr != nil && err.Error() != tt.wantErr.Error() { + t.Errorf("SetGoMemLimitWithOpts() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.want { - t.Errorf("SetGoMemLimitWithProvider() got = %v, want %v", got, tt.want) + t.Errorf("SetGoMemLimitWithOpts() got = %v, want %v", got, tt.want) } if debug.SetMemoryLimit(-1) != tt.gomemlimit { t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", debug.SetMemoryLimit(-1), tt.gomemlimit) @@ -236,3 +207,93 @@ func TestSetGoMemLimitWithProvider_WithSystemProvider(t *testing.T) { }) } } + +func TestSetGoMemLimitWithOpts_rollbackOnPanic(t *testing.T) { + t.Cleanup(func() { + debug.SetMemoryLimit(math.MaxInt64) + }) + + limit := int64(987654321) + _ = debug.SetMemoryLimit(987654321) + _, err := SetGoMemLimitWithOpts( + WithProvider(func() (uint64, error) { + debug.SetMemoryLimit(123456789) + panic("panic") + }), + WithRatio(1), + ) + if err == nil { + t.Error("SetGoMemLimitWithOpts() error = nil, want panic") + } + + curr := debug.SetMemoryLimit(-1) + if curr != limit { + t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", curr, limit) + } +} + +func TestSetGoMemLimitWithOpts_WithRefreshInterval(t *testing.T) { + t.Cleanup(func() { + debug.SetMemoryLimit(math.MaxInt64) + }) + + var limit atomic.Int64 + output, err := SetGoMemLimitWithOpts( + WithProvider(func() (uint64, error) { + l := limit.Load() + if l == 0 { + return 0, ErrNoLimit + } + return uint64(l), nil + }), + WithRatio(1), + WithRefreshInterval(10*time.Millisecond), + ) + if err != nil { + t.Errorf("SetGoMemLimitWithOpts() error = %v", err) + } else if output != limit.Load() { + t.Errorf("SetGoMemLimitWithOpts() got = %v, want %v", output, limit.Load()) + } + + // 1. no limit + curr := debug.SetMemoryLimit(-1) + if curr != math.MaxInt64 { + t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", curr, limit.Load()) + } + + // 2. max limit + limit.Add(math.MaxInt64) + time.Sleep(100 * time.Millisecond) + + curr = debug.SetMemoryLimit(-1) + if curr != math.MaxInt64 { + t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", curr, math.MaxInt64) + } + + // 3. adjust limit + limit.Add(-1024) + time.Sleep(100 * time.Millisecond) + + curr = debug.SetMemoryLimit(-1) + if curr != math.MaxInt64-1024 { + t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", curr, math.MaxInt64-1024) + } + + // 4. no limit again (don't change the limit) + limit.Store(0) + time.Sleep(100 * time.Millisecond) + + curr = debug.SetMemoryLimit(-1) + if curr != math.MaxInt64-1024 { + t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", curr, math.MaxInt64-1024) + } + + // 5. new limit + limit.Store(math.MaxInt32) + time.Sleep(100 * time.Millisecond) + + curr = debug.SetMemoryLimit(-1) + if curr != math.MaxInt32 { + t.Errorf("debug.SetMemoryLimit(-1) got = %v, want %v", curr, math.MaxInt32) + } +}