From ab3b30dc515bcc1f2d039e8cbef4765829eaf77c Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Sun, 24 Sep 2023 20:30:24 +0200 Subject: [PATCH 01/26] feat: query metrics in parallel Signed-off-by: Jorge Turrado --- cmd/adapter/main.go | 1 + cmd/operator/main.go | 1 + cmd/webhooks/main.go | 1 + go.mod | 1 + go.sum | 2 + vendor/go.uber.org/automaxprocs/.codecov.yml | 14 ++ vendor/go.uber.org/automaxprocs/.gitignore | 33 ++++ vendor/go.uber.org/automaxprocs/CHANGELOG.md | 47 +++++ .../automaxprocs/CODE_OF_CONDUCT.md | 75 ++++++++ .../go.uber.org/automaxprocs/CONTRIBUTING.md | 81 ++++++++ vendor/go.uber.org/automaxprocs/LICENSE | 19 ++ vendor/go.uber.org/automaxprocs/Makefile | 46 +++++ vendor/go.uber.org/automaxprocs/README.md | 71 +++++++ .../go.uber.org/automaxprocs/automaxprocs.go | 33 ++++ vendor/go.uber.org/automaxprocs/glide.yaml | 7 + .../automaxprocs/internal/cgroups/cgroup.go | 79 ++++++++ .../automaxprocs/internal/cgroups/cgroups.go | 118 ++++++++++++ .../automaxprocs/internal/cgroups/cgroups2.go | 176 ++++++++++++++++++ .../automaxprocs/internal/cgroups/doc.go | 23 +++ .../automaxprocs/internal/cgroups/errors.go | 52 ++++++ .../internal/cgroups/mountpoint.go | 171 +++++++++++++++++ .../automaxprocs/internal/cgroups/subsys.go | 103 ++++++++++ .../internal/runtime/cpu_quota_linux.go | 71 +++++++ .../internal/runtime/cpu_quota_unsupported.go | 31 +++ .../automaxprocs/internal/runtime/runtime.go | 33 ++++ .../automaxprocs/maxprocs/maxprocs.go | 130 +++++++++++++ .../automaxprocs/maxprocs/version.go | 24 +++ vendor/modules.txt | 6 + 28 files changed, 1449 insertions(+) create mode 100644 vendor/go.uber.org/automaxprocs/.codecov.yml create mode 100644 vendor/go.uber.org/automaxprocs/.gitignore create mode 100644 vendor/go.uber.org/automaxprocs/CHANGELOG.md create mode 100644 vendor/go.uber.org/automaxprocs/CODE_OF_CONDUCT.md create mode 100644 vendor/go.uber.org/automaxprocs/CONTRIBUTING.md create mode 100644 vendor/go.uber.org/automaxprocs/LICENSE create mode 100644 vendor/go.uber.org/automaxprocs/Makefile create mode 100644 vendor/go.uber.org/automaxprocs/README.md create mode 100644 vendor/go.uber.org/automaxprocs/automaxprocs.go create mode 100644 vendor/go.uber.org/automaxprocs/glide.yaml create mode 100644 vendor/go.uber.org/automaxprocs/internal/cgroups/cgroup.go create mode 100644 vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups.go create mode 100644 vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups2.go create mode 100644 vendor/go.uber.org/automaxprocs/internal/cgroups/doc.go create mode 100644 vendor/go.uber.org/automaxprocs/internal/cgroups/errors.go create mode 100644 vendor/go.uber.org/automaxprocs/internal/cgroups/mountpoint.go create mode 100644 vendor/go.uber.org/automaxprocs/internal/cgroups/subsys.go create mode 100644 vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_linux.go create mode 100644 vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_unsupported.go create mode 100644 vendor/go.uber.org/automaxprocs/internal/runtime/runtime.go create mode 100644 vendor/go.uber.org/automaxprocs/maxprocs/maxprocs.go create mode 100644 vendor/go.uber.org/automaxprocs/maxprocs/version.go diff --git a/cmd/adapter/main.go b/cmd/adapter/main.go index 7f17bc105a1..cea7ff84238 100644 --- a/cmd/adapter/main.go +++ b/cmd/adapter/main.go @@ -24,6 +24,7 @@ import ( "os" "github.com/prometheus/client_golang/prometheus/collectors" + _ "go.uber.org/automaxprocs" appsv1 "k8s.io/api/apps/v1" apimetrics "k8s.io/apiserver/pkg/endpoints/metrics" "k8s.io/client-go/kubernetes/scheme" diff --git a/cmd/operator/main.go b/cmd/operator/main.go index 4f1984e728e..d3a5a8ed9cb 100644 --- a/cmd/operator/main.go +++ b/cmd/operator/main.go @@ -22,6 +22,7 @@ import ( "time" "github.com/spf13/pflag" + _ "go.uber.org/automaxprocs" apimachineryruntime "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" kubeinformers "k8s.io/client-go/informers" diff --git a/cmd/webhooks/main.go b/cmd/webhooks/main.go index ac736c89458..1ad2cbec2f1 100644 --- a/cmd/webhooks/main.go +++ b/cmd/webhooks/main.go @@ -22,6 +22,7 @@ import ( "os" "github.com/spf13/pflag" + _ "go.uber.org/automaxprocs" apimachineryruntime "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" diff --git a/go.mod b/go.mod index 164434f6202..0d350d91613 100644 --- a/go.mod +++ b/go.mod @@ -299,6 +299,7 @@ require ( go.opentelemetry.io/proto/otlp v0.19.0 // indirect go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect go.uber.org/atomic v1.11.0 // indirect + go.uber.org/automaxprocs v1.5.3 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/crypto v0.13.0 // indirect diff --git a/go.sum b/go.sum index 4dba79e7c90..b100e0111fe 100644 --- a/go.sum +++ b/go.sum @@ -941,6 +941,8 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= +go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= diff --git a/vendor/go.uber.org/automaxprocs/.codecov.yml b/vendor/go.uber.org/automaxprocs/.codecov.yml new file mode 100644 index 00000000000..9a2ed4a9969 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/.codecov.yml @@ -0,0 +1,14 @@ +coverage: + range: 80..100 + round: down + precision: 2 + + status: + project: # measuring the overall project coverage + default: # context, you can create multiple ones with custom titles + enabled: yes # must be yes|true to enable this status + target: 90% # specify the target coverage for each commit status + # option: "auto" (must increase from parent commit or pull request base) + # option: "X%" a static target percentage to hit + if_not_found: success # if parent is not found report status as success, error, or failure + if_ci_failed: error # if ci fails report status as success, error, or failure diff --git a/vendor/go.uber.org/automaxprocs/.gitignore b/vendor/go.uber.org/automaxprocs/.gitignore new file mode 100644 index 00000000000..dd7bcf5130b --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/.gitignore @@ -0,0 +1,33 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test +vendor + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof +*.pprof +*.out +*.log +coverage.txt + +/bin +cover.out +cover.html diff --git a/vendor/go.uber.org/automaxprocs/CHANGELOG.md b/vendor/go.uber.org/automaxprocs/CHANGELOG.md new file mode 100644 index 00000000000..274797b0267 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/CHANGELOG.md @@ -0,0 +1,47 @@ +# Changelog + +## v1.5.3 (2023-07-19) + +- Fix mountinfo parsing when super options have fields with spaces. +- Fix division by zero while parsing cgroups. + +## v1.5.2 (2023-03-16) + +- Support child control cgroups +- Fix file descriptor leak +- Update dependencies + +## v1.5.1 (2022-04-06) + +- Fix cgroups v2 mountpoint detection. + +## v1.5.0 (2022-04-05) + +- Add support for cgroups v2. + +Thanks to @emadolsky for their contribution to this release. + +## v1.4.0 (2021-02-01) + +- Support colons in cgroup names. +- Remove linters from runtime dependencies. + +## v1.3.0 (2020-01-23) + +- Migrate to Go modules. + +## v1.2.0 (2018-02-22) + +- Fixed quota clamping to always round down rather than up; Rather than + guaranteeing constant throttling at saturation, instead assume that the + fractional CPU was added as a hedge for factors outside of Go's scheduler. + +## v1.1.0 (2017-11-10) + +- Log the new value of `GOMAXPROCS` rather than the current value. +- Make logs more explicit about whether `GOMAXPROCS` was modified or not. +- Allow customization of the minimum `GOMAXPROCS`, and modify default from 2 to 1. + +## v1.0.0 (2017-08-09) + +- Initial release. diff --git a/vendor/go.uber.org/automaxprocs/CODE_OF_CONDUCT.md b/vendor/go.uber.org/automaxprocs/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..e327d9aa5cd --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/CODE_OF_CONDUCT.md @@ -0,0 +1,75 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, +body size, disability, ethnicity, gender identity and expression, level of +experience, nationality, personal appearance, race, religion, or sexual +identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an +appointed representative at an online or offline event. Representation of a +project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at oss-conduct@uber.com. The project +team will review and investigate all complaints, and will respond in a way +that it deems appropriate to the circumstances. The project team is obligated +to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at +[http://contributor-covenant.org/version/1/4][version]. + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/go.uber.org/automaxprocs/CONTRIBUTING.md b/vendor/go.uber.org/automaxprocs/CONTRIBUTING.md new file mode 100644 index 00000000000..2b6a6040d78 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/CONTRIBUTING.md @@ -0,0 +1,81 @@ +# Contributing + +We'd love your help improving this package! + +If you'd like to add new exported APIs, please [open an issue][open-issue] +describing your proposal — discussing API changes ahead of time makes +pull request review much smoother. In your issue, pull request, and any other +communications, please remember to treat your fellow contributors with +respect! We take our [code of conduct](CODE_OF_CONDUCT.md) seriously. + +Note that you'll need to sign [Uber's Contributor License Agreement][cla] +before we can accept any of your contributions. If necessary, a bot will remind +you to accept the CLA when you open your pull request. + +## Setup + +[Fork][fork], then clone the repository: + +``` +mkdir -p $GOPATH/src/go.uber.org +cd $GOPATH/src/go.uber.org +git clone git@github.com:your_github_username/automaxprocs.git +cd automaxprocs +git remote add upstream https://github.com/uber-go/automaxprocs.git +git fetch upstream +``` + +Install the test dependencies: + +``` +make dependencies +``` + +Make sure that the tests and the linters pass: + +``` +make test +make lint +``` + +If you're not using the minor version of Go specified in the Makefile's +`LINTABLE_MINOR_VERSIONS` variable, `make lint` doesn't do anything. This is +fine, but it means that you'll only discover lint failures after you open your +pull request. + +## Making Changes + +Start by creating a new branch for your changes: + +``` +cd $GOPATH/src/go.uber.org/automaxprocs +git checkout master +git fetch upstream +git rebase upstream/master +git checkout -b cool_new_feature +``` + +Make your changes, then ensure that `make lint` and `make test` still pass. If +you're satisfied with your changes, push them to your fork. + +``` +git push origin cool_new_feature +``` + +Then use the GitHub UI to open a pull request. + +At this point, you're waiting on us to review your changes. We *try* to respond +to issues and pull requests within a few business days, and we may suggest some +improvements or alternatives. Once your changes are approved, one of the +project maintainers will merge them. + +We're much more likely to approve your changes if you: + +* Add tests for new functionality. +* Write a [good commit message][commit-message]. +* Maintain backward compatibility. + +[fork]: https://github.com/uber-go/automaxprocs/fork +[open-issue]: https://github.com/uber-go/automaxprocs/issues/new +[cla]: https://cla-assistant.io/uber-go/automaxprocs +[commit-message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html diff --git a/vendor/go.uber.org/automaxprocs/LICENSE b/vendor/go.uber.org/automaxprocs/LICENSE new file mode 100644 index 00000000000..20dcf51d96d --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2017 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/vendor/go.uber.org/automaxprocs/Makefile b/vendor/go.uber.org/automaxprocs/Makefile new file mode 100644 index 00000000000..1642b714801 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/Makefile @@ -0,0 +1,46 @@ +export GOBIN ?= $(shell pwd)/bin + +GO_FILES := $(shell \ + find . '(' -path '*/.*' -o -path './vendor' ')' -prune \ + -o -name '*.go' -print | cut -b3-) + +GOLINT = $(GOBIN)/golint +STATICCHECK = $(GOBIN)/staticcheck + +.PHONY: build +build: + go build ./... + +.PHONY: install +install: + go mod download + +.PHONY: test +test: + go test -race ./... + +.PHONY: cover +cover: + go test -coverprofile=cover.out -covermode=atomic -coverpkg=./... ./... + go tool cover -html=cover.out -o cover.html + +$(GOLINT): tools/go.mod + cd tools && go install golang.org/x/lint/golint + +$(STATICCHECK): tools/go.mod + cd tools && go install honnef.co/go/tools/cmd/staticcheck@2023.1.2 + +.PHONY: lint +lint: $(GOLINT) $(STATICCHECK) + @rm -rf lint.log + @echo "Checking gofmt" + @gofmt -d -s $(GO_FILES) 2>&1 | tee lint.log + @echo "Checking go vet" + @go vet ./... 2>&1 | tee -a lint.log + @echo "Checking golint" + @$(GOLINT) ./... | tee -a lint.log + @echo "Checking staticcheck" + @$(STATICCHECK) ./... 2>&1 | tee -a lint.log + @echo "Checking for license headers..." + @./.build/check_license.sh | tee -a lint.log + @[ ! -s lint.log ] diff --git a/vendor/go.uber.org/automaxprocs/README.md b/vendor/go.uber.org/automaxprocs/README.md new file mode 100644 index 00000000000..bfed32adae8 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/README.md @@ -0,0 +1,71 @@ +# automaxprocs [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] + +Automatically set `GOMAXPROCS` to match Linux container CPU quota. + +## Installation + +`go get -u go.uber.org/automaxprocs` + +## Quick Start + +```go +import _ "go.uber.org/automaxprocs" + +func main() { + // Your application logic here. +} +``` + +# Performance +Data measured from Uber's internal load balancer. We ran the load balancer with 200% CPU quota (i.e., 2 cores): + +| GOMAXPROCS | RPS | P50 (ms) | P99.9 (ms) | +| ------------------ | --------- | -------- | ---------- | +| 1 | 28,893.18 | 1.46 | 19.70 | +| 2 (equal to quota) | 44,715.07 | 0.84 | 26.38 | +| 3 | 44,212.93 | 0.66 | 30.07 | +| 4 | 41,071.15 | 0.57 | 42.94 | +| 8 | 33,111.69 | 0.43 | 64.32 | +| Default (24) | 22,191.40 | 0.45 | 76.19 | + +When `GOMAXPROCS` is increased above the CPU quota, we see P50 decrease slightly, but see significant increases to P99. We also see that the total RPS handled also decreases. + +When `GOMAXPROCS` is higher than the CPU quota allocated, we also saw significant throttling: + +``` +$ cat /sys/fs/cgroup/cpu,cpuacct/system.slice/[...]/cpu.stat +nr_periods 42227334 +nr_throttled 131923 +throttled_time 88613212216618 +``` + +Once `GOMAXPROCS` was reduced to match the CPU quota, we saw no CPU throttling. + +## Development Status: Stable + +All APIs are finalized, and no breaking changes will be made in the 1.x series +of releases. Users of semver-aware dependency management systems should pin +automaxprocs to `^1`. + +## Contributing + +We encourage and support an active, healthy community of contributors — +including you! Details are in the [contribution guide](CONTRIBUTING.md) and +the [code of conduct](CODE_OF_CONDUCT.md). The automaxprocs maintainers keep +an eye on issues and pull requests, but you can also report any negative +conduct to oss-conduct@uber.com. That email list is a private, safe space; +even the automaxprocs maintainers don't have access, so don't hesitate to hold +us to a high standard. + +
+ +Released under the [MIT License](LICENSE). + +[doc-img]: https://godoc.org/go.uber.org/automaxprocs?status.svg +[doc]: https://godoc.org/go.uber.org/automaxprocs +[ci-img]: https://github.com/uber-go/automaxprocs/actions/workflows/go.yml/badge.svg +[ci]: https://github.com/uber-go/automaxprocs/actions/workflows/go.yml +[cov-img]: https://codecov.io/gh/uber-go/automaxprocs/branch/master/graph/badge.svg +[cov]: https://codecov.io/gh/uber-go/automaxprocs + + diff --git a/vendor/go.uber.org/automaxprocs/automaxprocs.go b/vendor/go.uber.org/automaxprocs/automaxprocs.go new file mode 100644 index 00000000000..69946a3e1fd --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/automaxprocs.go @@ -0,0 +1,33 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package automaxprocs automatically sets GOMAXPROCS to match the Linux +// container CPU quota, if any. +package automaxprocs // import "go.uber.org/automaxprocs" + +import ( + "log" + + "go.uber.org/automaxprocs/maxprocs" +) + +func init() { + maxprocs.Set(maxprocs.Logger(log.Printf)) +} diff --git a/vendor/go.uber.org/automaxprocs/glide.yaml b/vendor/go.uber.org/automaxprocs/glide.yaml new file mode 100644 index 00000000000..d49aa7abf4d --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/glide.yaml @@ -0,0 +1,7 @@ +package: go.uber.org/automaxprocs +import: [] +testImport: +- package: github.com/stretchr/testify + version: ^1.1.4 + subpackages: + - assert diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroup.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroup.go new file mode 100644 index 00000000000..fe4ecf561e2 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroup.go @@ -0,0 +1,79 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build linux +// +build linux + +package cgroups + +import ( + "bufio" + "io" + "os" + "path/filepath" + "strconv" +) + +// CGroup represents the data structure for a Linux control group. +type CGroup struct { + path string +} + +// NewCGroup returns a new *CGroup from a given path. +func NewCGroup(path string) *CGroup { + return &CGroup{path: path} +} + +// Path returns the path of the CGroup*. +func (cg *CGroup) Path() string { + return cg.path +} + +// ParamPath returns the path of the given cgroup param under itself. +func (cg *CGroup) ParamPath(param string) string { + return filepath.Join(cg.path, param) +} + +// readFirstLine reads the first line from a cgroup param file. +func (cg *CGroup) readFirstLine(param string) (string, error) { + paramFile, err := os.Open(cg.ParamPath(param)) + if err != nil { + return "", err + } + defer paramFile.Close() + + scanner := bufio.NewScanner(paramFile) + if scanner.Scan() { + return scanner.Text(), nil + } + if err := scanner.Err(); err != nil { + return "", err + } + return "", io.ErrUnexpectedEOF +} + +// readInt parses the first line from a cgroup param file as int. +func (cg *CGroup) readInt(param string) (int, error) { + text, err := cg.readFirstLine(param) + if err != nil { + return 0, err + } + return strconv.Atoi(text) +} diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups.go new file mode 100644 index 00000000000..e89f5436028 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups.go @@ -0,0 +1,118 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build linux +// +build linux + +package cgroups + +const ( + // _cgroupFSType is the Linux CGroup file system type used in + // `/proc/$PID/mountinfo`. + _cgroupFSType = "cgroup" + // _cgroupSubsysCPU is the CPU CGroup subsystem. + _cgroupSubsysCPU = "cpu" + // _cgroupSubsysCPUAcct is the CPU accounting CGroup subsystem. + _cgroupSubsysCPUAcct = "cpuacct" + // _cgroupSubsysCPUSet is the CPUSet CGroup subsystem. + _cgroupSubsysCPUSet = "cpuset" + // _cgroupSubsysMemory is the Memory CGroup subsystem. + _cgroupSubsysMemory = "memory" + + // _cgroupCPUCFSQuotaUsParam is the file name for the CGroup CFS quota + // parameter. + _cgroupCPUCFSQuotaUsParam = "cpu.cfs_quota_us" + // _cgroupCPUCFSPeriodUsParam is the file name for the CGroup CFS period + // parameter. + _cgroupCPUCFSPeriodUsParam = "cpu.cfs_period_us" +) + +const ( + _procPathCGroup = "/proc/self/cgroup" + _procPathMountInfo = "/proc/self/mountinfo" +) + +// CGroups is a map that associates each CGroup with its subsystem name. +type CGroups map[string]*CGroup + +// NewCGroups returns a new *CGroups from given `mountinfo` and `cgroup` files +// under for some process under `/proc` file system (see also proc(5) for more +// information). +func NewCGroups(procPathMountInfo, procPathCGroup string) (CGroups, error) { + cgroupSubsystems, err := parseCGroupSubsystems(procPathCGroup) + if err != nil { + return nil, err + } + + cgroups := make(CGroups) + newMountPoint := func(mp *MountPoint) error { + if mp.FSType != _cgroupFSType { + return nil + } + + for _, opt := range mp.SuperOptions { + subsys, exists := cgroupSubsystems[opt] + if !exists { + continue + } + + cgroupPath, err := mp.Translate(subsys.Name) + if err != nil { + return err + } + cgroups[opt] = NewCGroup(cgroupPath) + } + + return nil + } + + if err := parseMountInfo(procPathMountInfo, newMountPoint); err != nil { + return nil, err + } + return cgroups, nil +} + +// NewCGroupsForCurrentProcess returns a new *CGroups instance for the current +// process. +func NewCGroupsForCurrentProcess() (CGroups, error) { + return NewCGroups(_procPathMountInfo, _procPathCGroup) +} + +// CPUQuota returns the CPU quota applied with the CPU cgroup controller. +// It is a result of `cpu.cfs_quota_us / cpu.cfs_period_us`. If the value of +// `cpu.cfs_quota_us` was not set (-1), the method returns `(-1, nil)`. +func (cg CGroups) CPUQuota() (float64, bool, error) { + cpuCGroup, exists := cg[_cgroupSubsysCPU] + if !exists { + return -1, false, nil + } + + cfsQuotaUs, err := cpuCGroup.readInt(_cgroupCPUCFSQuotaUsParam) + if defined := cfsQuotaUs > 0; err != nil || !defined { + return -1, defined, err + } + + cfsPeriodUs, err := cpuCGroup.readInt(_cgroupCPUCFSPeriodUsParam) + if defined := cfsPeriodUs > 0; err != nil || !defined { + return -1, defined, err + } + + return float64(cfsQuotaUs) / float64(cfsPeriodUs), true, nil +} diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups2.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups2.go new file mode 100644 index 00000000000..78556062fe2 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups2.go @@ -0,0 +1,176 @@ +// Copyright (c) 2022 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build linux +// +build linux + +package cgroups + +import ( + "bufio" + "errors" + "fmt" + "io" + "os" + "path" + "strconv" + "strings" +) + +const ( + // _cgroupv2CPUMax is the file name for the CGroup-V2 CPU max and period + // parameter. + _cgroupv2CPUMax = "cpu.max" + // _cgroupFSType is the Linux CGroup-V2 file system type used in + // `/proc/$PID/mountinfo`. + _cgroupv2FSType = "cgroup2" + + _cgroupv2MountPoint = "/sys/fs/cgroup" + + _cgroupV2CPUMaxDefaultPeriod = 100000 + _cgroupV2CPUMaxQuotaMax = "max" +) + +const ( + _cgroupv2CPUMaxQuotaIndex = iota + _cgroupv2CPUMaxPeriodIndex +) + +// ErrNotV2 indicates that the system is not using cgroups2. +var ErrNotV2 = errors.New("not using cgroups2") + +// CGroups2 provides access to cgroups data for systems using cgroups2. +type CGroups2 struct { + mountPoint string + groupPath string + cpuMaxFile string +} + +// NewCGroups2ForCurrentProcess builds a CGroups2 for the current process. +// +// This returns ErrNotV2 if the system is not using cgroups2. +func NewCGroups2ForCurrentProcess() (*CGroups2, error) { + return newCGroups2From(_procPathMountInfo, _procPathCGroup) +} + +func newCGroups2From(mountInfoPath, procPathCGroup string) (*CGroups2, error) { + isV2, err := isCGroupV2(mountInfoPath) + if err != nil { + return nil, err + } + + if !isV2 { + return nil, ErrNotV2 + } + + subsystems, err := parseCGroupSubsystems(procPathCGroup) + if err != nil { + return nil, err + } + + // Find v2 subsystem by looking for the `0` id + var v2subsys *CGroupSubsys + for _, subsys := range subsystems { + if subsys.ID == 0 { + v2subsys = subsys + break + } + } + + if v2subsys == nil { + return nil, ErrNotV2 + } + + return &CGroups2{ + mountPoint: _cgroupv2MountPoint, + groupPath: v2subsys.Name, + cpuMaxFile: _cgroupv2CPUMax, + }, nil +} + +func isCGroupV2(procPathMountInfo string) (bool, error) { + var ( + isV2 bool + newMountPoint = func(mp *MountPoint) error { + isV2 = isV2 || (mp.FSType == _cgroupv2FSType && mp.MountPoint == _cgroupv2MountPoint) + return nil + } + ) + + if err := parseMountInfo(procPathMountInfo, newMountPoint); err != nil { + return false, err + } + + return isV2, nil +} + +// CPUQuota returns the CPU quota applied with the CPU cgroup2 controller. +// It is a result of reading cpu quota and period from cpu.max file. +// It will return `cpu.max / cpu.period`. If cpu.max is set to max, it returns +// (-1, false, nil) +func (cg *CGroups2) CPUQuota() (float64, bool, error) { + cpuMaxParams, err := os.Open(path.Join(cg.mountPoint, cg.groupPath, cg.cpuMaxFile)) + if err != nil { + if os.IsNotExist(err) { + return -1, false, nil + } + return -1, false, err + } + defer cpuMaxParams.Close() + + scanner := bufio.NewScanner(cpuMaxParams) + if scanner.Scan() { + fields := strings.Fields(scanner.Text()) + if len(fields) == 0 || len(fields) > 2 { + return -1, false, fmt.Errorf("invalid format") + } + + if fields[_cgroupv2CPUMaxQuotaIndex] == _cgroupV2CPUMaxQuotaMax { + return -1, false, nil + } + + max, err := strconv.Atoi(fields[_cgroupv2CPUMaxQuotaIndex]) + if err != nil { + return -1, false, err + } + + var period int + if len(fields) == 1 { + period = _cgroupV2CPUMaxDefaultPeriod + } else { + period, err = strconv.Atoi(fields[_cgroupv2CPUMaxPeriodIndex]) + if err != nil { + return -1, false, err + } + + if period == 0 { + return -1, false, errors.New("zero value for period is not allowed") + } + } + + return float64(max) / float64(period), true, nil + } + + if err := scanner.Err(); err != nil { + return -1, false, err + } + + return 0, false, io.ErrUnexpectedEOF +} diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/doc.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/doc.go new file mode 100644 index 00000000000..113555f63da --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/cgroups/doc.go @@ -0,0 +1,23 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package cgroups provides utilities to access Linux control group (CGroups) +// parameters (CPU quota, for example) for a given process. +package cgroups diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/errors.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/errors.go new file mode 100644 index 00000000000..94ac75a46e8 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/cgroups/errors.go @@ -0,0 +1,52 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build linux +// +build linux + +package cgroups + +import "fmt" + +type cgroupSubsysFormatInvalidError struct { + line string +} + +type mountPointFormatInvalidError struct { + line string +} + +type pathNotExposedFromMountPointError struct { + mountPoint string + root string + path string +} + +func (err cgroupSubsysFormatInvalidError) Error() string { + return fmt.Sprintf("invalid format for CGroupSubsys: %q", err.line) +} + +func (err mountPointFormatInvalidError) Error() string { + return fmt.Sprintf("invalid format for MountPoint: %q", err.line) +} + +func (err pathNotExposedFromMountPointError) Error() string { + return fmt.Sprintf("path %q is not a descendant of mount point root %q and cannot be exposed from %q", err.path, err.root, err.mountPoint) +} diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/mountpoint.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/mountpoint.go new file mode 100644 index 00000000000..f3877f78aa6 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/cgroups/mountpoint.go @@ -0,0 +1,171 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build linux +// +build linux + +package cgroups + +import ( + "bufio" + "os" + "path/filepath" + "strconv" + "strings" +) + +const ( + _mountInfoSep = " " + _mountInfoOptsSep = "," + _mountInfoOptionalFieldsSep = "-" +) + +const ( + _miFieldIDMountID = iota + _miFieldIDParentID + _miFieldIDDeviceID + _miFieldIDRoot + _miFieldIDMountPoint + _miFieldIDOptions + _miFieldIDOptionalFields + + _miFieldCountFirstHalf +) + +const ( + _miFieldOffsetFSType = iota + _miFieldOffsetMountSource + _miFieldOffsetSuperOptions + + _miFieldCountSecondHalf +) + +const _miFieldCountMin = _miFieldCountFirstHalf + _miFieldCountSecondHalf + +// MountPoint is the data structure for the mount points in +// `/proc/$PID/mountinfo`. See also proc(5) for more information. +type MountPoint struct { + MountID int + ParentID int + DeviceID string + Root string + MountPoint string + Options []string + OptionalFields []string + FSType string + MountSource string + SuperOptions []string +} + +// NewMountPointFromLine parses a line read from `/proc/$PID/mountinfo` and +// returns a new *MountPoint. +func NewMountPointFromLine(line string) (*MountPoint, error) { + fields := strings.Split(line, _mountInfoSep) + + if len(fields) < _miFieldCountMin { + return nil, mountPointFormatInvalidError{line} + } + + mountID, err := strconv.Atoi(fields[_miFieldIDMountID]) + if err != nil { + return nil, err + } + + parentID, err := strconv.Atoi(fields[_miFieldIDParentID]) + if err != nil { + return nil, err + } + + for i, field := range fields[_miFieldIDOptionalFields:] { + if field == _mountInfoOptionalFieldsSep { + // End of optional fields. + fsTypeStart := _miFieldIDOptionalFields + i + 1 + + // Now we know where the optional fields end, split the line again with a + // limit to avoid issues with spaces in super options as present on WSL. + fields = strings.SplitN(line, _mountInfoSep, fsTypeStart+_miFieldCountSecondHalf) + if len(fields) != fsTypeStart+_miFieldCountSecondHalf { + return nil, mountPointFormatInvalidError{line} + } + + miFieldIDFSType := _miFieldOffsetFSType + fsTypeStart + miFieldIDMountSource := _miFieldOffsetMountSource + fsTypeStart + miFieldIDSuperOptions := _miFieldOffsetSuperOptions + fsTypeStart + + return &MountPoint{ + MountID: mountID, + ParentID: parentID, + DeviceID: fields[_miFieldIDDeviceID], + Root: fields[_miFieldIDRoot], + MountPoint: fields[_miFieldIDMountPoint], + Options: strings.Split(fields[_miFieldIDOptions], _mountInfoOptsSep), + OptionalFields: fields[_miFieldIDOptionalFields:(fsTypeStart - 1)], + FSType: fields[miFieldIDFSType], + MountSource: fields[miFieldIDMountSource], + SuperOptions: strings.Split(fields[miFieldIDSuperOptions], _mountInfoOptsSep), + }, nil + } + } + + return nil, mountPointFormatInvalidError{line} +} + +// Translate converts an absolute path inside the *MountPoint's file system to +// the host file system path in the mount namespace the *MountPoint belongs to. +func (mp *MountPoint) Translate(absPath string) (string, error) { + relPath, err := filepath.Rel(mp.Root, absPath) + + if err != nil { + return "", err + } + if relPath == ".." || strings.HasPrefix(relPath, "../") { + return "", pathNotExposedFromMountPointError{ + mountPoint: mp.MountPoint, + root: mp.Root, + path: absPath, + } + } + + return filepath.Join(mp.MountPoint, relPath), nil +} + +// parseMountInfo parses procPathMountInfo (usually at `/proc/$PID/mountinfo`) +// and yields parsed *MountPoint into newMountPoint. +func parseMountInfo(procPathMountInfo string, newMountPoint func(*MountPoint) error) error { + mountInfoFile, err := os.Open(procPathMountInfo) + if err != nil { + return err + } + defer mountInfoFile.Close() + + scanner := bufio.NewScanner(mountInfoFile) + + for scanner.Scan() { + mountPoint, err := NewMountPointFromLine(scanner.Text()) + if err != nil { + return err + } + if err := newMountPoint(mountPoint); err != nil { + return err + } + } + + return scanner.Err() +} diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/subsys.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/subsys.go new file mode 100644 index 00000000000..cddc3eaec39 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/cgroups/subsys.go @@ -0,0 +1,103 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build linux +// +build linux + +package cgroups + +import ( + "bufio" + "os" + "strconv" + "strings" +) + +const ( + _cgroupSep = ":" + _cgroupSubsysSep = "," +) + +const ( + _csFieldIDID = iota + _csFieldIDSubsystems + _csFieldIDName + _csFieldCount +) + +// CGroupSubsys represents the data structure for entities in +// `/proc/$PID/cgroup`. See also proc(5) for more information. +type CGroupSubsys struct { + ID int + Subsystems []string + Name string +} + +// NewCGroupSubsysFromLine returns a new *CGroupSubsys by parsing a string in +// the format of `/proc/$PID/cgroup` +func NewCGroupSubsysFromLine(line string) (*CGroupSubsys, error) { + fields := strings.SplitN(line, _cgroupSep, _csFieldCount) + + if len(fields) != _csFieldCount { + return nil, cgroupSubsysFormatInvalidError{line} + } + + id, err := strconv.Atoi(fields[_csFieldIDID]) + if err != nil { + return nil, err + } + + cgroup := &CGroupSubsys{ + ID: id, + Subsystems: strings.Split(fields[_csFieldIDSubsystems], _cgroupSubsysSep), + Name: fields[_csFieldIDName], + } + + return cgroup, nil +} + +// parseCGroupSubsystems parses procPathCGroup (usually at `/proc/$PID/cgroup`) +// and returns a new map[string]*CGroupSubsys. +func parseCGroupSubsystems(procPathCGroup string) (map[string]*CGroupSubsys, error) { + cgroupFile, err := os.Open(procPathCGroup) + if err != nil { + return nil, err + } + defer cgroupFile.Close() + + scanner := bufio.NewScanner(cgroupFile) + subsystems := make(map[string]*CGroupSubsys) + + for scanner.Scan() { + cgroup, err := NewCGroupSubsysFromLine(scanner.Text()) + if err != nil { + return nil, err + } + for _, subsys := range cgroup.Subsystems { + subsystems[subsys] = cgroup + } + } + + if err := scanner.Err(); err != nil { + return nil, err + } + + return subsystems, nil +} diff --git a/vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_linux.go b/vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_linux.go new file mode 100644 index 00000000000..3b974754c3e --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_linux.go @@ -0,0 +1,71 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build linux +// +build linux + +package runtime + +import ( + "errors" + "math" + + cg "go.uber.org/automaxprocs/internal/cgroups" +) + +// CPUQuotaToGOMAXPROCS converts the CPU quota applied to the calling process +// to a valid GOMAXPROCS value. +func CPUQuotaToGOMAXPROCS(minValue int) (int, CPUQuotaStatus, error) { + cgroups, err := newQueryer() + if err != nil { + return -1, CPUQuotaUndefined, err + } + + quota, defined, err := cgroups.CPUQuota() + if !defined || err != nil { + return -1, CPUQuotaUndefined, err + } + + maxProcs := int(math.Floor(quota)) + if minValue > 0 && maxProcs < minValue { + return minValue, CPUQuotaMinUsed, nil + } + return maxProcs, CPUQuotaUsed, nil +} + +type queryer interface { + CPUQuota() (float64, bool, error) +} + +var ( + _newCgroups2 = cg.NewCGroups2ForCurrentProcess + _newCgroups = cg.NewCGroupsForCurrentProcess +) + +func newQueryer() (queryer, error) { + cgroups, err := _newCgroups2() + if err == nil { + return cgroups, nil + } + if errors.Is(err, cg.ErrNotV2) { + return _newCgroups() + } + return nil, err +} diff --git a/vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_unsupported.go b/vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_unsupported.go new file mode 100644 index 00000000000..6922554484e --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_unsupported.go @@ -0,0 +1,31 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build !linux +// +build !linux + +package runtime + +// CPUQuotaToGOMAXPROCS converts the CPU quota applied to the calling process +// to a valid GOMAXPROCS value. This is Linux-specific and not supported in the +// current OS. +func CPUQuotaToGOMAXPROCS(_ int) (int, CPUQuotaStatus, error) { + return -1, CPUQuotaUndefined, nil +} diff --git a/vendor/go.uber.org/automaxprocs/internal/runtime/runtime.go b/vendor/go.uber.org/automaxprocs/internal/runtime/runtime.go new file mode 100644 index 00000000000..df6eacf0530 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/internal/runtime/runtime.go @@ -0,0 +1,33 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package runtime + +// CPUQuotaStatus presents the status of how CPU quota is used +type CPUQuotaStatus int + +const ( + // CPUQuotaUndefined is returned when CPU quota is undefined + CPUQuotaUndefined CPUQuotaStatus = iota + // CPUQuotaUsed is returned when a valid CPU quota can be used + CPUQuotaUsed + // CPUQuotaMinUsed is returned when CPU quota is smaller than the min value + CPUQuotaMinUsed +) diff --git a/vendor/go.uber.org/automaxprocs/maxprocs/maxprocs.go b/vendor/go.uber.org/automaxprocs/maxprocs/maxprocs.go new file mode 100644 index 00000000000..98176d64575 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/maxprocs/maxprocs.go @@ -0,0 +1,130 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package maxprocs lets Go programs easily configure runtime.GOMAXPROCS to +// match the configured Linux CPU quota. Unlike the top-level automaxprocs +// package, it lets the caller configure logging and handle errors. +package maxprocs // import "go.uber.org/automaxprocs/maxprocs" + +import ( + "os" + "runtime" + + iruntime "go.uber.org/automaxprocs/internal/runtime" +) + +const _maxProcsKey = "GOMAXPROCS" + +func currentMaxProcs() int { + return runtime.GOMAXPROCS(0) +} + +type config struct { + printf func(string, ...interface{}) + procs func(int) (int, iruntime.CPUQuotaStatus, error) + minGOMAXPROCS int +} + +func (c *config) log(fmt string, args ...interface{}) { + if c.printf != nil { + c.printf(fmt, args...) + } +} + +// An Option alters the behavior of Set. +type Option interface { + apply(*config) +} + +// Logger uses the supplied printf implementation for log output. By default, +// Set doesn't log anything. +func Logger(printf func(string, ...interface{})) Option { + return optionFunc(func(cfg *config) { + cfg.printf = printf + }) +} + +// Min sets the minimum GOMAXPROCS value that will be used. +// Any value below 1 is ignored. +func Min(n int) Option { + return optionFunc(func(cfg *config) { + if n >= 1 { + cfg.minGOMAXPROCS = n + } + }) +} + +type optionFunc func(*config) + +func (of optionFunc) apply(cfg *config) { of(cfg) } + +// Set GOMAXPROCS to match the Linux container CPU quota (if any), returning +// any error encountered and an undo function. +// +// Set is a no-op on non-Linux systems and in Linux environments without a +// configured CPU quota. +func Set(opts ...Option) (func(), error) { + cfg := &config{ + procs: iruntime.CPUQuotaToGOMAXPROCS, + minGOMAXPROCS: 1, + } + for _, o := range opts { + o.apply(cfg) + } + + undoNoop := func() { + cfg.log("maxprocs: No GOMAXPROCS change to reset") + } + + // Honor the GOMAXPROCS environment variable if present. Otherwise, amend + // `runtime.GOMAXPROCS()` with the current process' CPU quota if the OS is + // Linux, and guarantee a minimum value of 1. The minimum guaranteed value + // can be overridden using `maxprocs.Min()`. + if max, exists := os.LookupEnv(_maxProcsKey); exists { + cfg.log("maxprocs: Honoring GOMAXPROCS=%q as set in environment", max) + return undoNoop, nil + } + + maxProcs, status, err := cfg.procs(cfg.minGOMAXPROCS) + if err != nil { + return undoNoop, err + } + + if status == iruntime.CPUQuotaUndefined { + cfg.log("maxprocs: Leaving GOMAXPROCS=%v: CPU quota undefined", currentMaxProcs()) + return undoNoop, nil + } + + prev := currentMaxProcs() + undo := func() { + cfg.log("maxprocs: Resetting GOMAXPROCS to %v", prev) + runtime.GOMAXPROCS(prev) + } + + switch status { + case iruntime.CPUQuotaMinUsed: + cfg.log("maxprocs: Updating GOMAXPROCS=%v: using minimum allowed GOMAXPROCS", maxProcs) + case iruntime.CPUQuotaUsed: + cfg.log("maxprocs: Updating GOMAXPROCS=%v: determined from CPU quota", maxProcs) + } + + runtime.GOMAXPROCS(maxProcs) + return undo, nil +} diff --git a/vendor/go.uber.org/automaxprocs/maxprocs/version.go b/vendor/go.uber.org/automaxprocs/maxprocs/version.go new file mode 100644 index 00000000000..108a95535e5 --- /dev/null +++ b/vendor/go.uber.org/automaxprocs/maxprocs/version.go @@ -0,0 +1,24 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package maxprocs + +// Version is the current package version. +const Version = "1.5.2" diff --git a/vendor/modules.txt b/vendor/modules.txt index 95b56a8e940..a5e86e00f83 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1430,6 +1430,12 @@ go.starlark.net/syntax # go.uber.org/atomic v1.11.0 ## explicit; go 1.18 go.uber.org/atomic +# go.uber.org/automaxprocs v1.5.3 +## explicit; go 1.18 +go.uber.org/automaxprocs +go.uber.org/automaxprocs/internal/cgroups +go.uber.org/automaxprocs/internal/runtime +go.uber.org/automaxprocs/maxprocs # go.uber.org/multierr v1.11.0 ## explicit; go 1.19 go.uber.org/multierr From 79f00a3cd2c1ff2c1c0937c559cfc94355f412a8 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Mon, 25 Sep 2023 01:39:03 +0200 Subject: [PATCH 02/26] add scaledjobs Signed-off-by: Jorge Turrado --- pkg/scaling/scale_handler.go | 65 +++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index db9bc8b2adb..182994e1247 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -667,41 +667,54 @@ func (h *scaleHandler) getScaledJobMetrics(ctx context.Context, scaledJob *kedav } var scalersMetrics []scaledjob.ScalerMetrics scalers, _ := cache.GetScalers() + + wg := sync.WaitGroup{} + ch := make(chan scaledjob.ScalerMetrics, len(scalers)) for i, s := range scalers { - isActive := false - scalerType := fmt.Sprintf("%T:", s) + wg.Add(1) + go h.calculateJobScaler(ctx, s, scaledJob, cache, i, &wg, ch) + } + wg.Wait() + close(ch) + for scalerMetric := range ch { + scalersMetrics = append(scalersMetrics, scalerMetric) + } + return scalersMetrics +} - scalerLogger := log.WithValues("ScaledJob", scaledJob.Name, "Scaler", scalerType) +func (*scaleHandler) calculateJobScaler(ctx context.Context, s scalers.Scaler, scaledJob *kedav1alpha1.ScaledJob, cache *cache.ScalersCache, scalerIndex int, wg *sync.WaitGroup, ch chan scaledjob.ScalerMetrics) { + defer wg.Done() - metricSpecs := s.GetMetricSpecForScaling(ctx) + isActive := false + scalerType := fmt.Sprintf("%T:", s) - // skip scaler that doesn't return any metric specs (usually External scaler with incorrect metadata) - // or skip cpu/memory resource scaler - if len(metricSpecs) < 1 || metricSpecs[0].External == nil { - continue - } + scalerLogger := log.WithValues("ScaledJob", scaledJob.Name, "Scaler", scalerType) - metrics, isTriggerActive, _, err := cache.GetMetricsAndActivityForScaler(ctx, i, metricSpecs[0].External.Metric.Name) - if err != nil { - scalerLogger.V(1).Info("Error getting scaler metrics and activity, but continue", "error", err) - cache.Recorder.Event(scaledJob, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) - continue - } - if isTriggerActive { - isActive = true - } + metricSpecs := s.GetMetricSpecForScaling(ctx) - queueLength, maxValue, targetAverageValue := scaledjob.CalculateQueueLengthAndMaxValue(metrics, metricSpecs, scaledJob.MaxReplicaCount()) + if len(metricSpecs) < 1 || metricSpecs[0].External == nil { + return + } - scalerLogger.V(1).Info("Scaler Metric value", "isTriggerActive", isTriggerActive, metricSpecs[0].External.Metric.Name, queueLength, "targetAverageValue", targetAverageValue) + metrics, isTriggerActive, _, err := cache.GetMetricsAndActivityForScaler(ctx, scalerIndex, metricSpecs[0].External.Metric.Name) + if err != nil { + scalerLogger.V(1).Info("Error getting scaler metrics and activity, but continue", "error", err) + cache.Recorder.Event(scaledJob, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) + return + } + if isTriggerActive { + isActive = true + } + + queueLength, maxValue, targetAverageValue := scaledjob.CalculateQueueLengthAndMaxValue(metrics, metricSpecs, scaledJob.MaxReplicaCount()) - scalersMetrics = append(scalersMetrics, scaledjob.ScalerMetrics{ - QueueLength: queueLength, - MaxValue: maxValue, - IsActive: isActive, - }) + scalerLogger.V(1).Info("Scaler Metric value", "isTriggerActive", isTriggerActive, metricSpecs[0].External.Metric.Name, queueLength, "targetAverageValue", targetAverageValue) + + ch <- scaledjob.ScalerMetrics{ + QueueLength: queueLength, + MaxValue: maxValue, + IsActive: isActive, } - return scalersMetrics } // isScaledJobActive returns whether the input ScaledJob: From e6bddbbc8ddf29b3c3e4f9a97bf183137c6468b8 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Mon, 25 Sep 2023 17:11:20 +0200 Subject: [PATCH 03/26] add logic inside operator Signed-off-by: Jorge Turrado --- apis/keda/v1alpha1/scaledobject_types.go | 4 + apis/keda/v1alpha1/scaledobject_webhook.go | 26 +----- config/crd/bases/keda.sh_scaledobjects.yaml | 7 ++ controllers/keda/hpa.go | 23 ++--- pkg/eventreason/eventreason.go | 3 + pkg/scaling/scale_handler.go | 93 ++++++++++++++++----- 6 files changed, 95 insertions(+), 61 deletions(-) diff --git a/apis/keda/v1alpha1/scaledobject_types.go b/apis/keda/v1alpha1/scaledobject_types.go index c674fd2ae72..08cc0187c20 100644 --- a/apis/keda/v1alpha1/scaledobject_types.go +++ b/apis/keda/v1alpha1/scaledobject_types.go @@ -117,6 +117,10 @@ type AdvancedConfig struct { type ScalingModifiers struct { Formula string `json:"formula,omitempty"` Target string `json:"target,omitempty"` + // +optional + ActivationTarget string `json:"activationTarget,omitempty"` + // +optional + MetricType autoscalingv2.MetricTargetType `json:"metricType,omitempty"` } // HorizontalPodAutoscalerConfig specifies horizontal scale config diff --git a/apis/keda/v1alpha1/scaledobject_webhook.go b/apis/keda/v1alpha1/scaledobject_webhook.go index d7b9abea6d4..071630f2e16 100644 --- a/apis/keda/v1alpha1/scaledobject_webhook.go +++ b/apis/keda/v1alpha1/scaledobject_webhook.go @@ -392,31 +392,7 @@ func validateScalingModifiersTarget(so *ScaledObject) error { return fmt.Errorf("error converting target for scalingModifiers (string->float) to valid target: %w", err) } - // if target is given, composite-scaler will be passed to HPA -> all types - // need to be the same - make sure all metrics are of the same metricTargetType - - var trigType autoscalingv2.MetricTargetType - - // gauron99: possible TODO: more sofisticated check for trigger could be used here - // as well if solution is found (check just the right triggers that are used) - for _, trig := range so.Spec.Triggers { - if trig.Type == cpuString || trig.Type == memoryString || trig.Name == "" { - continue - } - var current autoscalingv2.MetricTargetType - if trig.MetricType == "" { - current = autoscalingv2.AverageValueMetricType // default is AverageValue - } else { - current = trig.MetricType - } - if trigType == "" { - trigType = current - } else if trigType != current { - err := fmt.Errorf("error trigger types are not the same for composite scaler: %s & %s", trigType, current) - return err - } - } - if trigType == autoscalingv2.UtilizationMetricType { + if so.Spec.Advanced.ScalingModifiers.MetricType == autoscalingv2.UtilizationMetricType { err := fmt.Errorf("error trigger type is Utilization, but it needs to be AverageValue or Value for external metrics") return err } diff --git a/config/crd/bases/keda.sh_scaledobjects.yaml b/config/crd/bases/keda.sh_scaledobjects.yaml index 86ab804d13e..c35bb68a1e2 100644 --- a/config/crd/bases/keda.sh_scaledobjects.yaml +++ b/config/crd/bases/keda.sh_scaledobjects.yaml @@ -207,8 +207,15 @@ spec: description: ScalingModifiers describes advanced scaling logic options like formula properties: + activationTarget: + type: string formula: type: string + metricType: + description: MetricTargetType specifies the type of metric + being targeted, and should be either "Value", "AverageValue", + or "Utilization" + type: string target: type: string type: object diff --git a/controllers/keda/hpa.go b/controllers/keda/hpa.go index 8f0e470782f..4dc9d3d08d9 100644 --- a/controllers/keda/hpa.go +++ b/controllers/keda/hpa.go @@ -258,19 +258,12 @@ func (r *ScaledObjectReconciler) getScaledObjectMetricSpecs(ctx context.Context, validNumTarget, _ := strconv.ParseFloat(scaledObject.Spec.Advanced.ScalingModifiers.Target, 64) // check & get metric specs type - var validMetricType autoscalingv2.MetricTargetType - for _, metric := range metricSpecs { - if metric.External == nil { - continue - } - if validMetricType == "" { - validMetricType = metric.External.Target.Type - } else if metric.External.Target.Type != validMetricType { - err := fmt.Errorf("error metric target type is not the same for composite scaler: %s & %s", validMetricType, metric.External.Target.Type) - return nil, err - } + metricType := autoscalingv2.AverageValueMetricType + if scaledObject.Spec.Advanced.ScalingModifiers.MetricType != "" { + metricType = scaledObject.Spec.Advanced.ScalingModifiers.MetricType } - if validMetricType == autoscalingv2.UtilizationMetricType { + + if metricType == autoscalingv2.UtilizationMetricType { err := fmt.Errorf("error metric target type is Utilization, but it needs to be AverageValue or Value for external metrics") return nil, err } @@ -280,11 +273,11 @@ func (r *ScaledObjectReconciler) getScaledObjectMetricSpecs(ctx context.Context, quan := resource.NewMilliQuantity(int64(validNumTarget*1000), resource.DecimalSI) correctHpaTarget := autoscalingv2.MetricTarget{ - Type: validMetricType, + Type: metricType, } - if validMetricType == autoscalingv2.AverageValueMetricType { + if metricType == autoscalingv2.AverageValueMetricType { correctHpaTarget.AverageValue = quan - } else if validMetricType == autoscalingv2.ValueMetricType { + } else if metricType == autoscalingv2.ValueMetricType { correctHpaTarget.Value = quan } compMetricName := kedav1alpha1.CompositeMetricName diff --git a/pkg/eventreason/eventreason.go b/pkg/eventreason/eventreason.go index 6b56ef79abd..6fbc854ddc6 100644 --- a/pkg/eventreason/eventreason.go +++ b/pkg/eventreason/eventreason.go @@ -50,6 +50,9 @@ const ( // KEDAScalerFailed is for event when a scaler fails for a ScaledJob or a ScaledObject KEDAScalerFailed = "KEDAScalerFailed" + // KEDAMetricSourceFailed is for event when a scaler fails as metric source for custom formula + KEDAMetricSourceFailed = "KEDAMetricSourceFailed" + // KEDAScaleTargetActivated is for event when the scale target of ScaledObject was activated KEDAScaleTargetActivated = "KEDAScaleTargetActivated" diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index 182994e1247..0bbec91ce31 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -19,6 +19,7 @@ package scaling import ( "context" "fmt" + "strconv" "strings" "sync" "time" @@ -555,6 +556,8 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k isScaledObjectActive := false isScalerError := false metricsRecord := map[string]metricscache.MetricsRecord{} + metricTriggerPairList := make(map[string]string) + var matchingMetrics []external_metrics.ExternalMetricValue cache, err := h.GetScalersCache(ctx, scaledObject) prommetrics.RecordScaledObjectError(scaledObject.Namespace, scaledObject.Name, err) @@ -592,9 +595,6 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k // if cpu/memory resource scaler has minReplicas==0 & at least one external // trigger exists -> object can be scaled to zero if spec.External == nil { - if len(scaledObject.Spec.Triggers) <= cpuMemCount { - isScaledObjectActive = true - } continue } @@ -605,6 +605,7 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k if latency != -1 { prommetrics.RecordScalerLatency(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, float64(latency)) } + matchingMetrics = append(matchingMetrics, metrics...) logger.V(1).Info("Getting metrics and activity from scaler", "scaler", scalerName, "metricName", metricName, "metrics", metrics, "activity", isMetricActive, "scalerError", err) if scalerConfigs[scalerIndex].TriggerUseCachedMetrics { @@ -615,28 +616,47 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k } } - if err != nil { - isScalerError = true - logger.Error(err, "error getting scale decision", "scaler", scalerName) - cache.Recorder.Event(scaledObject, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) - } else { - for _, metric := range metrics { - metricValue := metric.Value.AsApproximateFloat64() - prommetrics.RecordScalerMetric(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metric.MetricName, metricValue) + if scaledObject.IsUsingModifiers() { + if err != nil { + isScalerError = true + logger.Error(err, "error getting metric source", "source", scalerName) + cache.Recorder.Event(scaledObject, corev1.EventTypeWarning, eventreason.KEDAMetricSourceFailed, err.Error()) + } else { + for _, metric := range metrics { + metricValue := metric.Value.AsApproximateFloat64() + prommetrics.RecordScalerMetric(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metric.MetricName, metricValue) + } } - - if isMetricActive { - isScaledObjectActive = true - if spec.External != nil { - logger.V(1).Info("Scaler for scaledObject is active", "scaler", scalerName, "metricName", metricName) + prommetrics.RecordScalerError(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, err) + } else { + if err != nil { + isScalerError = true + logger.Error(err, "error getting scale decision", "scaler", scalerName) + cache.Recorder.Event(scaledObject, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) + } else { + for _, metric := range metrics { + metricValue := metric.Value.AsApproximateFloat64() + prommetrics.RecordScalerMetric(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metric.MetricName, metricValue) } - if spec.Resource != nil { - logger.V(1).Info("Scaler for scaledObject is active", "scaler", scalerName, "metricName", spec.Resource.Name) + + if isMetricActive { + isScaledObjectActive = true + if spec.External != nil { + logger.V(1).Info("Scaler for scaledObject is active", "scaler", scalerName, "metricName", metricName) + } + if spec.Resource != nil { + logger.V(1).Info("Scaler for scaledObject is active", "scaler", scalerName, "metricName", spec.Resource.Name) + } } } + prommetrics.RecordScalerError(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, err) + prommetrics.RecordScalerActive(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, isMetricActive) + } + + metricTriggerPairList, err = modifiers.AddPairTriggerAndMetric(metricTriggerPairList, scaledObject, metricName, scalerConfigs[scalerIndex].TriggerName) + if err != nil { + logger.Error(err, "error pairing triggers & metrics for compositeScaler") } - prommetrics.RecordScalerError(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, err) - prommetrics.RecordScalerActive(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, isMetricActive) } } @@ -650,7 +670,38 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k logger.V(1).Info("scaler error encountered, clearing scaler cache") } - return isScaledObjectActive, isScalerError, metricsRecord, nil + // apply scaling modifiers + matchingMetrics = modifiers.HandleScalingModifiers(scaledObject, matchingMetrics, metricTriggerPairList, false, cache, logger) + + // when we are using formula, we need to reevaluate if it's active here + if scaledObject.IsUsingModifiers() && !isScalerError { + isScaledObjectActive = false + activationValue := float64(0) + if scaledObject.Spec.Advanced.ScalingModifiers.ActivationTarget != "" { + targetQueryValue, err := strconv.ParseFloat(scaledObject.Spec.Advanced.ScalingModifiers.ActivationTarget, 64) + if err != nil { + return false, true, metricsRecord, fmt.Errorf("scalingModifiers.ActivationTarget parsing error %w", err) + } + activationValue = targetQueryValue + } + + for _, metric := range matchingMetrics { + value := metric.Value.AsApproximateFloat64() + prommetrics.RecordScalerMetric(scaledObject.Namespace, scaledObject.Name, kedav1alpha1.CompositeMetricName, 0, metric.MetricName, value) + prommetrics.RecordScalerActive(scaledObject.Namespace, scaledObject.Name, kedav1alpha1.CompositeMetricName, 0, metric.MetricName, value > activationValue) + if !isScaledObjectActive { + isScaledObjectActive = value > activationValue + } + } + + } + + // if cpu/memory resource scaler has minReplicas==0 & at least one external + // trigger exists -> object can be scaled to zero + if len(scaledObject.Spec.Triggers) <= cpuMemCount && !isScalerError { + isScaledObjectActive = true + } + return isScaledObjectActive, isScalerError, metricsRecord, err } // / --------------------------------------------------------------------------- /// From a136fdafc93460e3a89236c7d275ce320dd34a1a Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Mon, 25 Sep 2023 17:15:48 +0200 Subject: [PATCH 04/26] add activation test Signed-off-by: Jorge Turrado --- .../internals/scaling_modifiers/scaling_modifiers_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/internals/scaling_modifiers/scaling_modifiers_test.go b/tests/internals/scaling_modifiers/scaling_modifiers_test.go index ab02f01ea73..036e6e63894 100644 --- a/tests/internals/scaling_modifiers/scaling_modifiers_test.go +++ b/tests/internals/scaling_modifiers/scaling_modifiers_test.go @@ -143,6 +143,7 @@ spec: scalingModifiers: formula: metrics_api + kw_trig target: '2' + activationTarget: '2' pollingInterval: 5 cooldownPeriod: 5 minReplicaCount: 0 @@ -238,6 +239,12 @@ func TestScalingModifiers(t *testing.T) { func testFormula(t *testing.T, kc *kubernetes.Clientset, data templateData) { t.Log("--- testFormula ---") + + // formula simply adds 2 metrics together (0+2=2; activationTarget = 2 -> replicas should be 0) + data.MetricValue = 0 + KubectlApplyWithTemplate(t, data, "updateMetricsTemplate", updateMetricsTemplate) + AssertReplicaCountNotChangeDuringTimePeriod(t, kc, deploymentName, namespace, 0, 60) + // formula simply adds 2 metrics together (3+2=5; target = 2 -> 5/2 replicas should be 3) data.MetricValue = 3 KubectlApplyWithTemplate(t, data, "updateMetricsTemplate", updateMetricsTemplate) From 75acb47a87fd428c877a401353c56e32acece3a5 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Mon, 25 Sep 2023 17:56:50 +0200 Subject: [PATCH 05/26] remove the requirement of value using the scaler AsMetricSource Signed-off-by: Jorge Turrado --- pkg/scalers/arangodb_scaler.go | 7 ++++++- pkg/scalers/aws_dynamodb_scaler.go | 6 +++++- pkg/scalers/azure_app_insights_scaler.go | 6 +++++- pkg/scalers/azure_log_analytics_scaler.go | 6 +++++- pkg/scalers/azure_monitor_scaler.go | 6 +++++- pkg/scalers/cassandra_scaler.go | 6 +++++- pkg/scalers/couchdb_scaler.go | 6 +++++- pkg/scalers/datadog_scaler.go | 6 +++++- pkg/scalers/elasticsearch_scaler.go | 6 +++++- pkg/scalers/gcp_cloud_tasks_scaler.go | 1 - pkg/scalers/influxdb_scaler.go | 7 ++++++- pkg/scalers/kubernetes_workload_scaler.go | 6 +++++- pkg/scalers/loki_scaler.go | 7 ++++++- pkg/scalers/metrics_api_scaler.go | 6 +++++- pkg/scalers/mongo_scaler.go | 6 +++++- pkg/scalers/mssql_scaler.go | 6 +++++- pkg/scalers/mysql_scaler.go | 7 ++++++- pkg/scalers/newrelic_scaler.go | 6 +++++- pkg/scalers/postgresql_scaler.go | 7 ++++++- pkg/scalers/predictkube_scaler.go | 7 ++++++- pkg/scalers/prometheus_scaler.go | 6 +++++- pkg/scalers/scaler.go | 3 +++ pkg/scalers/solr_scaler.go | 6 +++++- pkg/scaling/scale_handler.go | 3 +++ pkg/scaling/scalers_builder.go | 3 ++- 25 files changed, 119 insertions(+), 23 deletions(-) diff --git a/pkg/scalers/arangodb_scaler.go b/pkg/scalers/arangodb_scaler.go index 1a7208f5ee1..90a3eecce6b 100644 --- a/pkg/scalers/arangodb_scaler.go +++ b/pkg/scalers/arangodb_scaler.go @@ -155,7 +155,12 @@ func parseArangoDBMetadata(config *ScalerConfig) (*arangoDBMetadata, error) { } meta.queryValue = queryValue } else { - return nil, fmt.Errorf("no queryValue given") + if config.AsMetricSource { + meta.queryValue = 0 + } else { + return nil, fmt.Errorf("no queryValue given") + } + } meta.activationQueryValue = 0 diff --git a/pkg/scalers/aws_dynamodb_scaler.go b/pkg/scalers/aws_dynamodb_scaler.go index f4f82027092..05b173fd1ab 100644 --- a/pkg/scalers/aws_dynamodb_scaler.go +++ b/pkg/scalers/aws_dynamodb_scaler.go @@ -152,7 +152,11 @@ func parseAwsDynamoDBMetadata(config *ScalerConfig) (*awsDynamoDBMetadata, error meta.targetValue = n } else { - return nil, ErrAwsDynamoNoTargetValue + if config.AsMetricSource { + meta.targetValue = 0 + } else { + return nil, ErrAwsDynamoNoTargetValue + } } if val, ok := config.TriggerMetadata["activationTargetValue"]; ok && val != "" { diff --git a/pkg/scalers/azure_app_insights_scaler.go b/pkg/scalers/azure_app_insights_scaler.go index d704bd3bde4..b4a164dd58f 100644 --- a/pkg/scalers/azure_app_insights_scaler.go +++ b/pkg/scalers/azure_app_insights_scaler.go @@ -79,7 +79,11 @@ func parseAzureAppInsightsMetadata(config *ScalerConfig, logger logr.Logger) (*a val, err := getParameterFromConfig(config, azureAppInsightsTargetValueName, false) if err != nil { - return nil, err + if config.AsMetricSource { + meta.targetValue = 0 + } else { + return nil, err + } } targetValue, err := strconv.ParseFloat(val, 64) if err != nil { diff --git a/pkg/scalers/azure_log_analytics_scaler.go b/pkg/scalers/azure_log_analytics_scaler.go index 77cb7b74a39..167e0c83d66 100644 --- a/pkg/scalers/azure_log_analytics_scaler.go +++ b/pkg/scalers/azure_log_analytics_scaler.go @@ -182,7 +182,11 @@ func parseAzureLogAnalyticsMetadata(config *ScalerConfig) (*azureLogAnalyticsMet // Getting threshold, observe that we don't check AuthParams for threshold val, err := getParameterFromConfig(config, "threshold", false) if err != nil { - return nil, err + if config.AsMetricSource { + val = "0" + } else { + return nil, err + } } threshold, err := strconv.ParseFloat(val, 64) if err != nil { diff --git a/pkg/scalers/azure_monitor_scaler.go b/pkg/scalers/azure_monitor_scaler.go index f6a044814ec..5f66854ad33 100644 --- a/pkg/scalers/azure_monitor_scaler.go +++ b/pkg/scalers/azure_monitor_scaler.go @@ -87,7 +87,11 @@ func parseAzureMonitorMetadata(config *ScalerConfig, logger logr.Logger) (*azure } meta.targetValue = targetValue } else { - return nil, fmt.Errorf("no targetValue given") + if config.AsMetricSource { + meta.targetValue = 0 + } else { + return nil, fmt.Errorf("no targetValue given") + } } if val, ok := config.TriggerMetadata[activationTargetValueName]; ok && val != "" { diff --git a/pkg/scalers/cassandra_scaler.go b/pkg/scalers/cassandra_scaler.go index 3ee539d1623..976f6359047 100644 --- a/pkg/scalers/cassandra_scaler.go +++ b/pkg/scalers/cassandra_scaler.go @@ -82,7 +82,11 @@ func parseCassandraMetadata(config *ScalerConfig) (*CassandraMetadata, error) { } meta.targetQueryValue = targetQueryValue } else { - return nil, fmt.Errorf("no targetQueryValue given") + if config.AsMetricSource { + meta.targetQueryValue = 0 + } else { + return nil, fmt.Errorf("no targetQueryValue given") + } } meta.activationTargetQueryValue = 0 diff --git a/pkg/scalers/couchdb_scaler.go b/pkg/scalers/couchdb_scaler.go index 507a5848f97..3025e807ec7 100644 --- a/pkg/scalers/couchdb_scaler.go +++ b/pkg/scalers/couchdb_scaler.go @@ -114,7 +114,11 @@ func parseCouchDBMetadata(config *ScalerConfig) (*couchDBMetadata, string, error } meta.queryValue = queryValue } else { - return nil, "", fmt.Errorf("no queryValue given") + if config.AsMetricSource { + meta.queryValue = 0 + } else { + return nil, "", fmt.Errorf("no queryValue given") + } } meta.activationQueryValue = 0 diff --git a/pkg/scalers/datadog_scaler.go b/pkg/scalers/datadog_scaler.go index e021bfa7cc9..8e643fe118d 100644 --- a/pkg/scalers/datadog_scaler.go +++ b/pkg/scalers/datadog_scaler.go @@ -143,7 +143,11 @@ func parseDatadogMetadata(config *ScalerConfig, logger logr.Logger) (*datadogMet } meta.queryValue = queryValue } else { - return nil, fmt.Errorf("no queryValue given") + if config.AsMetricSource { + meta.queryValue = 0 + } else { + return nil, fmt.Errorf("no queryValue given") + } } if val, ok := config.TriggerMetadata["queryAggregator"]; ok && val != "" { diff --git a/pkg/scalers/elasticsearch_scaler.go b/pkg/scalers/elasticsearch_scaler.go index f06827b9f44..6e72ea4ce5b 100644 --- a/pkg/scalers/elasticsearch_scaler.go +++ b/pkg/scalers/elasticsearch_scaler.go @@ -199,7 +199,11 @@ func parseElasticsearchMetadata(config *ScalerConfig) (*elasticsearchMetadata, e targetValueString, err := GetFromAuthOrMeta(config, "targetValue") if err != nil { - return nil, err + if config.AsMetricSource { + targetValueString = "0" + } else { + return nil, err + } } targetValue, err := strconv.ParseFloat(targetValueString, 64) if err != nil { diff --git a/pkg/scalers/gcp_cloud_tasks_scaler.go b/pkg/scalers/gcp_cloud_tasks_scaler.go index 88a3382702c..e482824b68e 100644 --- a/pkg/scalers/gcp_cloud_tasks_scaler.go +++ b/pkg/scalers/gcp_cloud_tasks_scaler.go @@ -73,7 +73,6 @@ func parseGcpCloudTasksMetadata(config *ScalerConfig) (*gcpCloudTaskMetadata, er if val == "" { return nil, fmt.Errorf("no queue name given") } - meta.queueName = val } else { return nil, fmt.Errorf("no queue name given") diff --git a/pkg/scalers/influxdb_scaler.go b/pkg/scalers/influxdb_scaler.go index 36aa59230e1..25ce21193d3 100644 --- a/pkg/scalers/influxdb_scaler.go +++ b/pkg/scalers/influxdb_scaler.go @@ -131,7 +131,12 @@ func parseInfluxDBMetadata(config *ScalerConfig) (*influxDBMetadata, error) { } thresholdValue = value } else { - return nil, fmt.Errorf("no threshold value given") + if config.AsMetricSource { + thresholdValue = 0 + } else { + return nil, fmt.Errorf("no threshold value given") + } + } unsafeSsl = false if val, ok := config.TriggerMetadata["unsafeSsl"]; ok { diff --git a/pkg/scalers/kubernetes_workload_scaler.go b/pkg/scalers/kubernetes_workload_scaler.go index 342a8956cee..3e2bc8fca91 100644 --- a/pkg/scalers/kubernetes_workload_scaler.go +++ b/pkg/scalers/kubernetes_workload_scaler.go @@ -73,7 +73,11 @@ func parseWorkloadMetadata(config *ScalerConfig) (*kubernetesWorkloadMetadata, e meta.podSelector = podSelector value, err := strconv.ParseFloat(config.TriggerMetadata[valueKey], 64) if err != nil || value == 0 { - return nil, fmt.Errorf("value must be a float greater than 0") + if config.AsMetricSource { + value = 0 + } else { + return nil, fmt.Errorf("value must be a float greater than 0") + } } meta.value = value diff --git a/pkg/scalers/loki_scaler.go b/pkg/scalers/loki_scaler.go index 601bffa5f18..98c9912bbd2 100644 --- a/pkg/scalers/loki_scaler.go +++ b/pkg/scalers/loki_scaler.go @@ -110,7 +110,12 @@ func parseLokiMetadata(config *ScalerConfig) (meta *lokiMetadata, err error) { meta.threshold = t } else { - return nil, fmt.Errorf("no %s given", lokiThreshold) + if config.AsMetricSource { + meta.threshold = 0 + } else { + return nil, fmt.Errorf("no %s given", lokiThreshold) + } + } meta.activationThreshold = 0 diff --git a/pkg/scalers/metrics_api_scaler.go b/pkg/scalers/metrics_api_scaler.go index 668288e797c..350f3981eee 100644 --- a/pkg/scalers/metrics_api_scaler.go +++ b/pkg/scalers/metrics_api_scaler.go @@ -114,7 +114,11 @@ func parseMetricsAPIMetadata(config *ScalerConfig) (*metricsAPIScalerMetadata, e } meta.targetValue = targetValue } else { - return nil, fmt.Errorf("no targetValue given in metadata") + if config.AsMetricSource { + meta.targetValue = 0 + } else { + return nil, fmt.Errorf("no targetValue given in metadata") + } } meta.activationTargetValue = 0 diff --git a/pkg/scalers/mongo_scaler.go b/pkg/scalers/mongo_scaler.go index 61328f0ab7b..271e5567c3b 100644 --- a/pkg/scalers/mongo_scaler.go +++ b/pkg/scalers/mongo_scaler.go @@ -131,7 +131,11 @@ func parseMongoDBMetadata(config *ScalerConfig) (*mongoDBMetadata, string, error } meta.queryValue = queryValue } else { - return nil, "", fmt.Errorf("no queryValue given") + if config.AsMetricSource { + meta.queryValue = 0 + } else { + return nil, "", fmt.Errorf("no queryValue given") + } } meta.activationQueryValue = 0 diff --git a/pkg/scalers/mssql_scaler.go b/pkg/scalers/mssql_scaler.go index 285b6e9acb6..353420d326e 100644 --- a/pkg/scalers/mssql_scaler.go +++ b/pkg/scalers/mssql_scaler.go @@ -113,7 +113,11 @@ func parseMSSQLMetadata(config *ScalerConfig) (*mssqlMetadata, error) { } meta.targetValue = targetValue } else { - return nil, ErrMsSQLNoTargetValue + if config.AsMetricSource { + meta.targetValue = 0 + } else { + return nil, ErrMsSQLNoTargetValue + } } // Activation target value diff --git a/pkg/scalers/mysql_scaler.go b/pkg/scalers/mysql_scaler.go index 5ba4b5eedc5..b2c8458107f 100644 --- a/pkg/scalers/mysql_scaler.go +++ b/pkg/scalers/mysql_scaler.go @@ -78,7 +78,12 @@ func parseMySQLMetadata(config *ScalerConfig) (*mySQLMetadata, error) { } meta.queryValue = queryValue } else { - return nil, fmt.Errorf("no queryValue given") + if config.AsMetricSource { + meta.queryValue = 0 + } else { + return nil, fmt.Errorf("no queryValue given") + } + } meta.activationQueryValue = 0 diff --git a/pkg/scalers/newrelic_scaler.go b/pkg/scalers/newrelic_scaler.go index 5090732da02..8aef0a71de8 100644 --- a/pkg/scalers/newrelic_scaler.go +++ b/pkg/scalers/newrelic_scaler.go @@ -116,7 +116,11 @@ func parseNewRelicMetadata(config *ScalerConfig, logger logr.Logger) (*newrelicM } meta.threshold = t } else { - return nil, fmt.Errorf("missing %s value", threshold) + if config.AsMetricSource { + meta.threshold = 0 + } else { + return nil, fmt.Errorf("missing %s value", threshold) + } } meta.activationThreshold = 0 diff --git a/pkg/scalers/postgresql_scaler.go b/pkg/scalers/postgresql_scaler.go index e9aa6d562a5..e23aa1ad7b1 100644 --- a/pkg/scalers/postgresql_scaler.go +++ b/pkg/scalers/postgresql_scaler.go @@ -73,7 +73,12 @@ func parsePostgreSQLMetadata(config *ScalerConfig) (*postgreSQLMetadata, error) } meta.targetQueryValue = targetQueryValue } else { - return nil, fmt.Errorf("no targetQueryValue given") + if config.AsMetricSource { + meta.targetQueryValue = 0 + } else { + return nil, fmt.Errorf("no targetQueryValue given") + } + } meta.activationTargetQueryValue = 0 diff --git a/pkg/scalers/predictkube_scaler.go b/pkg/scalers/predictkube_scaler.go index 4ea7341a088..c699091bc34 100644 --- a/pkg/scalers/predictkube_scaler.go +++ b/pkg/scalers/predictkube_scaler.go @@ -405,7 +405,12 @@ func parsePredictKubeMetadata(config *ScalerConfig) (result *predictKubeMetadata } meta.threshold = threshold } else { - return nil, fmt.Errorf("no threshold given") + if config.AsMetricSource { + meta.threshold = 0 + } else { + return nil, fmt.Errorf("no threshold given") + } + } meta.activationThreshold = 0 diff --git a/pkg/scalers/prometheus_scaler.go b/pkg/scalers/prometheus_scaler.go index 4cde60ec88c..f80e801207b 100644 --- a/pkg/scalers/prometheus_scaler.go +++ b/pkg/scalers/prometheus_scaler.go @@ -159,7 +159,11 @@ func parsePrometheusMetadata(config *ScalerConfig) (meta *prometheusMetadata, er meta.threshold = t } else { - return nil, fmt.Errorf("no %s given", promThreshold) + if config.AsMetricSource { + meta.threshold = 0 + } else { + return nil, fmt.Errorf("no %s given", promThreshold) + } } meta.activationThreshold = 0 diff --git a/pkg/scalers/scaler.go b/pkg/scalers/scaler.go index b90bced0be6..3aaa1bad11a 100644 --- a/pkg/scalers/scaler.go +++ b/pkg/scalers/scaler.go @@ -99,6 +99,9 @@ type ScalerConfig struct { // MetricType MetricType v2.MetricTargetType + + // When we use the scaler for composite scaler, we shouldn't require the value because it'll be ignored + AsMetricSource bool } var ( diff --git a/pkg/scalers/solr_scaler.go b/pkg/scalers/solr_scaler.go index 486eb0c4cd7..7188286901a 100644 --- a/pkg/scalers/solr_scaler.go +++ b/pkg/scalers/solr_scaler.go @@ -93,7 +93,11 @@ func parseSolrMetadata(config *ScalerConfig) (*solrMetadata, error) { } meta.targetQueryValue = targetQueryValue } else { - return nil, fmt.Errorf("no targetQueryValue given") + if config.AsMetricSource { + meta.targetQueryValue = 0 + } else { + return nil, fmt.Errorf("no targetQueryValue given") + } } meta.activationTargetQueryValue = 0 diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index 0bbec91ce31..6bf22d2160b 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -320,6 +320,8 @@ func (h *scaleHandler) performGetScalersCache(ctx context.Context, key string, s } } + asMetricSource := false + if scalableObject == nil { switch scalableObjectKind { case "ScaledObject": @@ -330,6 +332,7 @@ func (h *scaleHandler) performGetScalersCache(ctx context.Context, key string, s return nil, err } scalableObject = scaledObject + asMetricSource = scaledObject.IsUsingModifiers() case "ScaledJob": scaledJob := &kedav1alpha1.ScaledJob{} err := h.client.Get(ctx, types.NamespacedName{Name: scalableObjectName, Namespace: scalableObjectNamespace}, scaledJob) diff --git a/pkg/scaling/scalers_builder.go b/pkg/scaling/scalers_builder.go index b9f1a23f2e1..58cd74ac5cc 100644 --- a/pkg/scaling/scalers_builder.go +++ b/pkg/scaling/scalers_builder.go @@ -36,7 +36,7 @@ import ( /// --------------------------------------------------------------------------- /// // buildScalers returns list of Scalers for the specified triggers -func (h *scaleHandler) buildScalers(ctx context.Context, withTriggers *kedav1alpha1.WithTriggers, podTemplateSpec *corev1.PodTemplateSpec, containerName string) ([]cache.ScalerBuilder, error) { +func (h *scaleHandler) buildScalers(ctx context.Context, withTriggers *kedav1alpha1.WithTriggers, podTemplateSpec *corev1.PodTemplateSpec, containerName string, asMetricSource bool) ([]cache.ScalerBuilder, error) { logger := log.WithValues("type", withTriggers.Kind, "namespace", withTriggers.Namespace, "name", withTriggers.Name) var err error resolvedEnv := make(map[string]string) @@ -64,6 +64,7 @@ func (h *scaleHandler) buildScalers(ctx context.Context, withTriggers *kedav1alp GlobalHTTPTimeout: h.globalHTTPTimeout, ScalerIndex: triggerIndex, MetricType: trigger.MetricType, + AsMetricSource: asMetricSource, } authParams, podIdentity, err := resolver.ResolveAuthRefAndPodIdentity(ctx, h.client, logger, trigger.AuthenticationRef, podTemplateSpec, withTriggers.Namespace, h.secretsLister) From d59ac67d66f8c7c7d8db40f70e1d6964daa39572 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Mon, 25 Sep 2023 17:57:29 +0200 Subject: [PATCH 06/26] add missing change Signed-off-by: Jorge Turrado --- pkg/scaling/scale_handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index 6bf22d2160b..adc80353420 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -358,7 +358,7 @@ func (h *scaleHandler) performGetScalersCache(ctx context.Context, key string, s return nil, err } - scalers, err := h.buildScalers(ctx, withTriggers, podTemplateSpec, containerName) + scalers, err := h.buildScalers(ctx, withTriggers, podTemplateSpec, containerName, asMetricSource) if err != nil { return nil, err } From 69500826598e0d04b6caf096bb3647cab7b93f5e Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Mon, 25 Sep 2023 18:36:01 +0200 Subject: [PATCH 07/26] reduce complexity Signed-off-by: Jorge Turrado --- pkg/scaling/scale_handler.go | 205 +++++++++++++++++++++-------------- 1 file changed, 124 insertions(+), 81 deletions(-) diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index adc80353420..da11ce6a7aa 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -34,6 +34,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/go-logr/logr" kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1" "github.com/kedacore/keda/v2/pkg/common/message" "github.com/kedacore/keda/v2/pkg/eventreason" @@ -557,7 +558,7 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k logger := log.WithValues("scaledObject.Namespace", scaledObject.Namespace, "scaledObject.Name", scaledObject.Name) isScaledObjectActive := false - isScalerError := false + isScaledObjectError := false metricsRecord := map[string]metricscache.MetricsRecord{} metricTriggerPairList := make(map[string]string) var matchingMetrics []external_metrics.ExternalMetricValue @@ -582,90 +583,31 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k // Let's collect status of all scalers, no matter if any scaler raises error or is active scalers, scalerConfigs := cache.GetScalers() for scalerIndex := 0; scalerIndex < len(scalers); scalerIndex++ { - scalerName := strings.Replace(fmt.Sprintf("%T", scalers[scalerIndex]), "*scalers.", "", 1) - if scalerConfigs[scalerIndex].TriggerName != "" { - scalerName = scalerConfigs[scalerIndex].TriggerName + result := + h.processScaledObjectStateScaler( + ctx, + scalers[scalerIndex], + scalerIndex, + scalerConfigs[scalerIndex], + cache, + logger, + scaledObject, + ) + if !isScaledObjectActive { + isScaledObjectActive = result.IsActive } - - metricSpecs, err := cache.GetMetricSpecForScalingForScaler(ctx, scalerIndex) - if err != nil { - isScalerError = true - logger.Error(err, "error getting metric spec for the scaler", "scaler", scalerName) - cache.Recorder.Event(scaledObject, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) + if !isScaledObjectError { + isScaledObjectError = result.IsError } - - for _, spec := range metricSpecs { - // if cpu/memory resource scaler has minReplicas==0 & at least one external - // trigger exists -> object can be scaled to zero - if spec.External == nil { - continue - } - - metricName := spec.External.Metric.Name - - var latency int64 - metrics, isMetricActive, latency, err := cache.GetMetricsAndActivityForScaler(ctx, scalerIndex, metricName) - if latency != -1 { - prommetrics.RecordScalerLatency(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, float64(latency)) - } - matchingMetrics = append(matchingMetrics, metrics...) - logger.V(1).Info("Getting metrics and activity from scaler", "scaler", scalerName, "metricName", metricName, "metrics", metrics, "activity", isMetricActive, "scalerError", err) - - if scalerConfigs[scalerIndex].TriggerUseCachedMetrics { - metricsRecord[metricName] = metricscache.MetricsRecord{ - IsActive: isMetricActive, - Metric: metrics, - ScalerError: err, - } - } - - if scaledObject.IsUsingModifiers() { - if err != nil { - isScalerError = true - logger.Error(err, "error getting metric source", "source", scalerName) - cache.Recorder.Event(scaledObject, corev1.EventTypeWarning, eventreason.KEDAMetricSourceFailed, err.Error()) - } else { - for _, metric := range metrics { - metricValue := metric.Value.AsApproximateFloat64() - prommetrics.RecordScalerMetric(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metric.MetricName, metricValue) - } - } - prommetrics.RecordScalerError(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, err) - } else { - if err != nil { - isScalerError = true - logger.Error(err, "error getting scale decision", "scaler", scalerName) - cache.Recorder.Event(scaledObject, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) - } else { - for _, metric := range metrics { - metricValue := metric.Value.AsApproximateFloat64() - prommetrics.RecordScalerMetric(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metric.MetricName, metricValue) - } - - if isMetricActive { - isScaledObjectActive = true - if spec.External != nil { - logger.V(1).Info("Scaler for scaledObject is active", "scaler", scalerName, "metricName", metricName) - } - if spec.Resource != nil { - logger.V(1).Info("Scaler for scaledObject is active", "scaler", scalerName, "metricName", spec.Resource.Name) - } - } - } - prommetrics.RecordScalerError(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, err) - prommetrics.RecordScalerActive(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, isMetricActive) - } - - metricTriggerPairList, err = modifiers.AddPairTriggerAndMetric(metricTriggerPairList, scaledObject, metricName, scalerConfigs[scalerIndex].TriggerName) - if err != nil { - logger.Error(err, "error pairing triggers & metrics for compositeScaler") - } + matchingMetrics = append(matchingMetrics, result.Metrics...) + for k, v := range result.Pairs { + metricTriggerPairList[k] = v } } // invalidate the cache for the ScaledObject, if we hit an error in any scaler // in this case we try to build all scalers (and resolve all secrets/creds) again in the next call - if isScalerError { + if isScaledObjectError { err := h.ClearScalersCache(ctx, scaledObject) if err != nil { logger.Error(err, "error clearing scalers cache") @@ -677,7 +619,7 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k matchingMetrics = modifiers.HandleScalingModifiers(scaledObject, matchingMetrics, metricTriggerPairList, false, cache, logger) // when we are using formula, we need to reevaluate if it's active here - if scaledObject.IsUsingModifiers() && !isScalerError { + if scaledObject.IsUsingModifiers() && !isScaledObjectError { isScaledObjectActive = false activationValue := float64(0) if scaledObject.Spec.Advanced.ScalingModifiers.ActivationTarget != "" { @@ -701,10 +643,111 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k // if cpu/memory resource scaler has minReplicas==0 & at least one external // trigger exists -> object can be scaled to zero - if len(scaledObject.Spec.Triggers) <= cpuMemCount && !isScalerError { + if len(scaledObject.Spec.Triggers) <= cpuMemCount && !isScaledObjectError { isScaledObjectActive = true } - return isScaledObjectActive, isScalerError, metricsRecord, err + return isScaledObjectActive, isScaledObjectError, metricsRecord, err +} + +type ScaledObjectStateScalerResult struct { + IsActive bool + IsError bool + Metrics []external_metrics.ExternalMetricValue + Pairs map[string]string + Records map[string]metricscache.MetricsRecord +} + +func (*scaleHandler) processScaledObjectStateScaler( + ctx context.Context, + scaler scalers.Scaler, + scalerIndex int, + scalerConfig scalers.ScalerConfig, + cache *cache.ScalersCache, + logger logr.Logger, + scaledObject *kedav1alpha1.ScaledObject, +) ScaledObjectStateScalerResult { + result := ScaledObjectStateScalerResult{} + + scalerName := strings.Replace(fmt.Sprintf("%T", scaler), "*scalers.", "", 1) + if scalerConfig.TriggerName != "" { + scalerName = scalerConfig.TriggerName + } + + metricSpecs, err := cache.GetMetricSpecForScalingForScaler(ctx, scalerIndex) + if err != nil { + result.IsError = true + logger.Error(err, "error getting metric spec for the scaler", "scaler", scalerName) + cache.Recorder.Event(scaledObject, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) + } + + for _, spec := range metricSpecs { + // if cpu/memory resource scaler has minReplicas==0 & at least one external + // trigger exists -> object can be scaled to zero + if spec.External == nil { + continue + } + + metricName := spec.External.Metric.Name + + var latency int64 + metrics, isMetricActive, latency, err := cache.GetMetricsAndActivityForScaler(ctx, scalerIndex, metricName) + if latency != -1 { + prommetrics.RecordScalerLatency(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, float64(latency)) + } + result.Metrics = append(result.Metrics, metrics...) + logger.V(1).Info("Getting metrics and activity from scaler", "scaler", scalerName, "metricName", metricName, "metrics", metrics, "activity", isMetricActive, "scalerError", err) + + if scalerConfig.TriggerUseCachedMetrics { + result.Records[metricName] = metricscache.MetricsRecord{ + IsActive: isMetricActive, + Metric: metrics, + ScalerError: err, + } + } + + if scaledObject.IsUsingModifiers() { + if err != nil { + result.IsError = true + logger.Error(err, "error getting metric source", "source", scalerName) + cache.Recorder.Event(scaledObject, corev1.EventTypeWarning, eventreason.KEDAMetricSourceFailed, err.Error()) + } else { + for _, metric := range metrics { + metricValue := metric.Value.AsApproximateFloat64() + prommetrics.RecordScalerMetric(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metric.MetricName, metricValue) + } + } + prommetrics.RecordScalerError(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, err) + } else { + if err != nil { + result.IsError = true + logger.Error(err, "error getting scale decision", "scaler", scalerName) + cache.Recorder.Event(scaledObject, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) + } else { + for _, metric := range metrics { + metricValue := metric.Value.AsApproximateFloat64() + prommetrics.RecordScalerMetric(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metric.MetricName, metricValue) + } + + if isMetricActive { + result.IsActive = true + if spec.External != nil { + logger.V(1).Info("Scaler for scaledObject is active", "scaler", scalerName, "metricName", metricName) + } + if spec.Resource != nil { + logger.V(1).Info("Scaler for scaledObject is active", "scaler", scalerName, "metricName", spec.Resource.Name) + } + } + } + prommetrics.RecordScalerError(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, err) + prommetrics.RecordScalerActive(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, isMetricActive) + } + + result.Pairs, err = modifiers.AddPairTriggerAndMetric(result.Pairs, scaledObject, metricName, scalerConfig.TriggerName) + if err != nil { + logger.Error(err, "error pairing triggers & metrics for compositeScaler") + } + } + return result } // / --------------------------------------------------------------------------- /// From 40be21c33965d887b1477ba9073697ac95a32b7c Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Mon, 25 Sep 2023 18:38:08 +0200 Subject: [PATCH 08/26] revert go.uber.org/automaxprocs Signed-off-by: Jorge Turrado --- cmd/adapter/main.go | 1 - cmd/operator/main.go | 1 - cmd/webhooks/main.go | 1 - go.mod | 1 - go.sum | 2 - vendor/go.uber.org/automaxprocs/.codecov.yml | 14 -- vendor/go.uber.org/automaxprocs/.gitignore | 33 ---- vendor/go.uber.org/automaxprocs/CHANGELOG.md | 47 ----- .../automaxprocs/CODE_OF_CONDUCT.md | 75 -------- .../go.uber.org/automaxprocs/CONTRIBUTING.md | 81 -------- vendor/go.uber.org/automaxprocs/LICENSE | 19 -- vendor/go.uber.org/automaxprocs/Makefile | 46 ----- vendor/go.uber.org/automaxprocs/README.md | 71 ------- .../go.uber.org/automaxprocs/automaxprocs.go | 33 ---- vendor/go.uber.org/automaxprocs/glide.yaml | 7 - .../automaxprocs/internal/cgroups/cgroup.go | 79 -------- .../automaxprocs/internal/cgroups/cgroups.go | 118 ------------ .../automaxprocs/internal/cgroups/cgroups2.go | 176 ------------------ .../automaxprocs/internal/cgroups/doc.go | 23 --- .../automaxprocs/internal/cgroups/errors.go | 52 ------ .../internal/cgroups/mountpoint.go | 171 ----------------- .../automaxprocs/internal/cgroups/subsys.go | 103 ---------- .../internal/runtime/cpu_quota_linux.go | 71 ------- .../internal/runtime/cpu_quota_unsupported.go | 31 --- .../automaxprocs/internal/runtime/runtime.go | 33 ---- .../automaxprocs/maxprocs/maxprocs.go | 130 ------------- .../automaxprocs/maxprocs/version.go | 24 --- vendor/modules.txt | 6 - 28 files changed, 1449 deletions(-) delete mode 100644 vendor/go.uber.org/automaxprocs/.codecov.yml delete mode 100644 vendor/go.uber.org/automaxprocs/.gitignore delete mode 100644 vendor/go.uber.org/automaxprocs/CHANGELOG.md delete mode 100644 vendor/go.uber.org/automaxprocs/CODE_OF_CONDUCT.md delete mode 100644 vendor/go.uber.org/automaxprocs/CONTRIBUTING.md delete mode 100644 vendor/go.uber.org/automaxprocs/LICENSE delete mode 100644 vendor/go.uber.org/automaxprocs/Makefile delete mode 100644 vendor/go.uber.org/automaxprocs/README.md delete mode 100644 vendor/go.uber.org/automaxprocs/automaxprocs.go delete mode 100644 vendor/go.uber.org/automaxprocs/glide.yaml delete mode 100644 vendor/go.uber.org/automaxprocs/internal/cgroups/cgroup.go delete mode 100644 vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups.go delete mode 100644 vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups2.go delete mode 100644 vendor/go.uber.org/automaxprocs/internal/cgroups/doc.go delete mode 100644 vendor/go.uber.org/automaxprocs/internal/cgroups/errors.go delete mode 100644 vendor/go.uber.org/automaxprocs/internal/cgroups/mountpoint.go delete mode 100644 vendor/go.uber.org/automaxprocs/internal/cgroups/subsys.go delete mode 100644 vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_linux.go delete mode 100644 vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_unsupported.go delete mode 100644 vendor/go.uber.org/automaxprocs/internal/runtime/runtime.go delete mode 100644 vendor/go.uber.org/automaxprocs/maxprocs/maxprocs.go delete mode 100644 vendor/go.uber.org/automaxprocs/maxprocs/version.go diff --git a/cmd/adapter/main.go b/cmd/adapter/main.go index cea7ff84238..7f17bc105a1 100644 --- a/cmd/adapter/main.go +++ b/cmd/adapter/main.go @@ -24,7 +24,6 @@ import ( "os" "github.com/prometheus/client_golang/prometheus/collectors" - _ "go.uber.org/automaxprocs" appsv1 "k8s.io/api/apps/v1" apimetrics "k8s.io/apiserver/pkg/endpoints/metrics" "k8s.io/client-go/kubernetes/scheme" diff --git a/cmd/operator/main.go b/cmd/operator/main.go index d3a5a8ed9cb..4f1984e728e 100644 --- a/cmd/operator/main.go +++ b/cmd/operator/main.go @@ -22,7 +22,6 @@ import ( "time" "github.com/spf13/pflag" - _ "go.uber.org/automaxprocs" apimachineryruntime "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" kubeinformers "k8s.io/client-go/informers" diff --git a/cmd/webhooks/main.go b/cmd/webhooks/main.go index 1ad2cbec2f1..ac736c89458 100644 --- a/cmd/webhooks/main.go +++ b/cmd/webhooks/main.go @@ -22,7 +22,6 @@ import ( "os" "github.com/spf13/pflag" - _ "go.uber.org/automaxprocs" apimachineryruntime "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" diff --git a/go.mod b/go.mod index 0d350d91613..164434f6202 100644 --- a/go.mod +++ b/go.mod @@ -299,7 +299,6 @@ require ( go.opentelemetry.io/proto/otlp v0.19.0 // indirect go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect go.uber.org/atomic v1.11.0 // indirect - go.uber.org/automaxprocs v1.5.3 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/crypto v0.13.0 // indirect diff --git a/go.sum b/go.sum index b100e0111fe..4dba79e7c90 100644 --- a/go.sum +++ b/go.sum @@ -941,8 +941,6 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= -go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= diff --git a/vendor/go.uber.org/automaxprocs/.codecov.yml b/vendor/go.uber.org/automaxprocs/.codecov.yml deleted file mode 100644 index 9a2ed4a9969..00000000000 --- a/vendor/go.uber.org/automaxprocs/.codecov.yml +++ /dev/null @@ -1,14 +0,0 @@ -coverage: - range: 80..100 - round: down - precision: 2 - - status: - project: # measuring the overall project coverage - default: # context, you can create multiple ones with custom titles - enabled: yes # must be yes|true to enable this status - target: 90% # specify the target coverage for each commit status - # option: "auto" (must increase from parent commit or pull request base) - # option: "X%" a static target percentage to hit - if_not_found: success # if parent is not found report status as success, error, or failure - if_ci_failed: error # if ci fails report status as success, error, or failure diff --git a/vendor/go.uber.org/automaxprocs/.gitignore b/vendor/go.uber.org/automaxprocs/.gitignore deleted file mode 100644 index dd7bcf5130b..00000000000 --- a/vendor/go.uber.org/automaxprocs/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test -vendor - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof -*.pprof -*.out -*.log -coverage.txt - -/bin -cover.out -cover.html diff --git a/vendor/go.uber.org/automaxprocs/CHANGELOG.md b/vendor/go.uber.org/automaxprocs/CHANGELOG.md deleted file mode 100644 index 274797b0267..00000000000 --- a/vendor/go.uber.org/automaxprocs/CHANGELOG.md +++ /dev/null @@ -1,47 +0,0 @@ -# Changelog - -## v1.5.3 (2023-07-19) - -- Fix mountinfo parsing when super options have fields with spaces. -- Fix division by zero while parsing cgroups. - -## v1.5.2 (2023-03-16) - -- Support child control cgroups -- Fix file descriptor leak -- Update dependencies - -## v1.5.1 (2022-04-06) - -- Fix cgroups v2 mountpoint detection. - -## v1.5.0 (2022-04-05) - -- Add support for cgroups v2. - -Thanks to @emadolsky for their contribution to this release. - -## v1.4.0 (2021-02-01) - -- Support colons in cgroup names. -- Remove linters from runtime dependencies. - -## v1.3.0 (2020-01-23) - -- Migrate to Go modules. - -## v1.2.0 (2018-02-22) - -- Fixed quota clamping to always round down rather than up; Rather than - guaranteeing constant throttling at saturation, instead assume that the - fractional CPU was added as a hedge for factors outside of Go's scheduler. - -## v1.1.0 (2017-11-10) - -- Log the new value of `GOMAXPROCS` rather than the current value. -- Make logs more explicit about whether `GOMAXPROCS` was modified or not. -- Allow customization of the minimum `GOMAXPROCS`, and modify default from 2 to 1. - -## v1.0.0 (2017-08-09) - -- Initial release. diff --git a/vendor/go.uber.org/automaxprocs/CODE_OF_CONDUCT.md b/vendor/go.uber.org/automaxprocs/CODE_OF_CONDUCT.md deleted file mode 100644 index e327d9aa5cd..00000000000 --- a/vendor/go.uber.org/automaxprocs/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,75 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, -body size, disability, ethnicity, gender identity and expression, level of -experience, nationality, personal appearance, race, religion, or sexual -identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an -appointed representative at an online or offline event. Representation of a -project may be further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at oss-conduct@uber.com. The project -team will review and investigate all complaints, and will respond in a way -that it deems appropriate to the circumstances. The project team is obligated -to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 1.4, available at -[http://contributor-covenant.org/version/1/4][version]. - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/go.uber.org/automaxprocs/CONTRIBUTING.md b/vendor/go.uber.org/automaxprocs/CONTRIBUTING.md deleted file mode 100644 index 2b6a6040d78..00000000000 --- a/vendor/go.uber.org/automaxprocs/CONTRIBUTING.md +++ /dev/null @@ -1,81 +0,0 @@ -# Contributing - -We'd love your help improving this package! - -If you'd like to add new exported APIs, please [open an issue][open-issue] -describing your proposal — discussing API changes ahead of time makes -pull request review much smoother. In your issue, pull request, and any other -communications, please remember to treat your fellow contributors with -respect! We take our [code of conduct](CODE_OF_CONDUCT.md) seriously. - -Note that you'll need to sign [Uber's Contributor License Agreement][cla] -before we can accept any of your contributions. If necessary, a bot will remind -you to accept the CLA when you open your pull request. - -## Setup - -[Fork][fork], then clone the repository: - -``` -mkdir -p $GOPATH/src/go.uber.org -cd $GOPATH/src/go.uber.org -git clone git@github.com:your_github_username/automaxprocs.git -cd automaxprocs -git remote add upstream https://github.com/uber-go/automaxprocs.git -git fetch upstream -``` - -Install the test dependencies: - -``` -make dependencies -``` - -Make sure that the tests and the linters pass: - -``` -make test -make lint -``` - -If you're not using the minor version of Go specified in the Makefile's -`LINTABLE_MINOR_VERSIONS` variable, `make lint` doesn't do anything. This is -fine, but it means that you'll only discover lint failures after you open your -pull request. - -## Making Changes - -Start by creating a new branch for your changes: - -``` -cd $GOPATH/src/go.uber.org/automaxprocs -git checkout master -git fetch upstream -git rebase upstream/master -git checkout -b cool_new_feature -``` - -Make your changes, then ensure that `make lint` and `make test` still pass. If -you're satisfied with your changes, push them to your fork. - -``` -git push origin cool_new_feature -``` - -Then use the GitHub UI to open a pull request. - -At this point, you're waiting on us to review your changes. We *try* to respond -to issues and pull requests within a few business days, and we may suggest some -improvements or alternatives. Once your changes are approved, one of the -project maintainers will merge them. - -We're much more likely to approve your changes if you: - -* Add tests for new functionality. -* Write a [good commit message][commit-message]. -* Maintain backward compatibility. - -[fork]: https://github.com/uber-go/automaxprocs/fork -[open-issue]: https://github.com/uber-go/automaxprocs/issues/new -[cla]: https://cla-assistant.io/uber-go/automaxprocs -[commit-message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html diff --git a/vendor/go.uber.org/automaxprocs/LICENSE b/vendor/go.uber.org/automaxprocs/LICENSE deleted file mode 100644 index 20dcf51d96d..00000000000 --- a/vendor/go.uber.org/automaxprocs/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2017 Uber Technologies, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/vendor/go.uber.org/automaxprocs/Makefile b/vendor/go.uber.org/automaxprocs/Makefile deleted file mode 100644 index 1642b714801..00000000000 --- a/vendor/go.uber.org/automaxprocs/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -export GOBIN ?= $(shell pwd)/bin - -GO_FILES := $(shell \ - find . '(' -path '*/.*' -o -path './vendor' ')' -prune \ - -o -name '*.go' -print | cut -b3-) - -GOLINT = $(GOBIN)/golint -STATICCHECK = $(GOBIN)/staticcheck - -.PHONY: build -build: - go build ./... - -.PHONY: install -install: - go mod download - -.PHONY: test -test: - go test -race ./... - -.PHONY: cover -cover: - go test -coverprofile=cover.out -covermode=atomic -coverpkg=./... ./... - go tool cover -html=cover.out -o cover.html - -$(GOLINT): tools/go.mod - cd tools && go install golang.org/x/lint/golint - -$(STATICCHECK): tools/go.mod - cd tools && go install honnef.co/go/tools/cmd/staticcheck@2023.1.2 - -.PHONY: lint -lint: $(GOLINT) $(STATICCHECK) - @rm -rf lint.log - @echo "Checking gofmt" - @gofmt -d -s $(GO_FILES) 2>&1 | tee lint.log - @echo "Checking go vet" - @go vet ./... 2>&1 | tee -a lint.log - @echo "Checking golint" - @$(GOLINT) ./... | tee -a lint.log - @echo "Checking staticcheck" - @$(STATICCHECK) ./... 2>&1 | tee -a lint.log - @echo "Checking for license headers..." - @./.build/check_license.sh | tee -a lint.log - @[ ! -s lint.log ] diff --git a/vendor/go.uber.org/automaxprocs/README.md b/vendor/go.uber.org/automaxprocs/README.md deleted file mode 100644 index bfed32adae8..00000000000 --- a/vendor/go.uber.org/automaxprocs/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# automaxprocs [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] - -Automatically set `GOMAXPROCS` to match Linux container CPU quota. - -## Installation - -`go get -u go.uber.org/automaxprocs` - -## Quick Start - -```go -import _ "go.uber.org/automaxprocs" - -func main() { - // Your application logic here. -} -``` - -# Performance -Data measured from Uber's internal load balancer. We ran the load balancer with 200% CPU quota (i.e., 2 cores): - -| GOMAXPROCS | RPS | P50 (ms) | P99.9 (ms) | -| ------------------ | --------- | -------- | ---------- | -| 1 | 28,893.18 | 1.46 | 19.70 | -| 2 (equal to quota) | 44,715.07 | 0.84 | 26.38 | -| 3 | 44,212.93 | 0.66 | 30.07 | -| 4 | 41,071.15 | 0.57 | 42.94 | -| 8 | 33,111.69 | 0.43 | 64.32 | -| Default (24) | 22,191.40 | 0.45 | 76.19 | - -When `GOMAXPROCS` is increased above the CPU quota, we see P50 decrease slightly, but see significant increases to P99. We also see that the total RPS handled also decreases. - -When `GOMAXPROCS` is higher than the CPU quota allocated, we also saw significant throttling: - -``` -$ cat /sys/fs/cgroup/cpu,cpuacct/system.slice/[...]/cpu.stat -nr_periods 42227334 -nr_throttled 131923 -throttled_time 88613212216618 -``` - -Once `GOMAXPROCS` was reduced to match the CPU quota, we saw no CPU throttling. - -## Development Status: Stable - -All APIs are finalized, and no breaking changes will be made in the 1.x series -of releases. Users of semver-aware dependency management systems should pin -automaxprocs to `^1`. - -## Contributing - -We encourage and support an active, healthy community of contributors — -including you! Details are in the [contribution guide](CONTRIBUTING.md) and -the [code of conduct](CODE_OF_CONDUCT.md). The automaxprocs maintainers keep -an eye on issues and pull requests, but you can also report any negative -conduct to oss-conduct@uber.com. That email list is a private, safe space; -even the automaxprocs maintainers don't have access, so don't hesitate to hold -us to a high standard. - -
- -Released under the [MIT License](LICENSE). - -[doc-img]: https://godoc.org/go.uber.org/automaxprocs?status.svg -[doc]: https://godoc.org/go.uber.org/automaxprocs -[ci-img]: https://github.com/uber-go/automaxprocs/actions/workflows/go.yml/badge.svg -[ci]: https://github.com/uber-go/automaxprocs/actions/workflows/go.yml -[cov-img]: https://codecov.io/gh/uber-go/automaxprocs/branch/master/graph/badge.svg -[cov]: https://codecov.io/gh/uber-go/automaxprocs - - diff --git a/vendor/go.uber.org/automaxprocs/automaxprocs.go b/vendor/go.uber.org/automaxprocs/automaxprocs.go deleted file mode 100644 index 69946a3e1fd..00000000000 --- a/vendor/go.uber.org/automaxprocs/automaxprocs.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// Package automaxprocs automatically sets GOMAXPROCS to match the Linux -// container CPU quota, if any. -package automaxprocs // import "go.uber.org/automaxprocs" - -import ( - "log" - - "go.uber.org/automaxprocs/maxprocs" -) - -func init() { - maxprocs.Set(maxprocs.Logger(log.Printf)) -} diff --git a/vendor/go.uber.org/automaxprocs/glide.yaml b/vendor/go.uber.org/automaxprocs/glide.yaml deleted file mode 100644 index d49aa7abf4d..00000000000 --- a/vendor/go.uber.org/automaxprocs/glide.yaml +++ /dev/null @@ -1,7 +0,0 @@ -package: go.uber.org/automaxprocs -import: [] -testImport: -- package: github.com/stretchr/testify - version: ^1.1.4 - subpackages: - - assert diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroup.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroup.go deleted file mode 100644 index fe4ecf561e2..00000000000 --- a/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroup.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -//go:build linux -// +build linux - -package cgroups - -import ( - "bufio" - "io" - "os" - "path/filepath" - "strconv" -) - -// CGroup represents the data structure for a Linux control group. -type CGroup struct { - path string -} - -// NewCGroup returns a new *CGroup from a given path. -func NewCGroup(path string) *CGroup { - return &CGroup{path: path} -} - -// Path returns the path of the CGroup*. -func (cg *CGroup) Path() string { - return cg.path -} - -// ParamPath returns the path of the given cgroup param under itself. -func (cg *CGroup) ParamPath(param string) string { - return filepath.Join(cg.path, param) -} - -// readFirstLine reads the first line from a cgroup param file. -func (cg *CGroup) readFirstLine(param string) (string, error) { - paramFile, err := os.Open(cg.ParamPath(param)) - if err != nil { - return "", err - } - defer paramFile.Close() - - scanner := bufio.NewScanner(paramFile) - if scanner.Scan() { - return scanner.Text(), nil - } - if err := scanner.Err(); err != nil { - return "", err - } - return "", io.ErrUnexpectedEOF -} - -// readInt parses the first line from a cgroup param file as int. -func (cg *CGroup) readInt(param string) (int, error) { - text, err := cg.readFirstLine(param) - if err != nil { - return 0, err - } - return strconv.Atoi(text) -} diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups.go deleted file mode 100644 index e89f5436028..00000000000 --- a/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -//go:build linux -// +build linux - -package cgroups - -const ( - // _cgroupFSType is the Linux CGroup file system type used in - // `/proc/$PID/mountinfo`. - _cgroupFSType = "cgroup" - // _cgroupSubsysCPU is the CPU CGroup subsystem. - _cgroupSubsysCPU = "cpu" - // _cgroupSubsysCPUAcct is the CPU accounting CGroup subsystem. - _cgroupSubsysCPUAcct = "cpuacct" - // _cgroupSubsysCPUSet is the CPUSet CGroup subsystem. - _cgroupSubsysCPUSet = "cpuset" - // _cgroupSubsysMemory is the Memory CGroup subsystem. - _cgroupSubsysMemory = "memory" - - // _cgroupCPUCFSQuotaUsParam is the file name for the CGroup CFS quota - // parameter. - _cgroupCPUCFSQuotaUsParam = "cpu.cfs_quota_us" - // _cgroupCPUCFSPeriodUsParam is the file name for the CGroup CFS period - // parameter. - _cgroupCPUCFSPeriodUsParam = "cpu.cfs_period_us" -) - -const ( - _procPathCGroup = "/proc/self/cgroup" - _procPathMountInfo = "/proc/self/mountinfo" -) - -// CGroups is a map that associates each CGroup with its subsystem name. -type CGroups map[string]*CGroup - -// NewCGroups returns a new *CGroups from given `mountinfo` and `cgroup` files -// under for some process under `/proc` file system (see also proc(5) for more -// information). -func NewCGroups(procPathMountInfo, procPathCGroup string) (CGroups, error) { - cgroupSubsystems, err := parseCGroupSubsystems(procPathCGroup) - if err != nil { - return nil, err - } - - cgroups := make(CGroups) - newMountPoint := func(mp *MountPoint) error { - if mp.FSType != _cgroupFSType { - return nil - } - - for _, opt := range mp.SuperOptions { - subsys, exists := cgroupSubsystems[opt] - if !exists { - continue - } - - cgroupPath, err := mp.Translate(subsys.Name) - if err != nil { - return err - } - cgroups[opt] = NewCGroup(cgroupPath) - } - - return nil - } - - if err := parseMountInfo(procPathMountInfo, newMountPoint); err != nil { - return nil, err - } - return cgroups, nil -} - -// NewCGroupsForCurrentProcess returns a new *CGroups instance for the current -// process. -func NewCGroupsForCurrentProcess() (CGroups, error) { - return NewCGroups(_procPathMountInfo, _procPathCGroup) -} - -// CPUQuota returns the CPU quota applied with the CPU cgroup controller. -// It is a result of `cpu.cfs_quota_us / cpu.cfs_period_us`. If the value of -// `cpu.cfs_quota_us` was not set (-1), the method returns `(-1, nil)`. -func (cg CGroups) CPUQuota() (float64, bool, error) { - cpuCGroup, exists := cg[_cgroupSubsysCPU] - if !exists { - return -1, false, nil - } - - cfsQuotaUs, err := cpuCGroup.readInt(_cgroupCPUCFSQuotaUsParam) - if defined := cfsQuotaUs > 0; err != nil || !defined { - return -1, defined, err - } - - cfsPeriodUs, err := cpuCGroup.readInt(_cgroupCPUCFSPeriodUsParam) - if defined := cfsPeriodUs > 0; err != nil || !defined { - return -1, defined, err - } - - return float64(cfsQuotaUs) / float64(cfsPeriodUs), true, nil -} diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups2.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups2.go deleted file mode 100644 index 78556062fe2..00000000000 --- a/vendor/go.uber.org/automaxprocs/internal/cgroups/cgroups2.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright (c) 2022 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -//go:build linux -// +build linux - -package cgroups - -import ( - "bufio" - "errors" - "fmt" - "io" - "os" - "path" - "strconv" - "strings" -) - -const ( - // _cgroupv2CPUMax is the file name for the CGroup-V2 CPU max and period - // parameter. - _cgroupv2CPUMax = "cpu.max" - // _cgroupFSType is the Linux CGroup-V2 file system type used in - // `/proc/$PID/mountinfo`. - _cgroupv2FSType = "cgroup2" - - _cgroupv2MountPoint = "/sys/fs/cgroup" - - _cgroupV2CPUMaxDefaultPeriod = 100000 - _cgroupV2CPUMaxQuotaMax = "max" -) - -const ( - _cgroupv2CPUMaxQuotaIndex = iota - _cgroupv2CPUMaxPeriodIndex -) - -// ErrNotV2 indicates that the system is not using cgroups2. -var ErrNotV2 = errors.New("not using cgroups2") - -// CGroups2 provides access to cgroups data for systems using cgroups2. -type CGroups2 struct { - mountPoint string - groupPath string - cpuMaxFile string -} - -// NewCGroups2ForCurrentProcess builds a CGroups2 for the current process. -// -// This returns ErrNotV2 if the system is not using cgroups2. -func NewCGroups2ForCurrentProcess() (*CGroups2, error) { - return newCGroups2From(_procPathMountInfo, _procPathCGroup) -} - -func newCGroups2From(mountInfoPath, procPathCGroup string) (*CGroups2, error) { - isV2, err := isCGroupV2(mountInfoPath) - if err != nil { - return nil, err - } - - if !isV2 { - return nil, ErrNotV2 - } - - subsystems, err := parseCGroupSubsystems(procPathCGroup) - if err != nil { - return nil, err - } - - // Find v2 subsystem by looking for the `0` id - var v2subsys *CGroupSubsys - for _, subsys := range subsystems { - if subsys.ID == 0 { - v2subsys = subsys - break - } - } - - if v2subsys == nil { - return nil, ErrNotV2 - } - - return &CGroups2{ - mountPoint: _cgroupv2MountPoint, - groupPath: v2subsys.Name, - cpuMaxFile: _cgroupv2CPUMax, - }, nil -} - -func isCGroupV2(procPathMountInfo string) (bool, error) { - var ( - isV2 bool - newMountPoint = func(mp *MountPoint) error { - isV2 = isV2 || (mp.FSType == _cgroupv2FSType && mp.MountPoint == _cgroupv2MountPoint) - return nil - } - ) - - if err := parseMountInfo(procPathMountInfo, newMountPoint); err != nil { - return false, err - } - - return isV2, nil -} - -// CPUQuota returns the CPU quota applied with the CPU cgroup2 controller. -// It is a result of reading cpu quota and period from cpu.max file. -// It will return `cpu.max / cpu.period`. If cpu.max is set to max, it returns -// (-1, false, nil) -func (cg *CGroups2) CPUQuota() (float64, bool, error) { - cpuMaxParams, err := os.Open(path.Join(cg.mountPoint, cg.groupPath, cg.cpuMaxFile)) - if err != nil { - if os.IsNotExist(err) { - return -1, false, nil - } - return -1, false, err - } - defer cpuMaxParams.Close() - - scanner := bufio.NewScanner(cpuMaxParams) - if scanner.Scan() { - fields := strings.Fields(scanner.Text()) - if len(fields) == 0 || len(fields) > 2 { - return -1, false, fmt.Errorf("invalid format") - } - - if fields[_cgroupv2CPUMaxQuotaIndex] == _cgroupV2CPUMaxQuotaMax { - return -1, false, nil - } - - max, err := strconv.Atoi(fields[_cgroupv2CPUMaxQuotaIndex]) - if err != nil { - return -1, false, err - } - - var period int - if len(fields) == 1 { - period = _cgroupV2CPUMaxDefaultPeriod - } else { - period, err = strconv.Atoi(fields[_cgroupv2CPUMaxPeriodIndex]) - if err != nil { - return -1, false, err - } - - if period == 0 { - return -1, false, errors.New("zero value for period is not allowed") - } - } - - return float64(max) / float64(period), true, nil - } - - if err := scanner.Err(); err != nil { - return -1, false, err - } - - return 0, false, io.ErrUnexpectedEOF -} diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/doc.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/doc.go deleted file mode 100644 index 113555f63da..00000000000 --- a/vendor/go.uber.org/automaxprocs/internal/cgroups/doc.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// Package cgroups provides utilities to access Linux control group (CGroups) -// parameters (CPU quota, for example) for a given process. -package cgroups diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/errors.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/errors.go deleted file mode 100644 index 94ac75a46e8..00000000000 --- a/vendor/go.uber.org/automaxprocs/internal/cgroups/errors.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -//go:build linux -// +build linux - -package cgroups - -import "fmt" - -type cgroupSubsysFormatInvalidError struct { - line string -} - -type mountPointFormatInvalidError struct { - line string -} - -type pathNotExposedFromMountPointError struct { - mountPoint string - root string - path string -} - -func (err cgroupSubsysFormatInvalidError) Error() string { - return fmt.Sprintf("invalid format for CGroupSubsys: %q", err.line) -} - -func (err mountPointFormatInvalidError) Error() string { - return fmt.Sprintf("invalid format for MountPoint: %q", err.line) -} - -func (err pathNotExposedFromMountPointError) Error() string { - return fmt.Sprintf("path %q is not a descendant of mount point root %q and cannot be exposed from %q", err.path, err.root, err.mountPoint) -} diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/mountpoint.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/mountpoint.go deleted file mode 100644 index f3877f78aa6..00000000000 --- a/vendor/go.uber.org/automaxprocs/internal/cgroups/mountpoint.go +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -//go:build linux -// +build linux - -package cgroups - -import ( - "bufio" - "os" - "path/filepath" - "strconv" - "strings" -) - -const ( - _mountInfoSep = " " - _mountInfoOptsSep = "," - _mountInfoOptionalFieldsSep = "-" -) - -const ( - _miFieldIDMountID = iota - _miFieldIDParentID - _miFieldIDDeviceID - _miFieldIDRoot - _miFieldIDMountPoint - _miFieldIDOptions - _miFieldIDOptionalFields - - _miFieldCountFirstHalf -) - -const ( - _miFieldOffsetFSType = iota - _miFieldOffsetMountSource - _miFieldOffsetSuperOptions - - _miFieldCountSecondHalf -) - -const _miFieldCountMin = _miFieldCountFirstHalf + _miFieldCountSecondHalf - -// MountPoint is the data structure for the mount points in -// `/proc/$PID/mountinfo`. See also proc(5) for more information. -type MountPoint struct { - MountID int - ParentID int - DeviceID string - Root string - MountPoint string - Options []string - OptionalFields []string - FSType string - MountSource string - SuperOptions []string -} - -// NewMountPointFromLine parses a line read from `/proc/$PID/mountinfo` and -// returns a new *MountPoint. -func NewMountPointFromLine(line string) (*MountPoint, error) { - fields := strings.Split(line, _mountInfoSep) - - if len(fields) < _miFieldCountMin { - return nil, mountPointFormatInvalidError{line} - } - - mountID, err := strconv.Atoi(fields[_miFieldIDMountID]) - if err != nil { - return nil, err - } - - parentID, err := strconv.Atoi(fields[_miFieldIDParentID]) - if err != nil { - return nil, err - } - - for i, field := range fields[_miFieldIDOptionalFields:] { - if field == _mountInfoOptionalFieldsSep { - // End of optional fields. - fsTypeStart := _miFieldIDOptionalFields + i + 1 - - // Now we know where the optional fields end, split the line again with a - // limit to avoid issues with spaces in super options as present on WSL. - fields = strings.SplitN(line, _mountInfoSep, fsTypeStart+_miFieldCountSecondHalf) - if len(fields) != fsTypeStart+_miFieldCountSecondHalf { - return nil, mountPointFormatInvalidError{line} - } - - miFieldIDFSType := _miFieldOffsetFSType + fsTypeStart - miFieldIDMountSource := _miFieldOffsetMountSource + fsTypeStart - miFieldIDSuperOptions := _miFieldOffsetSuperOptions + fsTypeStart - - return &MountPoint{ - MountID: mountID, - ParentID: parentID, - DeviceID: fields[_miFieldIDDeviceID], - Root: fields[_miFieldIDRoot], - MountPoint: fields[_miFieldIDMountPoint], - Options: strings.Split(fields[_miFieldIDOptions], _mountInfoOptsSep), - OptionalFields: fields[_miFieldIDOptionalFields:(fsTypeStart - 1)], - FSType: fields[miFieldIDFSType], - MountSource: fields[miFieldIDMountSource], - SuperOptions: strings.Split(fields[miFieldIDSuperOptions], _mountInfoOptsSep), - }, nil - } - } - - return nil, mountPointFormatInvalidError{line} -} - -// Translate converts an absolute path inside the *MountPoint's file system to -// the host file system path in the mount namespace the *MountPoint belongs to. -func (mp *MountPoint) Translate(absPath string) (string, error) { - relPath, err := filepath.Rel(mp.Root, absPath) - - if err != nil { - return "", err - } - if relPath == ".." || strings.HasPrefix(relPath, "../") { - return "", pathNotExposedFromMountPointError{ - mountPoint: mp.MountPoint, - root: mp.Root, - path: absPath, - } - } - - return filepath.Join(mp.MountPoint, relPath), nil -} - -// parseMountInfo parses procPathMountInfo (usually at `/proc/$PID/mountinfo`) -// and yields parsed *MountPoint into newMountPoint. -func parseMountInfo(procPathMountInfo string, newMountPoint func(*MountPoint) error) error { - mountInfoFile, err := os.Open(procPathMountInfo) - if err != nil { - return err - } - defer mountInfoFile.Close() - - scanner := bufio.NewScanner(mountInfoFile) - - for scanner.Scan() { - mountPoint, err := NewMountPointFromLine(scanner.Text()) - if err != nil { - return err - } - if err := newMountPoint(mountPoint); err != nil { - return err - } - } - - return scanner.Err() -} diff --git a/vendor/go.uber.org/automaxprocs/internal/cgroups/subsys.go b/vendor/go.uber.org/automaxprocs/internal/cgroups/subsys.go deleted file mode 100644 index cddc3eaec39..00000000000 --- a/vendor/go.uber.org/automaxprocs/internal/cgroups/subsys.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -//go:build linux -// +build linux - -package cgroups - -import ( - "bufio" - "os" - "strconv" - "strings" -) - -const ( - _cgroupSep = ":" - _cgroupSubsysSep = "," -) - -const ( - _csFieldIDID = iota - _csFieldIDSubsystems - _csFieldIDName - _csFieldCount -) - -// CGroupSubsys represents the data structure for entities in -// `/proc/$PID/cgroup`. See also proc(5) for more information. -type CGroupSubsys struct { - ID int - Subsystems []string - Name string -} - -// NewCGroupSubsysFromLine returns a new *CGroupSubsys by parsing a string in -// the format of `/proc/$PID/cgroup` -func NewCGroupSubsysFromLine(line string) (*CGroupSubsys, error) { - fields := strings.SplitN(line, _cgroupSep, _csFieldCount) - - if len(fields) != _csFieldCount { - return nil, cgroupSubsysFormatInvalidError{line} - } - - id, err := strconv.Atoi(fields[_csFieldIDID]) - if err != nil { - return nil, err - } - - cgroup := &CGroupSubsys{ - ID: id, - Subsystems: strings.Split(fields[_csFieldIDSubsystems], _cgroupSubsysSep), - Name: fields[_csFieldIDName], - } - - return cgroup, nil -} - -// parseCGroupSubsystems parses procPathCGroup (usually at `/proc/$PID/cgroup`) -// and returns a new map[string]*CGroupSubsys. -func parseCGroupSubsystems(procPathCGroup string) (map[string]*CGroupSubsys, error) { - cgroupFile, err := os.Open(procPathCGroup) - if err != nil { - return nil, err - } - defer cgroupFile.Close() - - scanner := bufio.NewScanner(cgroupFile) - subsystems := make(map[string]*CGroupSubsys) - - for scanner.Scan() { - cgroup, err := NewCGroupSubsysFromLine(scanner.Text()) - if err != nil { - return nil, err - } - for _, subsys := range cgroup.Subsystems { - subsystems[subsys] = cgroup - } - } - - if err := scanner.Err(); err != nil { - return nil, err - } - - return subsystems, nil -} diff --git a/vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_linux.go b/vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_linux.go deleted file mode 100644 index 3b974754c3e..00000000000 --- a/vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_linux.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -//go:build linux -// +build linux - -package runtime - -import ( - "errors" - "math" - - cg "go.uber.org/automaxprocs/internal/cgroups" -) - -// CPUQuotaToGOMAXPROCS converts the CPU quota applied to the calling process -// to a valid GOMAXPROCS value. -func CPUQuotaToGOMAXPROCS(minValue int) (int, CPUQuotaStatus, error) { - cgroups, err := newQueryer() - if err != nil { - return -1, CPUQuotaUndefined, err - } - - quota, defined, err := cgroups.CPUQuota() - if !defined || err != nil { - return -1, CPUQuotaUndefined, err - } - - maxProcs := int(math.Floor(quota)) - if minValue > 0 && maxProcs < minValue { - return minValue, CPUQuotaMinUsed, nil - } - return maxProcs, CPUQuotaUsed, nil -} - -type queryer interface { - CPUQuota() (float64, bool, error) -} - -var ( - _newCgroups2 = cg.NewCGroups2ForCurrentProcess - _newCgroups = cg.NewCGroupsForCurrentProcess -) - -func newQueryer() (queryer, error) { - cgroups, err := _newCgroups2() - if err == nil { - return cgroups, nil - } - if errors.Is(err, cg.ErrNotV2) { - return _newCgroups() - } - return nil, err -} diff --git a/vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_unsupported.go b/vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_unsupported.go deleted file mode 100644 index 6922554484e..00000000000 --- a/vendor/go.uber.org/automaxprocs/internal/runtime/cpu_quota_unsupported.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -//go:build !linux -// +build !linux - -package runtime - -// CPUQuotaToGOMAXPROCS converts the CPU quota applied to the calling process -// to a valid GOMAXPROCS value. This is Linux-specific and not supported in the -// current OS. -func CPUQuotaToGOMAXPROCS(_ int) (int, CPUQuotaStatus, error) { - return -1, CPUQuotaUndefined, nil -} diff --git a/vendor/go.uber.org/automaxprocs/internal/runtime/runtime.go b/vendor/go.uber.org/automaxprocs/internal/runtime/runtime.go deleted file mode 100644 index df6eacf0530..00000000000 --- a/vendor/go.uber.org/automaxprocs/internal/runtime/runtime.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package runtime - -// CPUQuotaStatus presents the status of how CPU quota is used -type CPUQuotaStatus int - -const ( - // CPUQuotaUndefined is returned when CPU quota is undefined - CPUQuotaUndefined CPUQuotaStatus = iota - // CPUQuotaUsed is returned when a valid CPU quota can be used - CPUQuotaUsed - // CPUQuotaMinUsed is returned when CPU quota is smaller than the min value - CPUQuotaMinUsed -) diff --git a/vendor/go.uber.org/automaxprocs/maxprocs/maxprocs.go b/vendor/go.uber.org/automaxprocs/maxprocs/maxprocs.go deleted file mode 100644 index 98176d64575..00000000000 --- a/vendor/go.uber.org/automaxprocs/maxprocs/maxprocs.go +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// Package maxprocs lets Go programs easily configure runtime.GOMAXPROCS to -// match the configured Linux CPU quota. Unlike the top-level automaxprocs -// package, it lets the caller configure logging and handle errors. -package maxprocs // import "go.uber.org/automaxprocs/maxprocs" - -import ( - "os" - "runtime" - - iruntime "go.uber.org/automaxprocs/internal/runtime" -) - -const _maxProcsKey = "GOMAXPROCS" - -func currentMaxProcs() int { - return runtime.GOMAXPROCS(0) -} - -type config struct { - printf func(string, ...interface{}) - procs func(int) (int, iruntime.CPUQuotaStatus, error) - minGOMAXPROCS int -} - -func (c *config) log(fmt string, args ...interface{}) { - if c.printf != nil { - c.printf(fmt, args...) - } -} - -// An Option alters the behavior of Set. -type Option interface { - apply(*config) -} - -// Logger uses the supplied printf implementation for log output. By default, -// Set doesn't log anything. -func Logger(printf func(string, ...interface{})) Option { - return optionFunc(func(cfg *config) { - cfg.printf = printf - }) -} - -// Min sets the minimum GOMAXPROCS value that will be used. -// Any value below 1 is ignored. -func Min(n int) Option { - return optionFunc(func(cfg *config) { - if n >= 1 { - cfg.minGOMAXPROCS = n - } - }) -} - -type optionFunc func(*config) - -func (of optionFunc) apply(cfg *config) { of(cfg) } - -// Set GOMAXPROCS to match the Linux container CPU quota (if any), returning -// any error encountered and an undo function. -// -// Set is a no-op on non-Linux systems and in Linux environments without a -// configured CPU quota. -func Set(opts ...Option) (func(), error) { - cfg := &config{ - procs: iruntime.CPUQuotaToGOMAXPROCS, - minGOMAXPROCS: 1, - } - for _, o := range opts { - o.apply(cfg) - } - - undoNoop := func() { - cfg.log("maxprocs: No GOMAXPROCS change to reset") - } - - // Honor the GOMAXPROCS environment variable if present. Otherwise, amend - // `runtime.GOMAXPROCS()` with the current process' CPU quota if the OS is - // Linux, and guarantee a minimum value of 1. The minimum guaranteed value - // can be overridden using `maxprocs.Min()`. - if max, exists := os.LookupEnv(_maxProcsKey); exists { - cfg.log("maxprocs: Honoring GOMAXPROCS=%q as set in environment", max) - return undoNoop, nil - } - - maxProcs, status, err := cfg.procs(cfg.minGOMAXPROCS) - if err != nil { - return undoNoop, err - } - - if status == iruntime.CPUQuotaUndefined { - cfg.log("maxprocs: Leaving GOMAXPROCS=%v: CPU quota undefined", currentMaxProcs()) - return undoNoop, nil - } - - prev := currentMaxProcs() - undo := func() { - cfg.log("maxprocs: Resetting GOMAXPROCS to %v", prev) - runtime.GOMAXPROCS(prev) - } - - switch status { - case iruntime.CPUQuotaMinUsed: - cfg.log("maxprocs: Updating GOMAXPROCS=%v: using minimum allowed GOMAXPROCS", maxProcs) - case iruntime.CPUQuotaUsed: - cfg.log("maxprocs: Updating GOMAXPROCS=%v: determined from CPU quota", maxProcs) - } - - runtime.GOMAXPROCS(maxProcs) - return undo, nil -} diff --git a/vendor/go.uber.org/automaxprocs/maxprocs/version.go b/vendor/go.uber.org/automaxprocs/maxprocs/version.go deleted file mode 100644 index 108a95535e5..00000000000 --- a/vendor/go.uber.org/automaxprocs/maxprocs/version.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package maxprocs - -// Version is the current package version. -const Version = "1.5.2" diff --git a/vendor/modules.txt b/vendor/modules.txt index a5e86e00f83..95b56a8e940 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1430,12 +1430,6 @@ go.starlark.net/syntax # go.uber.org/atomic v1.11.0 ## explicit; go 1.18 go.uber.org/atomic -# go.uber.org/automaxprocs v1.5.3 -## explicit; go 1.18 -go.uber.org/automaxprocs -go.uber.org/automaxprocs/internal/cgroups -go.uber.org/automaxprocs/internal/runtime -go.uber.org/automaxprocs/maxprocs # go.uber.org/multierr v1.11.0 ## explicit; go 1.19 go.uber.org/multierr From 8cee73c9bb2c0ce9ae8620e916e814dc2c25a5e1 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Mon, 25 Sep 2023 18:41:31 +0200 Subject: [PATCH 09/26] revert scaledjobs split Signed-off-by: Jorge Turrado --- pkg/scaling/scale_handler.go | 65 +++++++++++++++--------------------- 1 file changed, 26 insertions(+), 39 deletions(-) diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index da11ce6a7aa..b985ba9fd3f 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -764,54 +764,41 @@ func (h *scaleHandler) getScaledJobMetrics(ctx context.Context, scaledJob *kedav } var scalersMetrics []scaledjob.ScalerMetrics scalers, _ := cache.GetScalers() - - wg := sync.WaitGroup{} - ch := make(chan scaledjob.ScalerMetrics, len(scalers)) for i, s := range scalers { - wg.Add(1) - go h.calculateJobScaler(ctx, s, scaledJob, cache, i, &wg, ch) - } - wg.Wait() - close(ch) - for scalerMetric := range ch { - scalersMetrics = append(scalersMetrics, scalerMetric) - } - return scalersMetrics -} + isActive := false + scalerType := fmt.Sprintf("%T:", s) -func (*scaleHandler) calculateJobScaler(ctx context.Context, s scalers.Scaler, scaledJob *kedav1alpha1.ScaledJob, cache *cache.ScalersCache, scalerIndex int, wg *sync.WaitGroup, ch chan scaledjob.ScalerMetrics) { - defer wg.Done() + scalerLogger := log.WithValues("ScaledJob", scaledJob.Name, "Scaler", scalerType) - isActive := false - scalerType := fmt.Sprintf("%T:", s) + metricSpecs := s.GetMetricSpecForScaling(ctx) - scalerLogger := log.WithValues("ScaledJob", scaledJob.Name, "Scaler", scalerType) - - metricSpecs := s.GetMetricSpecForScaling(ctx) - - if len(metricSpecs) < 1 || metricSpecs[0].External == nil { - return - } + // skip scaler that doesn't return any metric specs (usually External scaler with incorrect metadata) + // or skip cpu/memory resource scaler + if len(metricSpecs) < 1 || metricSpecs[0].External == nil { + continue + } - metrics, isTriggerActive, _, err := cache.GetMetricsAndActivityForScaler(ctx, scalerIndex, metricSpecs[0].External.Metric.Name) - if err != nil { - scalerLogger.V(1).Info("Error getting scaler metrics and activity, but continue", "error", err) - cache.Recorder.Event(scaledJob, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) - return - } - if isTriggerActive { - isActive = true - } + metrics, isTriggerActive, _, err := cache.GetMetricsAndActivityForScaler(ctx, i, metricSpecs[0].External.Metric.Name) + if err != nil { + scalerLogger.V(1).Info("Error getting scaler metrics and activity, but continue", "error", err) + cache.Recorder.Event(scaledJob, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) + continue + } + if isTriggerActive { + isActive = true + } - queueLength, maxValue, targetAverageValue := scaledjob.CalculateQueueLengthAndMaxValue(metrics, metricSpecs, scaledJob.MaxReplicaCount()) + queueLength, maxValue, targetAverageValue := scaledjob.CalculateQueueLengthAndMaxValue(metrics, metricSpecs, scaledJob.MaxReplicaCount()) - scalerLogger.V(1).Info("Scaler Metric value", "isTriggerActive", isTriggerActive, metricSpecs[0].External.Metric.Name, queueLength, "targetAverageValue", targetAverageValue) + scalerLogger.V(1).Info("Scaler Metric value", "isTriggerActive", isTriggerActive, metricSpecs[0].External.Metric.Name, queueLength, "targetAverageValue", targetAverageValue) - ch <- scaledjob.ScalerMetrics{ - QueueLength: queueLength, - MaxValue: maxValue, - IsActive: isActive, + scalersMetrics = append(scalersMetrics, scaledjob.ScalerMetrics{ + QueueLength: queueLength, + MaxValue: maxValue, + IsActive: isActive, + }) } + return scalersMetrics } // isScaledJobActive returns whether the input ScaledJob: From 2409f4625103338bfc3c43fc890a96d687993d34 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Mon, 25 Sep 2023 18:46:19 +0200 Subject: [PATCH 10/26] update changelog Signed-off-by: Jorge Turrado --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be5f8fe0339..12dc90de36c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,7 +58,7 @@ To learn more about active deprecations, we recommend checking [GitHub Discussio Here is an overview of all new **experimental** features: -- **General**: Add support for formula based evaluation of metric values ([#2440](https://github.com/kedacore/keda/issues/2440)) +- **General**: Add support for formula based evaluation of metric values ([#2440](https://github.com/kedacore/keda/issues/2440)|[#4998](https://github.com/kedacore/keda/pull/4998)) ### Improvements - **General**: Add apiserver Prometheus metrics to KEDA Metric Server ([#4460](https://github.com/kedacore/keda/issues/4460)) From 0c621d3e48df32c967ff09bb54a4934e07008193 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Mon, 25 Sep 2023 18:48:20 +0200 Subject: [PATCH 11/26] update e2e test Signed-off-by: Jorge Turrado --- tests/internals/scaling_modifiers/scaling_modifiers_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/internals/scaling_modifiers/scaling_modifiers_test.go b/tests/internals/scaling_modifiers/scaling_modifiers_test.go index 036e6e63894..960b245d9a9 100644 --- a/tests/internals/scaling_modifiers/scaling_modifiers_test.go +++ b/tests/internals/scaling_modifiers/scaling_modifiers_test.go @@ -155,7 +155,6 @@ spec: - type: metrics-api name: metrics_api metadata: - targetValue: "2" url: "{{.MetricsServerEndpoint}}" valueLocation: 'value' method: "query" @@ -165,7 +164,6 @@ spec: name: kw_trig metadata: podSelector: pod=workload-test - value: '1' ` workloadDeploymentTemplate = ` From 5c0a9957a852bafb1d8e478021c7462aefb98165 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Mon, 25 Sep 2023 18:54:42 +0200 Subject: [PATCH 12/26] fix styles Signed-off-by: Jorge Turrado --- pkg/scalers/arangodb_scaler.go | 1 - pkg/scalers/influxdb_scaler.go | 1 - pkg/scalers/loki_scaler.go | 1 - pkg/scaling/scale_handler.go | 2 +- 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/pkg/scalers/arangodb_scaler.go b/pkg/scalers/arangodb_scaler.go index 90a3eecce6b..a9dd1f558fa 100644 --- a/pkg/scalers/arangodb_scaler.go +++ b/pkg/scalers/arangodb_scaler.go @@ -160,7 +160,6 @@ func parseArangoDBMetadata(config *ScalerConfig) (*arangoDBMetadata, error) { } else { return nil, fmt.Errorf("no queryValue given") } - } meta.activationQueryValue = 0 diff --git a/pkg/scalers/influxdb_scaler.go b/pkg/scalers/influxdb_scaler.go index 25ce21193d3..8522607cee3 100644 --- a/pkg/scalers/influxdb_scaler.go +++ b/pkg/scalers/influxdb_scaler.go @@ -136,7 +136,6 @@ func parseInfluxDBMetadata(config *ScalerConfig) (*influxDBMetadata, error) { } else { return nil, fmt.Errorf("no threshold value given") } - } unsafeSsl = false if val, ok := config.TriggerMetadata["unsafeSsl"]; ok { diff --git a/pkg/scalers/loki_scaler.go b/pkg/scalers/loki_scaler.go index 98c9912bbd2..f5e62dcd45e 100644 --- a/pkg/scalers/loki_scaler.go +++ b/pkg/scalers/loki_scaler.go @@ -115,7 +115,6 @@ func parseLokiMetadata(config *ScalerConfig) (meta *lokiMetadata, err error) { } else { return nil, fmt.Errorf("no %s given", lokiThreshold) } - } meta.activationThreshold = 0 diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index b985ba9fd3f..06526b69338 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -24,6 +24,7 @@ import ( "sync" "time" + "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -34,7 +35,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" - "github.com/go-logr/logr" kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1" "github.com/kedacore/keda/v2/pkg/common/message" "github.com/kedacore/keda/v2/pkg/eventreason" From 5627851852a64185e840ca963b917daa8178bd8d Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Mon, 25 Sep 2023 18:58:11 +0200 Subject: [PATCH 13/26] fix styles Signed-off-by: Jorge Turrado --- pkg/scalers/mysql_scaler.go | 1 - pkg/scalers/postgresql_scaler.go | 1 - pkg/scalers/predictkube_scaler.go | 1 - pkg/scaling/scale_handler.go | 1 - 4 files changed, 4 deletions(-) diff --git a/pkg/scalers/mysql_scaler.go b/pkg/scalers/mysql_scaler.go index b2c8458107f..aa5154efd3f 100644 --- a/pkg/scalers/mysql_scaler.go +++ b/pkg/scalers/mysql_scaler.go @@ -83,7 +83,6 @@ func parseMySQLMetadata(config *ScalerConfig) (*mySQLMetadata, error) { } else { return nil, fmt.Errorf("no queryValue given") } - } meta.activationQueryValue = 0 diff --git a/pkg/scalers/postgresql_scaler.go b/pkg/scalers/postgresql_scaler.go index e23aa1ad7b1..b0f2551fbef 100644 --- a/pkg/scalers/postgresql_scaler.go +++ b/pkg/scalers/postgresql_scaler.go @@ -78,7 +78,6 @@ func parsePostgreSQLMetadata(config *ScalerConfig) (*postgreSQLMetadata, error) } else { return nil, fmt.Errorf("no targetQueryValue given") } - } meta.activationTargetQueryValue = 0 diff --git a/pkg/scalers/predictkube_scaler.go b/pkg/scalers/predictkube_scaler.go index c699091bc34..336a4eed342 100644 --- a/pkg/scalers/predictkube_scaler.go +++ b/pkg/scalers/predictkube_scaler.go @@ -410,7 +410,6 @@ func parsePredictKubeMetadata(config *ScalerConfig) (result *predictKubeMetadata } else { return nil, fmt.Errorf("no threshold given") } - } meta.activationThreshold = 0 diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index 06526b69338..01df98449c4 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -638,7 +638,6 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k isScaledObjectActive = value > activationValue } } - } // if cpu/memory resource scaler has minReplicas==0 & at least one external From 09e2fd952b82d0e024e32c85c54fcee5d7fb8fc9 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Mon, 25 Sep 2023 19:40:02 +0200 Subject: [PATCH 14/26] fix test issues Signed-off-by: Jorge Turrado --- pkg/scaling/scale_handler.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index 01df98449c4..a2c6622766d 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -603,6 +603,9 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k for k, v := range result.Pairs { metricTriggerPairList[k] = v } + for k, v := range result.Records { + metricsRecord[k] = v + } } // invalidate the cache for the ScaledObject, if we hit an error in any scaler @@ -665,7 +668,13 @@ func (*scaleHandler) processScaledObjectStateScaler( logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject, ) ScaledObjectStateScalerResult { - result := ScaledObjectStateScalerResult{} + result := ScaledObjectStateScalerResult{ + IsActive: false, + IsError: false, + Metrics: []external_metrics.ExternalMetricValue{}, + Pairs: map[string]string{}, + Records: map[string]metricscache.MetricsRecord{}, + } scalerName := strings.Replace(fmt.Sprintf("%T", scaler), "*scalers.", "", 1) if scalerConfig.TriggerName != "" { From ff7c44fde95d4ea413aa1b943812b10d6d8c7961 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Mon, 25 Sep 2023 22:49:03 +0200 Subject: [PATCH 15/26] apply feedback Signed-off-by: Jorge Turrado --- pkg/scaling/scale_handler.go | 40 ++++++++++++++---------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index a2c6622766d..00fcab0d303 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -643,8 +643,8 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k } } - // if cpu/memory resource scaler has minReplicas==0 & at least one external - // trigger exists -> object can be scaled to zero + // cpu/memory scaler only can scale to zero if there is any other external metric because otherwise + // it'll never scale from 0. If all the triggers are only cpu/memory, we enforce the IsActive if len(scaledObject.Spec.Triggers) <= cpuMemCount && !isScaledObjectError { isScaledObjectActive = true } @@ -652,6 +652,7 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k } type ScaledObjectStateScalerResult struct { + // IsActive'll be overrided by formula calculation IsActive bool IsError bool Metrics []external_metrics.ExternalMetricValue @@ -689,8 +690,6 @@ func (*scaleHandler) processScaledObjectStateScaler( } for _, spec := range metricSpecs { - // if cpu/memory resource scaler has minReplicas==0 & at least one external - // trigger exists -> object can be scaled to zero if spec.External == nil { continue } @@ -713,31 +712,23 @@ func (*scaleHandler) processScaledObjectStateScaler( } } - if scaledObject.IsUsingModifiers() { - if err != nil { - result.IsError = true + if err != nil { + result.IsError = true + if scaledObject.IsUsingModifiers() { logger.Error(err, "error getting metric source", "source", scalerName) cache.Recorder.Event(scaledObject, corev1.EventTypeWarning, eventreason.KEDAMetricSourceFailed, err.Error()) } else { - for _, metric := range metrics { - metricValue := metric.Value.AsApproximateFloat64() - prommetrics.RecordScalerMetric(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metric.MetricName, metricValue) - } - } - prommetrics.RecordScalerError(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, err) - } else { - if err != nil { - result.IsError = true logger.Error(err, "error getting scale decision", "scaler", scalerName) cache.Recorder.Event(scaledObject, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) - } else { - for _, metric := range metrics { - metricValue := metric.Value.AsApproximateFloat64() - prommetrics.RecordScalerMetric(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metric.MetricName, metricValue) - } - + } + } else { + result.IsActive = true + for _, metric := range metrics { + metricValue := metric.Value.AsApproximateFloat64() + prommetrics.RecordScalerMetric(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metric.MetricName, metricValue) + } + if !scaledObject.IsUsingModifiers() { if isMetricActive { - result.IsActive = true if spec.External != nil { logger.V(1).Info("Scaler for scaledObject is active", "scaler", scalerName, "metricName", metricName) } @@ -745,9 +736,8 @@ func (*scaleHandler) processScaledObjectStateScaler( logger.V(1).Info("Scaler for scaledObject is active", "scaler", scalerName, "metricName", spec.Resource.Name) } } + prommetrics.RecordScalerActive(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, isMetricActive) } - prommetrics.RecordScalerError(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, err) - prommetrics.RecordScalerActive(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, isMetricActive) } result.Pairs, err = modifiers.AddPairTriggerAndMetric(result.Pairs, scaledObject, metricName, scalerConfig.TriggerName) From 796e739f8d95e842b1ac3396daa46d54cdfe09d4 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Mon, 25 Sep 2023 23:35:43 +0200 Subject: [PATCH 16/26] update AsSource detection Signed-off-by: Jorge Turrado --- pkg/scaling/scale_handler.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index 00fcab0d303..dbbe5be0cc4 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -321,8 +321,6 @@ func (h *scaleHandler) performGetScalersCache(ctx context.Context, key string, s } } - asMetricSource := false - if scalableObject == nil { switch scalableObjectKind { case "ScaledObject": @@ -333,7 +331,6 @@ func (h *scaleHandler) performGetScalersCache(ctx context.Context, key string, s return nil, err } scalableObject = scaledObject - asMetricSource = scaledObject.IsUsingModifiers() case "ScaledJob": scaledJob := &kedav1alpha1.ScaledJob{} err := h.client.Get(ctx, types.NamespacedName{Name: scalableObjectName, Namespace: scalableObjectNamespace}, scaledJob) @@ -359,6 +356,13 @@ func (h *scaleHandler) performGetScalersCache(ctx context.Context, key string, s return nil, err } + asMetricSource := false + switch obj := scalableObject.(type) { + case *kedav1alpha1.ScaledObject: + asMetricSource = obj.IsUsingModifiers() + default: + } + scalers, err := h.buildScalers(ctx, withTriggers, podTemplateSpec, containerName, asMetricSource) if err != nil { return nil, err @@ -626,11 +630,11 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k isScaledObjectActive = false activationValue := float64(0) if scaledObject.Spec.Advanced.ScalingModifiers.ActivationTarget != "" { - targetQueryValue, err := strconv.ParseFloat(scaledObject.Spec.Advanced.ScalingModifiers.ActivationTarget, 64) + targetValue, err := strconv.ParseFloat(scaledObject.Spec.Advanced.ScalingModifiers.ActivationTarget, 64) if err != nil { return false, true, metricsRecord, fmt.Errorf("scalingModifiers.ActivationTarget parsing error %w", err) } - activationValue = targetQueryValue + activationValue = targetValue } for _, metric := range matchingMetrics { From ac8e3d1e3d331463c5d9f0b226469bc32e6e1b78 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Tue, 26 Sep 2023 00:30:13 +0200 Subject: [PATCH 17/26] fix scaling test Signed-off-by: Jorge Turrado --- tests/internals/scaling_modifiers/scaling_modifiers_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/internals/scaling_modifiers/scaling_modifiers_test.go b/tests/internals/scaling_modifiers/scaling_modifiers_test.go index 960b245d9a9..c8abf95a7a5 100644 --- a/tests/internals/scaling_modifiers/scaling_modifiers_test.go +++ b/tests/internals/scaling_modifiers/scaling_modifiers_test.go @@ -239,6 +239,7 @@ func testFormula(t *testing.T, kc *kubernetes.Clientset, data templateData) { t.Log("--- testFormula ---") // formula simply adds 2 metrics together (0+2=2; activationTarget = 2 -> replicas should be 0) + KubectlApplyWithTemplate(t, data, "soFallbackTemplate", soFallbackTemplate) data.MetricValue = 0 KubectlApplyWithTemplate(t, data, "updateMetricsTemplate", updateMetricsTemplate) AssertReplicaCountNotChangeDuringTimePeriod(t, kc, deploymentName, namespace, 0, 60) @@ -247,7 +248,6 @@ func testFormula(t *testing.T, kc *kubernetes.Clientset, data templateData) { data.MetricValue = 3 KubectlApplyWithTemplate(t, data, "updateMetricsTemplate", updateMetricsTemplate) - KubectlApplyWithTemplate(t, data, "soFallbackTemplate", soFallbackTemplate) _, err := ExecuteCommand(fmt.Sprintf("kubectl scale deployment/depl-workload-base --replicas=2 -n %s", namespace)) assert.NoErrorf(t, err, "cannot scale workload deployment - %s", err) From cae15e66b85f891152c88d87c64994315b33c610 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Tue, 26 Sep 2023 00:54:01 +0200 Subject: [PATCH 18/26] fix error from last refactor Signed-off-by: Jorge Turrado --- pkg/scaling/scale_handler.go | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index dbbe5be0cc4..a931e3f5002 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -626,23 +626,26 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k matchingMetrics = modifiers.HandleScalingModifiers(scaledObject, matchingMetrics, metricTriggerPairList, false, cache, logger) // when we are using formula, we need to reevaluate if it's active here - if scaledObject.IsUsingModifiers() && !isScaledObjectError { + if scaledObject.IsUsingModifiers() { + // we need to reset the activity even if there is an error isScaledObjectActive = false - activationValue := float64(0) - if scaledObject.Spec.Advanced.ScalingModifiers.ActivationTarget != "" { - targetValue, err := strconv.ParseFloat(scaledObject.Spec.Advanced.ScalingModifiers.ActivationTarget, 64) - if err != nil { - return false, true, metricsRecord, fmt.Errorf("scalingModifiers.ActivationTarget parsing error %w", err) + if !isScaledObjectError { + activationValue := float64(0) + if scaledObject.Spec.Advanced.ScalingModifiers.ActivationTarget != "" { + targetValue, err := strconv.ParseFloat(scaledObject.Spec.Advanced.ScalingModifiers.ActivationTarget, 64) + if err != nil { + return false, true, metricsRecord, fmt.Errorf("scalingModifiers.ActivationTarget parsing error %w", err) + } + activationValue = targetValue } - activationValue = targetValue - } - for _, metric := range matchingMetrics { - value := metric.Value.AsApproximateFloat64() - prommetrics.RecordScalerMetric(scaledObject.Namespace, scaledObject.Name, kedav1alpha1.CompositeMetricName, 0, metric.MetricName, value) - prommetrics.RecordScalerActive(scaledObject.Namespace, scaledObject.Name, kedav1alpha1.CompositeMetricName, 0, metric.MetricName, value > activationValue) - if !isScaledObjectActive { - isScaledObjectActive = value > activationValue + for _, metric := range matchingMetrics { + value := metric.Value.AsApproximateFloat64() + prommetrics.RecordScalerMetric(scaledObject.Namespace, scaledObject.Name, kedav1alpha1.CompositeMetricName, 0, metric.MetricName, value) + prommetrics.RecordScalerActive(scaledObject.Namespace, scaledObject.Name, kedav1alpha1.CompositeMetricName, 0, metric.MetricName, value > activationValue) + if !isScaledObjectActive { + isScaledObjectActive = value > activationValue + } } } } @@ -726,7 +729,7 @@ func (*scaleHandler) processScaledObjectStateScaler( cache.Recorder.Event(scaledObject, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) } } else { - result.IsActive = true + result.IsActive = isMetricActive for _, metric := range metrics { metricValue := metric.Value.AsApproximateFloat64() prommetrics.RecordScalerMetric(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metric.MetricName, metricValue) From bdaf87d3bf1dfcfcdd6345de4661bb2a499c2716 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Tue, 26 Sep 2023 08:28:40 +0200 Subject: [PATCH 19/26] increase wait on test Signed-off-by: Jorge Turrado --- .../sequential/prometheus_metrics/prometheus_metrics_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/sequential/prometheus_metrics/prometheus_metrics_test.go b/tests/sequential/prometheus_metrics/prometheus_metrics_test.go index f86cb1e3e1a..4ff40b7041b 100644 --- a/tests/sequential/prometheus_metrics/prometheus_metrics_test.go +++ b/tests/sequential/prometheus_metrics/prometheus_metrics_test.go @@ -371,8 +371,8 @@ func testScalerErrors(t *testing.T, data templateData) { if val, ok := family["keda_scaler_errors"]; ok { errCounterVal1 := getErrorMetricsValue(val) - // wait for 10 seconds to correctly fetch metrics. - time.Sleep(10 * time.Second) + // wait for 20 seconds to correctly fetch metrics. + time.Sleep(20 * time.Second) family = fetchAndParsePrometheusMetrics(t, fmt.Sprintf("curl --insecure %s", kedaOperatorPrometheusURL)) if val, ok := family["keda_scaler_errors"]; ok { From ae8b1419bc369af2f946ea76a796b6122b46e516 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Tue, 26 Sep 2023 08:33:32 +0200 Subject: [PATCH 20/26] increase timeout on test Signed-off-by: Jorge Turrado --- apis/keda/v1alpha1/scaledobject_webhook_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apis/keda/v1alpha1/scaledobject_webhook_test.go b/apis/keda/v1alpha1/scaledobject_webhook_test.go index c20ad35d644..c02361ac432 100644 --- a/apis/keda/v1alpha1/scaledobject_webhook_test.go +++ b/apis/keda/v1alpha1/scaledobject_webhook_test.go @@ -18,6 +18,7 @@ package v1alpha1 import ( "context" + "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -416,7 +417,9 @@ var _ = It("shouldn't create so when stabilizationWindowSeconds exceeds 3600", f Eventually(func() error { return k8sClient.Create(context.Background(), so) - }).Should(HaveOccurred()) + }). + WithTimeout(5 * time.Second). + Should(HaveOccurred()) }) var _ = It("should validate the so creation with ScalingModifiers.Formula", func() { From 443de3436dbeceeb4c6a51901ff756e13ae66e1a Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Tue, 26 Sep 2023 13:17:19 +0200 Subject: [PATCH 21/26] refactor the code and add commets Signed-off-by: Jorge Turrado --- pkg/scaling/scale_handler.go | 105 +++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 43 deletions(-) diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index a931e3f5002..1e0fb361523 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -587,16 +587,19 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k // Let's collect status of all scalers, no matter if any scaler raises error or is active scalers, scalerConfigs := cache.GetScalers() for scalerIndex := 0; scalerIndex < len(scalers); scalerIndex++ { + + req := getStateScalerRequest{ + ctx: ctx, + scaler: scalers[scalerIndex], + scalerIndex: scalerIndex, + scalerConfig: scalerConfigs[scalerIndex], + cache: cache, + logger: logger, + scaledObject: scaledObject, + } + result := - h.processScaledObjectStateScaler( - ctx, - scalers[scalerIndex], - scalerIndex, - scalerConfigs[scalerIndex], - cache, - logger, - scaledObject, - ) + h.getScalerState(req) if !isScaledObjectActive { isScaledObjectActive = result.IsActive } @@ -658,8 +661,26 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k return isScaledObjectActive, isScaledObjectError, metricsRecord, err } -type ScaledObjectStateScalerResult struct { - // IsActive'll be overrided by formula calculation +// getStateScalerResult is used as paremeter +// for the function getScalerState. It contains +// all the needed information for getting the +// scaler state +type getStateScalerRequest struct { + ctx context.Context + scaler scalers.Scaler + scalerConfig scalers.ScalerConfig + scalerIndex int + cache *cache.ScalersCache + logger logr.Logger + scaledObject *kedav1alpha1.ScaledObject +} + +// getStateScalerResult is used as return +// for the funtion getScalerState. It contains +// the state of the scaler and all the required +// info for calculating the ScaledObjectState +type getStateScalerResult struct { + // IsActive will be overrided by formula calculation IsActive bool IsError bool Metrics []external_metrics.ExternalMetricValue @@ -667,16 +688,14 @@ type ScaledObjectStateScalerResult struct { Records map[string]metricscache.MetricsRecord } -func (*scaleHandler) processScaledObjectStateScaler( - ctx context.Context, - scaler scalers.Scaler, - scalerIndex int, - scalerConfig scalers.ScalerConfig, - cache *cache.ScalersCache, - logger logr.Logger, - scaledObject *kedav1alpha1.ScaledObject, -) ScaledObjectStateScalerResult { - result := ScaledObjectStateScalerResult{ +// getScalerState returns getStateScalerResult with the state +// for an specific request (getStateScalerRequest). The state +// contains if it's active or with erros, but also the records +// for the cache and he metrics for the custom formulas +func (*scaleHandler) getScalerState( + request getStateScalerRequest, +) getStateScalerResult { + result := getStateScalerResult{ IsActive: false, IsError: false, Metrics: []external_metrics.ExternalMetricValue{}, @@ -684,16 +703,16 @@ func (*scaleHandler) processScaledObjectStateScaler( Records: map[string]metricscache.MetricsRecord{}, } - scalerName := strings.Replace(fmt.Sprintf("%T", scaler), "*scalers.", "", 1) - if scalerConfig.TriggerName != "" { - scalerName = scalerConfig.TriggerName + scalerName := strings.Replace(fmt.Sprintf("%T", request.scaler), "*scalers.", "", 1) + if request.scalerConfig.TriggerName != "" { + scalerName = request.scalerConfig.TriggerName } - metricSpecs, err := cache.GetMetricSpecForScalingForScaler(ctx, scalerIndex) + metricSpecs, err := request.cache.GetMetricSpecForScalingForScaler(request.ctx, request.scalerIndex) if err != nil { result.IsError = true - logger.Error(err, "error getting metric spec for the scaler", "scaler", scalerName) - cache.Recorder.Event(scaledObject, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) + request.logger.Error(err, "error getting metric spec for the scaler", "scaler", scalerName) + request.cache.Recorder.Event(request.scaledObject, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) } for _, spec := range metricSpecs { @@ -704,14 +723,14 @@ func (*scaleHandler) processScaledObjectStateScaler( metricName := spec.External.Metric.Name var latency int64 - metrics, isMetricActive, latency, err := cache.GetMetricsAndActivityForScaler(ctx, scalerIndex, metricName) + metrics, isMetricActive, latency, err := request.cache.GetMetricsAndActivityForScaler(request.ctx, request.scalerIndex, metricName) if latency != -1 { - prommetrics.RecordScalerLatency(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, float64(latency)) + prommetrics.RecordScalerLatency(request.scaledObject.Namespace, request.scaledObject.Name, scalerName, request.scalerIndex, metricName, float64(latency)) } result.Metrics = append(result.Metrics, metrics...) - logger.V(1).Info("Getting metrics and activity from scaler", "scaler", scalerName, "metricName", metricName, "metrics", metrics, "activity", isMetricActive, "scalerError", err) + request.logger.V(1).Info("Getting metrics and activity from scaler", "scaler", scalerName, "metricName", metricName, "metrics", metrics, "activity", isMetricActive, "scalerError", err) - if scalerConfig.TriggerUseCachedMetrics { + if request.scalerConfig.TriggerUseCachedMetrics { result.Records[metricName] = metricscache.MetricsRecord{ IsActive: isMetricActive, Metric: metrics, @@ -721,35 +740,35 @@ func (*scaleHandler) processScaledObjectStateScaler( if err != nil { result.IsError = true - if scaledObject.IsUsingModifiers() { - logger.Error(err, "error getting metric source", "source", scalerName) - cache.Recorder.Event(scaledObject, corev1.EventTypeWarning, eventreason.KEDAMetricSourceFailed, err.Error()) + if request.scaledObject.IsUsingModifiers() { + request.logger.Error(err, "error getting metric source", "source", scalerName) + request.cache.Recorder.Event(request.scaledObject, corev1.EventTypeWarning, eventreason.KEDAMetricSourceFailed, err.Error()) } else { - logger.Error(err, "error getting scale decision", "scaler", scalerName) - cache.Recorder.Event(scaledObject, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) + request.logger.Error(err, "error getting scale decision", "scaler", scalerName) + request.cache.Recorder.Event(request.scaledObject, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) } } else { result.IsActive = isMetricActive for _, metric := range metrics { metricValue := metric.Value.AsApproximateFloat64() - prommetrics.RecordScalerMetric(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metric.MetricName, metricValue) + prommetrics.RecordScalerMetric(request.scaledObject.Namespace, request.scaledObject.Name, scalerName, request.scalerIndex, metric.MetricName, metricValue) } - if !scaledObject.IsUsingModifiers() { + if !request.scaledObject.IsUsingModifiers() { if isMetricActive { if spec.External != nil { - logger.V(1).Info("Scaler for scaledObject is active", "scaler", scalerName, "metricName", metricName) + request.logger.V(1).Info("Scaler for scaledObject is active", "scaler", scalerName, "metricName", metricName) } if spec.Resource != nil { - logger.V(1).Info("Scaler for scaledObject is active", "scaler", scalerName, "metricName", spec.Resource.Name) + request.logger.V(1).Info("Scaler for scaledObject is active", "scaler", scalerName, "metricName", spec.Resource.Name) } } - prommetrics.RecordScalerActive(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, isMetricActive) + prommetrics.RecordScalerActive(request.scaledObject.Namespace, request.scaledObject.Name, scalerName, request.scalerIndex, metricName, isMetricActive) } } - result.Pairs, err = modifiers.AddPairTriggerAndMetric(result.Pairs, scaledObject, metricName, scalerConfig.TriggerName) + result.Pairs, err = modifiers.AddPairTriggerAndMetric(result.Pairs, request.scaledObject, metricName, request.scalerConfig.TriggerName) if err != nil { - logger.Error(err, "error pairing triggers & metrics for compositeScaler") + request.logger.Error(err, "error pairing triggers & metrics for compositeScaler") } } return result From 7e4a7436021d90d1368f1e11a60f068923b7a81d Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Tue, 26 Sep 2023 13:20:35 +0200 Subject: [PATCH 22/26] remove whiteline Signed-off-by: Jorge Turrado --- pkg/scaling/scale_handler.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index 1e0fb361523..577a4e69147 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -587,7 +587,6 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k // Let's collect status of all scalers, no matter if any scaler raises error or is active scalers, scalerConfigs := cache.GetScalers() for scalerIndex := 0; scalerIndex < len(scalers); scalerIndex++ { - req := getStateScalerRequest{ ctx: ctx, scaler: scalers[scalerIndex], From 07ae3eec53958b93b5bcc8ab5c1407501e919d3c Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Tue, 26 Sep 2023 13:45:43 +0200 Subject: [PATCH 23/26] remove the request Signed-off-by: Jorge Turrado --- pkg/scaling/scale_handler.go | 85 ++++++++++++++---------------------- 1 file changed, 33 insertions(+), 52 deletions(-) diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index 577a4e69147..a5b99068f05 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -587,18 +587,7 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k // Let's collect status of all scalers, no matter if any scaler raises error or is active scalers, scalerConfigs := cache.GetScalers() for scalerIndex := 0; scalerIndex < len(scalers); scalerIndex++ { - req := getStateScalerRequest{ - ctx: ctx, - scaler: scalers[scalerIndex], - scalerIndex: scalerIndex, - scalerConfig: scalerConfigs[scalerIndex], - cache: cache, - logger: logger, - scaledObject: scaledObject, - } - - result := - h.getScalerState(req) + result := h.getScalerState(ctx, scalers[scalerIndex], scalerIndex, scalerConfigs[scalerIndex], cache, logger, scaledObject) if !isScaledObjectActive { isScaledObjectActive = result.IsActive } @@ -660,20 +649,6 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k return isScaledObjectActive, isScaledObjectError, metricsRecord, err } -// getStateScalerResult is used as paremeter -// for the function getScalerState. It contains -// all the needed information for getting the -// scaler state -type getStateScalerRequest struct { - ctx context.Context - scaler scalers.Scaler - scalerConfig scalers.ScalerConfig - scalerIndex int - cache *cache.ScalersCache - logger logr.Logger - scaledObject *kedav1alpha1.ScaledObject -} - // getStateScalerResult is used as return // for the funtion getScalerState. It contains // the state of the scaler and all the required @@ -688,11 +663,17 @@ type getStateScalerResult struct { } // getScalerState returns getStateScalerResult with the state -// for an specific request (getStateScalerRequest). The state -// contains if it's active or with erros, but also the records -// for the cache and he metrics for the custom formulas +// for an specific scaler. The state contains if it's active or +// with erros, but also the records for the cache and he metrics +// for the custom formulas func (*scaleHandler) getScalerState( - request getStateScalerRequest, + ctx context.Context, + scaler scalers.Scaler, + scalerIndex int, + scalerConfig scalers.ScalerConfig, + cache *cache.ScalersCache, + logger logr.Logger, + scaledObject *kedav1alpha1.ScaledObject, ) getStateScalerResult { result := getStateScalerResult{ IsActive: false, @@ -702,16 +683,16 @@ func (*scaleHandler) getScalerState( Records: map[string]metricscache.MetricsRecord{}, } - scalerName := strings.Replace(fmt.Sprintf("%T", request.scaler), "*scalers.", "", 1) - if request.scalerConfig.TriggerName != "" { - scalerName = request.scalerConfig.TriggerName + scalerName := strings.Replace(fmt.Sprintf("%T", scaler), "*scalers.", "", 1) + if scalerConfig.TriggerName != "" { + scalerName = scalerConfig.TriggerName } - metricSpecs, err := request.cache.GetMetricSpecForScalingForScaler(request.ctx, request.scalerIndex) + metricSpecs, err := cache.GetMetricSpecForScalingForScaler(ctx, scalerIndex) if err != nil { result.IsError = true - request.logger.Error(err, "error getting metric spec for the scaler", "scaler", scalerName) - request.cache.Recorder.Event(request.scaledObject, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) + logger.Error(err, "error getting metric spec for the scaler", "scaler", scalerName) + cache.Recorder.Event(scaledObject, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) } for _, spec := range metricSpecs { @@ -722,14 +703,14 @@ func (*scaleHandler) getScalerState( metricName := spec.External.Metric.Name var latency int64 - metrics, isMetricActive, latency, err := request.cache.GetMetricsAndActivityForScaler(request.ctx, request.scalerIndex, metricName) + metrics, isMetricActive, latency, err := cache.GetMetricsAndActivityForScaler(ctx, scalerIndex, metricName) if latency != -1 { - prommetrics.RecordScalerLatency(request.scaledObject.Namespace, request.scaledObject.Name, scalerName, request.scalerIndex, metricName, float64(latency)) + prommetrics.RecordScalerLatency(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, float64(latency)) } result.Metrics = append(result.Metrics, metrics...) - request.logger.V(1).Info("Getting metrics and activity from scaler", "scaler", scalerName, "metricName", metricName, "metrics", metrics, "activity", isMetricActive, "scalerError", err) + logger.V(1).Info("Getting metrics and activity from scaler", "scaler", scalerName, "metricName", metricName, "metrics", metrics, "activity", isMetricActive, "scalerError", err) - if request.scalerConfig.TriggerUseCachedMetrics { + if scalerConfig.TriggerUseCachedMetrics { result.Records[metricName] = metricscache.MetricsRecord{ IsActive: isMetricActive, Metric: metrics, @@ -739,35 +720,35 @@ func (*scaleHandler) getScalerState( if err != nil { result.IsError = true - if request.scaledObject.IsUsingModifiers() { - request.logger.Error(err, "error getting metric source", "source", scalerName) - request.cache.Recorder.Event(request.scaledObject, corev1.EventTypeWarning, eventreason.KEDAMetricSourceFailed, err.Error()) + if scaledObject.IsUsingModifiers() { + logger.Error(err, "error getting metric source", "source", scalerName) + cache.Recorder.Event(scaledObject, corev1.EventTypeWarning, eventreason.KEDAMetricSourceFailed, err.Error()) } else { - request.logger.Error(err, "error getting scale decision", "scaler", scalerName) - request.cache.Recorder.Event(request.scaledObject, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) + logger.Error(err, "error getting scale decision", "scaler", scalerName) + cache.Recorder.Event(scaledObject, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) } } else { result.IsActive = isMetricActive for _, metric := range metrics { metricValue := metric.Value.AsApproximateFloat64() - prommetrics.RecordScalerMetric(request.scaledObject.Namespace, request.scaledObject.Name, scalerName, request.scalerIndex, metric.MetricName, metricValue) + prommetrics.RecordScalerMetric(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metric.MetricName, metricValue) } - if !request.scaledObject.IsUsingModifiers() { + if !scaledObject.IsUsingModifiers() { if isMetricActive { if spec.External != nil { - request.logger.V(1).Info("Scaler for scaledObject is active", "scaler", scalerName, "metricName", metricName) + logger.V(1).Info("Scaler for scaledObject is active", "scaler", scalerName, "metricName", metricName) } if spec.Resource != nil { - request.logger.V(1).Info("Scaler for scaledObject is active", "scaler", scalerName, "metricName", spec.Resource.Name) + logger.V(1).Info("Scaler for scaledObject is active", "scaler", scalerName, "metricName", spec.Resource.Name) } } - prommetrics.RecordScalerActive(request.scaledObject.Namespace, request.scaledObject.Name, scalerName, request.scalerIndex, metricName, isMetricActive) + prommetrics.RecordScalerActive(scaledObject.Namespace, scaledObject.Name, scalerName, scalerIndex, metricName, isMetricActive) } } - result.Pairs, err = modifiers.AddPairTriggerAndMetric(result.Pairs, request.scaledObject, metricName, request.scalerConfig.TriggerName) + result.Pairs, err = modifiers.AddPairTriggerAndMetric(result.Pairs, scaledObject, metricName, scalerConfig.TriggerName) if err != nil { - request.logger.Error(err, "error pairing triggers & metrics for compositeScaler") + logger.Error(err, "error pairing triggers & metrics for compositeScaler") } } return result From aad8352f49e668cc78e4fd4bfb346d46669d8d7f Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Tue, 26 Sep 2023 13:47:00 +0200 Subject: [PATCH 24/26] remove the request Signed-off-by: Jorge Turrado --- pkg/scaling/scale_handler.go | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index a5b99068f05..d643a1c9534 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -666,15 +666,8 @@ type getStateScalerResult struct { // for an specific scaler. The state contains if it's active or // with erros, but also the records for the cache and he metrics // for the custom formulas -func (*scaleHandler) getScalerState( - ctx context.Context, - scaler scalers.Scaler, - scalerIndex int, - scalerConfig scalers.ScalerConfig, - cache *cache.ScalersCache, - logger logr.Logger, - scaledObject *kedav1alpha1.ScaledObject, -) getStateScalerResult { +func (*scaleHandler) getScalerState(ctx context.Context, scaler scalers.Scaler, scalerIndex int, scalerConfig scalers.ScalerConfig, + cache *cache.ScalersCache, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject) getStateScalerResult { result := getStateScalerResult{ IsActive: false, IsError: false, From b047b4fecda8887a5f6804792b09976f7b964500 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Tue, 26 Sep 2023 13:47:27 +0200 Subject: [PATCH 25/26] remove the request Signed-off-by: Jorge Turrado --- pkg/scaling/scale_handler.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index d643a1c9534..350b34d61e1 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -649,11 +649,11 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k return isScaledObjectActive, isScaledObjectError, metricsRecord, err } -// getStateScalerResult is used as return +// scalerState is used as return // for the funtion getScalerState. It contains // the state of the scaler and all the required // info for calculating the ScaledObjectState -type getStateScalerResult struct { +type scalerState struct { // IsActive will be overrided by formula calculation IsActive bool IsError bool @@ -667,8 +667,8 @@ type getStateScalerResult struct { // with erros, but also the records for the cache and he metrics // for the custom formulas func (*scaleHandler) getScalerState(ctx context.Context, scaler scalers.Scaler, scalerIndex int, scalerConfig scalers.ScalerConfig, - cache *cache.ScalersCache, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject) getStateScalerResult { - result := getStateScalerResult{ + cache *cache.ScalersCache, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject) scalerState { + result := scalerState{ IsActive: false, IsError: false, Metrics: []external_metrics.ExternalMetricValue{}, From dd6d790eac40d445cabdd50d37bb5b8fff633ce4 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Tue, 26 Sep 2023 14:07:11 +0200 Subject: [PATCH 26/26] fix typo Signed-off-by: Jorge Turrado --- pkg/scaling/scale_handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/scaling/scale_handler.go b/pkg/scaling/scale_handler.go index 350b34d61e1..d95f3ab2c0c 100644 --- a/pkg/scaling/scale_handler.go +++ b/pkg/scaling/scale_handler.go @@ -650,7 +650,7 @@ func (h *scaleHandler) getScaledObjectState(ctx context.Context, scaledObject *k } // scalerState is used as return -// for the funtion getScalerState. It contains +// for the function getScalerState. It contains // the state of the scaler and all the required // info for calculating the ScaledObjectState type scalerState struct {