diff --git a/README.md b/README.md index 1a648e7..49b3d43 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,8 @@ [![License: GPL v2](https://img.shields.io/badge/License-GPL_v2-blue.svg)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) -cgroup-warden is a daemon that monitors [cgroups](https://man7.org/linux/man-pages/man7/cgroups.7.html), and provides a way to set resource limits on those cgroups. Created to support CHPC's [Arbiter](https://github.com/chpc-uofu/arbiter), but it may also run stand-alone. +cgroup-warden is a daemon that provides a way to set resource limits on those cgroups. Created to support CHPC's [Arbiter](https://github.com/chpc-uofu/arbiter), but it may also run stand-alone. -## Metrics - -cgroup-warden exposes cgroup metrics in the [OpenMetrics](https://openmetrics.io/) format through the `/metrics` endpoint. These metrics are -provided by the [cgroup_exporter](https://github.com/treydock/cgroup_exporter), with cgroup-warden just wrapping the collector. ## Control diff --git a/control.go b/control.go index 8167d78..8184625 100644 --- a/control.go +++ b/control.go @@ -7,14 +7,12 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "net/http" "os/user" "regexp" - "strconv" systemd "github.com/coreos/go-systemd/v22/dbus" - "github.com/go-kit/log" - "github.com/go-kit/log/level" dbus "github.com/godbus/dbus/v5" ) @@ -42,12 +40,12 @@ type controlRequest struct { } type controlResponse struct { - Message string `json:"message"` - Unit string `json:"unit,omitempty"` - Username string `json:"username,omitempty"` + Unit string `json:"unit"` + Username string `json:"username"` + Property property `json:"property"` } -func controlHandler(logger log.Logger) http.HandlerFunc { +func controlHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var err error var request controlRequest @@ -55,24 +53,29 @@ func controlHandler(logger log.Logger) http.HandlerFunc { err = json.NewDecoder(r.Body).Decode(&request) if err != nil { + slog.Warn("unable to decode json request", "err", err.Error()) http.Error(w, err.Error(), http.StatusBadRequest) return } response.Unit, response.Username, err = resolveUser(request) if err != nil { + slog.Warn("unable to resolve user", "err", err.Error(), "request", request) http.Error(w, err.Error(), http.StatusBadRequest) return } - property, err := constructProperty(request.Property) + value, err := dbus.ParseVariant(request.Property.Value, dbus.Signature{}) + property := systemd.Property{Name: request.Property.Name, Value: value} if err != nil { + slog.Warn("unable to construct property", "err", err.Error(), "request", request) http.Error(w, err.Error(), http.StatusBadRequest) return } sysconn, err := newSystemdConn() if err != nil { + slog.Warn("unable to connect to systemd", "err", err.Error()) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -80,6 +83,7 @@ func controlHandler(logger log.Logger) http.HandlerFunc { err = sysconn.conn.SetUnitPropertiesContext(sysconn.ctx, response.Unit, request.Runtime, property) if err != nil { + slog.Warn("unable to set property", "err", err.Error(), "property", property, "unit", response.Unit) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -87,89 +91,45 @@ func controlHandler(logger log.Logger) http.HandlerFunc { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("X-Content-Type-Options", "nosniff") w.WriteHeader(http.StatusOK) + response.Property = request.Property err = json.NewEncoder(w).Encode(response) if err != nil { - level.Error(logger).Log("msg", "error sending response", "err", err) + slog.Error("unable to send encode response", "error", err.Error()) } } } -func constructProperty(candidate property) (systemd.Property, error) { - var value any - var err error - var property systemd.Property - switch candidate.Name { - case "MemoryMax": - value, err = strconv.ParseFloat(candidate.Value, 64) - case "CPUQuotaPerSecUSec": - value, err = strconv.ParseFloat(candidate.Value, 64) - case "MemoryAccounting": - value, err = strconv.ParseBool(candidate.Value) - case "CPUAccounting": - value, err = strconv.ParseBool(candidate.Value) - default: - value, err = nil, fmt.Errorf("%v is not a valid property", candidate.Name) - } - - if err != nil { - return property, err - } +func resolveUser(request controlRequest) (string, string, error) { - property.Value = dbus.MakeVariant(value) - property.Name = candidate.Name - return property, err -} + var unit string + var username string + var err error -func resolveUser(request controlRequest) (username string, unit string, err error) { - if request.Unit != nil && request.Username != nil { - username, err = resolveUsername(*request.Unit) - if err != nil { - return - } - unit, err = resolveUnit(*request.Unit) - if err != nil { - return - } - if username != *request.Username || unit != *request.Unit { - err = errors.New("unit and username do not match") - return - } - } if request.Unit != nil { - username, err = resolveUsername(*request.Unit) - unit = *request.Unit - return + username, err = getUsername(*request.Unit) + return *request.Unit, username, err } + if request.Username != nil { - unit, err = resolveUnit(*request.Username) - username = *request.Username - return + unit, err := getUnit(*request.Username) + return unit, *request.Username, err } - err = errors.New("must provide unit or username") - return + + return unit, username, errors.New("must provide unit or username") } -func resolveUsername(unit string) (username string, err error) { +func getUsername(unit string) (string, error) { re := regexp.MustCompile(`user-(\d+)\.slice`) match := re.FindStringSubmatch(unit) - var usr *user.User if len(match) != 2 { - err = errors.New("invalid unit") - return + return "", errors.New("invalid unit string") } - usr, err = user.LookupId(match[1]) - if err != nil { - return - } - return usr.Username, nil + usr, err := user.LookupId(match[1]) + return usr.Username, err } -func resolveUnit(username string) (unit string, err error) { - var usr *user.User - usr, err = user.Lookup(username) - if err != nil { - return - } - unit = fmt.Sprintf("user-%v.slice", usr.Uid) - return +func getUnit(username string) (string, error) { + usr, err := user.Lookup(username) + unit := fmt.Sprintf("user-%v.slice", usr.Uid) + return unit, err } diff --git a/control_test.go b/control_test.go new file mode 100644 index 0000000..b0054ea --- /dev/null +++ b/control_test.go @@ -0,0 +1,44 @@ +package main + +import "testing" + +func TestResolveUser(t *testing.T) { + var prop property + unit := "user-0.slice" + username := "root" + testCases := []controlRequest{ + {Unit: &unit, Username: nil, Property: prop, Runtime: false}, + {Unit: nil, Username: &username, Property: prop, Runtime: false}, + {Unit: &unit, Username: &username, Property: prop, Runtime: false}, + } + + for _, tc := range testCases { + slice, name, err := resolveUser(tc) + if err != nil || slice != unit || name != username { + t.Fail() + } + } + + badRequest := controlRequest{Unit: nil, Username: nil, Property: prop, Runtime: false} + _, _, err := resolveUser(badRequest) + if err == nil { + t.Fail() + } + +} + +func TestGetUsername(t *testing.T) { + unit := "user-0.slice" + username, err := getUsername(unit) + if err != nil || username != "root" { + t.Fail() + } +} + +func TestGetUnit(t *testing.T) { + username := "root" + unit, err := getUnit(username) + if err != nil || unit != "user-0.slice" { + t.Fail() + } +} diff --git a/go.mod b/go.mod index 9799e40..94ad152 100644 --- a/go.mod +++ b/go.mod @@ -5,35 +5,6 @@ go 1.22 toolchain go1.22.5 require ( - github.com/alecthomas/kingpin/v2 v2.4.0 - github.com/containerd/cgroups v1.1.0 github.com/coreos/go-systemd/v22 v22.5.0 - github.com/go-kit/log v0.2.1 github.com/godbus/dbus/v5 v5.1.0 - github.com/prometheus/client_golang v1.19.1 - github.com/prometheus/common v0.53.0 - github.com/treydock/cgroup_exporter v0.9.1 ) - -require ( - github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cilium/ebpf v0.15.0 // indirect - github.com/containerd/cgroups/v3 v3.0.3 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/docker/go-units v0.5.0 // indirect - github.com/go-logfmt/logfmt v0.6.0 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/opencontainers/runtime-spec v1.2.0 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/procfs v0.15.0 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect - github.com/xhit/go-str2duration/v2 v2.1.0 // indirect - golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect - golang.org/x/sys v0.20.0 // indirect - google.golang.org/protobuf v1.34.1 // indirect -) - -// supports cgroups v2 -replace github.com/treydock/cgroup_exporter => github.com/treydock/cgroup_exporter v1.0.0-rc.4 diff --git a/go.sum b/go.sum index eb07598..5e54cec 100644 --- a/go.sum +++ b/go.sum @@ -1,109 +1,5 @@ -github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= -github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= -github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs= -github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk= -github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso= -github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= -github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= -github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0= -github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0= 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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/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-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= -github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= -github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -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/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -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/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -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/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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE= -github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= -github.com/prometheus/procfs v0.15.0 h1:A82kmvXJq2jTu5YUhSGNlYoxh85zLnKgPz4bMZgI5Ek= -github.com/prometheus/procfs v0.15.0/go.mod h1:Y0RJ/Y5g5wJpkTisOtqwDSo4HwhGmLB4VQSw2sQJLHk= -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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/treydock/cgroup_exporter v1.0.0-rc.4 h1:syobRLC5dTKgAL/cPcv2QnsALrOYSWppymgylDZWehg= -github.com/treydock/cgroup_exporter v1.0.0-rc.4/go.mod h1:eFVfMSWmqAp3qeGTWj1WFzMH2zSIoRMl1RsmWfxQCYg= -github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= -github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -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/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -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/main.go b/main.go index 6df51b2..de79dd3 100644 --- a/main.go +++ b/main.go @@ -3,14 +3,9 @@ package main import ( + "flag" "log" "net/http" - "os" - - "github.com/alecthomas/kingpin/v2" - kitlog "github.com/go-kit/log" - "github.com/prometheus/common/promlog" - "github.com/prometheus/common/promlog/flag" ) func authorize(next http.Handler, secret string) http.Handler { @@ -24,10 +19,9 @@ func authorize(next http.Handler, secret string) http.Handler { }) } -func newServer(paths []string, logger kitlog.Logger) http.Handler { +func newHandler() http.Handler { mux := http.NewServeMux() - mux.Handle("/metrics", metricsHandler(paths, logger)) - mux.Handle("/control", controlHandler(logger)) + mux.Handle("/control", controlHandler()) mux.Handle("/", http.NotFoundHandler()) var handler http.Handler = mux return handler @@ -35,39 +29,36 @@ func newServer(paths []string, logger kitlog.Logger) http.Handler { func main() { var ( - app = kingpin.New("cgroup-warden", "cgroup monitoring and resource control daemon") - paths = app.Flag("paths", "list of cgroup root paths to monitor").Default("/user.slice").Strings() - listen = app.Flag("listen", "address to listen on for telemetery").Default(":2112").String() - tls = app.Flag("tls", "ahether to use tls for telemetry.").Default("false").Bool() - cert = app.Flag("tls.cert", "certificate file to use for TLS verification").Envar("CGROUP_WARDEN_TLS_CERT_FILE").String() - key = app.Flag("tls.key", "key file to use for TLS verification").Envar("CGROUP_WARDEN_TLS_KEY_FILE").String() - bearer = app.Flag("bearer", "bearer token to authorize /control requests").Envar("CGROUP_WARDEN_BEARER_TOKEN").String() + listenAddr string + certFile string + keyFile string + bearerToken string + insecure bool ) - promlogConfig := &promlog.Config{} - flag.AddFlags(app, promlogConfig) - app.DefaultEnvars() - kingpin.MustParse(app.Parse(os.Args[1:])) - logger := promlog.New(promlogConfig) - server := newServer(*paths, logger) + flag.StringVar(&listenAddr, "listenAddr", ":2112", "address to listen on for telemetry") + flag.StringVar(&certFile, "certFile", "", "file containing certificate to use for tls") + flag.StringVar(&keyFile, "keyFile", "", "file containing key to use for tls") + flag.StringVar(&bearerToken, "bearerToken", "", "bearer token to use for authentication") + flag.BoolVar(&insecure, "insecure", false, "disable tls and bearer token authentication") + flag.Parse() - var err error - if *tls { - if *cert == "" { - app.FatalUsage("certificate required for use with TLS") + if !insecure { + if certFile == "" { + log.Fatal("certificate required for use with tls") } - if *key == "" { - app.FatalUsage("key required for use with TLS") + if keyFile == "" { + log.Fatal("key required for use with tls") } - if *bearer != "" { - server = authorize(server, *bearer) + if bearerToken == "" || len(bearerToken) < 16 { + log.Fatal("token of length > 16 required for authentication") } - err = http.ListenAndServeTLS(*listen, *cert, *key, server) - } else { - err = http.ListenAndServe(*listen, server) - } - if err != nil { - log.Fatal(err) + handler := authorize(newHandler(), bearerToken) + log.Fatal(http.ListenAndServeTLS(listenAddr, certFile, keyFile, handler)) + + } else { + handler := newHandler() + log.Fatal(http.ListenAndServe(listenAddr, handler)) } } diff --git a/metrics.go b/metrics.go deleted file mode 100644 index ce24892..0000000 --- a/metrics.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2024 Center for High Performance Computing - -package main - -import ( - "fmt" - "net/http" - - "github.com/containerd/cgroups" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/collectors/version" - - "github.com/go-kit/log" - "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/treydock/cgroup_exporter/collector" -) - -func metricsHandler(paths []string, logger log.Logger) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - registry := prometheus.NewRegistry() - v2 := (cgroups.Mode() == cgroups.Unified) - cgroupCollector := collector.NewCgroupCollector(v2, paths, logger) - registry.MustRegister(cgroupCollector) - registry.MustRegister(version.NewCollector(fmt.Sprintf("%s_exporter", collector.Namespace))) - gatherers := prometheus.Gatherers{registry} - h := promhttp.HandlerFor(gatherers, promhttp.HandlerOpts{}) - h.ServeHTTP(w, r) - } -}