rebase: Bump sigs.k8s.io/controller-runtime from 0.15.1 to 0.16.0

Bumps [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) from 0.15.1 to 0.16.0.
- [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases)
- [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md)
- [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.15.1...v0.16.0)

---
updated-dependencies:
- dependency-name: sigs.k8s.io/controller-runtime
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot] 2023-08-28 20:44:55 +00:00 committed by mergify[bot]
parent 97d9f701ec
commit a51516501c
78 changed files with 1539 additions and 2899 deletions

7
go.mod
View File

@ -44,7 +44,7 @@ require (
k8s.io/mount-utils v0.28.0
k8s.io/pod-security-admission v0.0.0
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2
sigs.k8s.io/controller-runtime v0.15.1
sigs.k8s.io/controller-runtime v0.16.0
)
require (
@ -150,9 +150,8 @@ require (
go.opentelemetry.io/otel/sdk v1.10.0 // indirect
go.opentelemetry.io/otel/trace v1.10.0 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.24.0 // indirect
go.uber.org/zap v1.25.0 // indirect
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect
golang.org/x/oauth2 v0.8.0 // indirect
golang.org/x/sync v0.3.0 // indirect
@ -160,7 +159,7 @@ require (
golang.org/x/text v0.12.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.12.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 // indirect

10
go.sum
View File

@ -731,8 +731,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.21.5/go.mod h1:VC7JDqsqiwXukYEDjoHh9
github.com/aws/smithy-go v1.14.2 h1:MJU9hqBGbvWZdApzpvoF2WAIJDbtjK2NDJSiJP7HblQ=
github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@ -1884,8 +1884,8 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -2811,8 +2811,8 @@ rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.1.2 h1:trsWhjU5jZrx6UvFu4WzQDrN7Pga4a7Qg+zcfcj64PA=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.1.2/go.mod h1:+qG7ISXqCDVVcyO8hLn12AKVYYUjM7ftlqsqmrhMZE0=
sigs.k8s.io/controller-runtime v0.2.2/go.mod h1:9dyohw3ZtoXQuV1e766PHUn+cmrRCIcBh6XIMFNMZ+I=
sigs.k8s.io/controller-runtime v0.15.1 h1:9UvgKD4ZJGcj24vefUFgZFP3xej/3igL9BsOUTb/+4c=
sigs.k8s.io/controller-runtime v0.15.1/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk=
sigs.k8s.io/controller-runtime v0.16.0 h1:5koYaaRVBHDr0LZAJjO5dWzUjMsh6cwa7q1Mmusrdvk=
sigs.k8s.io/controller-runtime v0.16.0/go.mod h1:77DnuwA8+J7AO0njzv3wbNlMOnGuLrwFr8JPNwx3J7g=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=

View File

@ -1,19 +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: 100 # 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
# Also update COVER_IGNORE_PKGS in the Makefile.
ignore:
- /internal/gen-atomicint/
- /internal/gen-valuewrapper/

15
vendor/go.uber.org/atomic/.gitignore generated vendored
View File

@ -1,15 +0,0 @@
/bin
.DS_Store
/vendor
cover.html
cover.out
lint.log
# Binaries
*.test
# Profiling output
*.prof
# Output of fossa analyzer
/fossa

View File

@ -1,117 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.10.0] - 2022-08-11
### Added
- Add `atomic.Float32` type for atomic operations on `float32`.
- Add `CompareAndSwap` and `Swap` methods to `atomic.String`, `atomic.Error`,
and `atomic.Value`.
- Add generic `atomic.Pointer[T]` type for atomic operations on pointers of any
type. This is present only for Go 1.18 or higher, and is a drop-in for
replacement for the standard library's `sync/atomic.Pointer` type.
### Changed
- Deprecate `CAS` methods on all types in favor of corresponding
`CompareAndSwap` methods.
Thanks to @eNV25 and @icpd for their contributions to this release.
[1.10.0]: https://github.com/uber-go/atomic/compare/v1.9.0...v1.10.0
## [1.9.0] - 2021-07-15
### Added
- Add `Float64.Swap` to match int atomic operations.
- Add `atomic.Time` type for atomic operations on `time.Time` values.
[1.9.0]: https://github.com/uber-go/atomic/compare/v1.8.0...v1.9.0
## [1.8.0] - 2021-06-09
### Added
- Add `atomic.Uintptr` type for atomic operations on `uintptr` values.
- Add `atomic.UnsafePointer` type for atomic operations on `unsafe.Pointer` values.
[1.8.0]: https://github.com/uber-go/atomic/compare/v1.7.0...v1.8.0
## [1.7.0] - 2020-09-14
### Added
- Support JSON serialization and deserialization of primitive atomic types.
- Support Text marshalling and unmarshalling for string atomics.
### Changed
- Disallow incorrect comparison of atomic values in a non-atomic way.
### Removed
- Remove dependency on `golang.org/x/{lint, tools}`.
[1.7.0]: https://github.com/uber-go/atomic/compare/v1.6.0...v1.7.0
## [1.6.0] - 2020-02-24
### Changed
- Drop library dependency on `golang.org/x/{lint, tools}`.
[1.6.0]: https://github.com/uber-go/atomic/compare/v1.5.1...v1.6.0
## [1.5.1] - 2019-11-19
- Fix bug where `Bool.CAS` and `Bool.Toggle` do work correctly together
causing `CAS` to fail even though the old value matches.
[1.5.1]: https://github.com/uber-go/atomic/compare/v1.5.0...v1.5.1
## [1.5.0] - 2019-10-29
### Changed
- With Go modules, only the `go.uber.org/atomic` import path is supported now.
If you need to use the old import path, please add a `replace` directive to
your `go.mod`.
[1.5.0]: https://github.com/uber-go/atomic/compare/v1.4.0...v1.5.0
## [1.4.0] - 2019-05-01
### Added
- Add `atomic.Error` type for atomic operations on `error` values.
[1.4.0]: https://github.com/uber-go/atomic/compare/v1.3.2...v1.4.0
## [1.3.2] - 2018-05-02
### Added
- Add `atomic.Duration` type for atomic operations on `time.Duration` values.
[1.3.2]: https://github.com/uber-go/atomic/compare/v1.3.1...v1.3.2
## [1.3.1] - 2017-11-14
### Fixed
- Revert optimization for `atomic.String.Store("")` which caused data races.
[1.3.1]: https://github.com/uber-go/atomic/compare/v1.3.0...v1.3.1
## [1.3.0] - 2017-11-13
### Added
- Add `atomic.Bool.CAS` for compare-and-swap semantics on bools.
### Changed
- Optimize `atomic.String.Store("")` by avoiding an allocation.
[1.3.0]: https://github.com/uber-go/atomic/compare/v1.2.0...v1.3.0
## [1.2.0] - 2017-04-12
### Added
- Shadow `atomic.Value` from `sync/atomic`.
[1.2.0]: https://github.com/uber-go/atomic/compare/v1.1.0...v1.2.0
## [1.1.0] - 2017-03-10
### Added
- Add atomic `Float64` type.
### Changed
- Support new `go.uber.org/atomic` import path.
[1.1.0]: https://github.com/uber-go/atomic/compare/v1.0.0...v1.1.0
## [1.0.0] - 2016-07-18
- Initial release.
[1.0.0]: https://github.com/uber-go/atomic/releases/tag/v1.0.0

View File

@ -1,19 +0,0 @@
Copyright (c) 2016 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.

79
vendor/go.uber.org/atomic/Makefile generated vendored
View File

@ -1,79 +0,0 @@
# Directory to place `go install`ed binaries into.
export GOBIN ?= $(shell pwd)/bin
GOLINT = $(GOBIN)/golint
GEN_ATOMICINT = $(GOBIN)/gen-atomicint
GEN_ATOMICWRAPPER = $(GOBIN)/gen-atomicwrapper
STATICCHECK = $(GOBIN)/staticcheck
GO_FILES ?= $(shell find . '(' -path .git -o -path vendor ')' -prune -o -name '*.go' -print)
# Also update ignore section in .codecov.yml.
COVER_IGNORE_PKGS = \
go.uber.org/atomic/internal/gen-atomicint \
go.uber.org/atomic/internal/gen-atomicwrapper
.PHONY: build
build:
go build ./...
.PHONY: test
test:
go test -race ./...
.PHONY: gofmt
gofmt:
$(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX))
gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true
@[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" && cat $(FMT_LOG) && false)
$(GOLINT):
cd tools && go install golang.org/x/lint/golint
$(STATICCHECK):
cd tools && go install honnef.co/go/tools/cmd/staticcheck
$(GEN_ATOMICWRAPPER): $(wildcard ./internal/gen-atomicwrapper/*)
go build -o $@ ./internal/gen-atomicwrapper
$(GEN_ATOMICINT): $(wildcard ./internal/gen-atomicint/*)
go build -o $@ ./internal/gen-atomicint
.PHONY: golint
golint: $(GOLINT)
$(GOLINT) ./...
.PHONY: staticcheck
staticcheck: $(STATICCHECK)
$(STATICCHECK) ./...
.PHONY: lint
lint: gofmt golint staticcheck generatenodirty
# comma separated list of packages to consider for code coverage.
COVER_PKG = $(shell \
go list -find ./... | \
grep -v $(foreach pkg,$(COVER_IGNORE_PKGS),-e "^$(pkg)$$") | \
paste -sd, -)
.PHONY: cover
cover:
go test -coverprofile=cover.out -coverpkg $(COVER_PKG) -v ./...
go tool cover -html=cover.out -o cover.html
.PHONY: generate
generate: $(GEN_ATOMICINT) $(GEN_ATOMICWRAPPER)
go generate ./...
.PHONY: generatenodirty
generatenodirty:
@[ -z "$$(git status --porcelain)" ] || ( \
echo "Working tree is dirty. Commit your changes first."; \
git status; \
exit 1 )
@make generate
@status=$$(git status --porcelain); \
[ -z "$$status" ] || ( \
echo "Working tree is dirty after `make generate`:"; \
echo "$$status"; \
echo "Please ensure that the generated code is up-to-date." )

63
vendor/go.uber.org/atomic/README.md generated vendored
View File

@ -1,63 +0,0 @@
# atomic [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![Go Report Card][reportcard-img]][reportcard]
Simple wrappers for primitive types to enforce atomic access.
## Installation
```shell
$ go get -u go.uber.org/atomic@v1
```
### Legacy Import Path
As of v1.5.0, the import path `go.uber.org/atomic` is the only supported way
of using this package. If you are using Go modules, this package will fail to
compile with the legacy import path path `github.com/uber-go/atomic`.
We recommend migrating your code to the new import path but if you're unable
to do so, or if your dependencies are still using the old import path, you
will have to add a `replace` directive to your `go.mod` file downgrading the
legacy import path to an older version.
```
replace github.com/uber-go/atomic => github.com/uber-go/atomic v1.4.0
```
You can do so automatically by running the following command.
```shell
$ go mod edit -replace github.com/uber-go/atomic=github.com/uber-go/atomic@v1.4.0
```
## Usage
The standard library's `sync/atomic` is powerful, but it's easy to forget which
variables must be accessed atomically. `go.uber.org/atomic` preserves all the
functionality of the standard library, but wraps the primitive types to
provide a safer, more convenient API.
```go
var atom atomic.Uint32
atom.Store(42)
atom.Sub(2)
atom.CAS(40, 11)
```
See the [documentation][doc] for a complete API specification.
## Development Status
Stable.
---
Released under the [MIT License](LICENSE.txt).
[doc-img]: https://godoc.org/github.com/uber-go/atomic?status.svg
[doc]: https://godoc.org/go.uber.org/atomic
[ci-img]: https://github.com/uber-go/atomic/actions/workflows/go.yml/badge.svg
[ci]: https://github.com/uber-go/atomic/actions/workflows/go.yml
[cov-img]: https://codecov.io/gh/uber-go/atomic/branch/master/graph/badge.svg
[cov]: https://codecov.io/gh/uber-go/atomic
[reportcard-img]: https://goreportcard.com/badge/go.uber.org/atomic
[reportcard]: https://goreportcard.com/report/go.uber.org/atomic

88
vendor/go.uber.org/atomic/bool.go generated vendored
View File

@ -1,88 +0,0 @@
// @generated Code generated by gen-atomicwrapper.
// Copyright (c) 2020-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.
package atomic
import (
"encoding/json"
)
// Bool is an atomic type-safe wrapper for bool values.
type Bool struct {
_ nocmp // disallow non-atomic comparison
v Uint32
}
var _zeroBool bool
// NewBool creates a new Bool.
func NewBool(val bool) *Bool {
x := &Bool{}
if val != _zeroBool {
x.Store(val)
}
return x
}
// Load atomically loads the wrapped bool.
func (x *Bool) Load() bool {
return truthy(x.v.Load())
}
// Store atomically stores the passed bool.
func (x *Bool) Store(val bool) {
x.v.Store(boolToInt(val))
}
// CAS is an atomic compare-and-swap for bool values.
//
// Deprecated: Use CompareAndSwap.
func (x *Bool) CAS(old, new bool) (swapped bool) {
return x.CompareAndSwap(old, new)
}
// CompareAndSwap is an atomic compare-and-swap for bool values.
func (x *Bool) CompareAndSwap(old, new bool) (swapped bool) {
return x.v.CompareAndSwap(boolToInt(old), boolToInt(new))
}
// Swap atomically stores the given bool and returns the old
// value.
func (x *Bool) Swap(val bool) (old bool) {
return truthy(x.v.Swap(boolToInt(val)))
}
// MarshalJSON encodes the wrapped bool into JSON.
func (x *Bool) MarshalJSON() ([]byte, error) {
return json.Marshal(x.Load())
}
// UnmarshalJSON decodes a bool from JSON.
func (x *Bool) UnmarshalJSON(b []byte) error {
var v bool
if err := json.Unmarshal(b, &v); err != nil {
return err
}
x.Store(v)
return nil
}

View File

@ -1,53 +0,0 @@
// Copyright (c) 2020 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 atomic
import (
"strconv"
)
//go:generate bin/gen-atomicwrapper -name=Bool -type=bool -wrapped=Uint32 -pack=boolToInt -unpack=truthy -cas -swap -json -file=bool.go
func truthy(n uint32) bool {
return n == 1
}
func boolToInt(b bool) uint32 {
if b {
return 1
}
return 0
}
// Toggle atomically negates the Boolean and returns the previous value.
func (b *Bool) Toggle() (old bool) {
for {
old := b.Load()
if b.CAS(old, !old) {
return old
}
}
}
// String encodes the wrapped value as a string.
func (b *Bool) String() string {
return strconv.FormatBool(b.Load())
}

23
vendor/go.uber.org/atomic/doc.go generated vendored
View File

@ -1,23 +0,0 @@
// Copyright (c) 2020 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 atomic provides simple wrappers around numerics to enforce atomic
// access.
package atomic

View File

@ -1,89 +0,0 @@
// @generated Code generated by gen-atomicwrapper.
// Copyright (c) 2020-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.
package atomic
import (
"encoding/json"
"time"
)
// Duration is an atomic type-safe wrapper for time.Duration values.
type Duration struct {
_ nocmp // disallow non-atomic comparison
v Int64
}
var _zeroDuration time.Duration
// NewDuration creates a new Duration.
func NewDuration(val time.Duration) *Duration {
x := &Duration{}
if val != _zeroDuration {
x.Store(val)
}
return x
}
// Load atomically loads the wrapped time.Duration.
func (x *Duration) Load() time.Duration {
return time.Duration(x.v.Load())
}
// Store atomically stores the passed time.Duration.
func (x *Duration) Store(val time.Duration) {
x.v.Store(int64(val))
}
// CAS is an atomic compare-and-swap for time.Duration values.
//
// Deprecated: Use CompareAndSwap.
func (x *Duration) CAS(old, new time.Duration) (swapped bool) {
return x.CompareAndSwap(old, new)
}
// CompareAndSwap is an atomic compare-and-swap for time.Duration values.
func (x *Duration) CompareAndSwap(old, new time.Duration) (swapped bool) {
return x.v.CompareAndSwap(int64(old), int64(new))
}
// Swap atomically stores the given time.Duration and returns the old
// value.
func (x *Duration) Swap(val time.Duration) (old time.Duration) {
return time.Duration(x.v.Swap(int64(val)))
}
// MarshalJSON encodes the wrapped time.Duration into JSON.
func (x *Duration) MarshalJSON() ([]byte, error) {
return json.Marshal(x.Load())
}
// UnmarshalJSON decodes a time.Duration from JSON.
func (x *Duration) UnmarshalJSON(b []byte) error {
var v time.Duration
if err := json.Unmarshal(b, &v); err != nil {
return err
}
x.Store(v)
return nil
}

View File

@ -1,40 +0,0 @@
// Copyright (c) 2020 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 atomic
import "time"
//go:generate bin/gen-atomicwrapper -name=Duration -type=time.Duration -wrapped=Int64 -pack=int64 -unpack=time.Duration -cas -swap -json -imports time -file=duration.go
// Add atomically adds to the wrapped time.Duration and returns the new value.
func (d *Duration) Add(delta time.Duration) time.Duration {
return time.Duration(d.v.Add(int64(delta)))
}
// Sub atomically subtracts from the wrapped time.Duration and returns the new value.
func (d *Duration) Sub(delta time.Duration) time.Duration {
return time.Duration(d.v.Sub(int64(delta)))
}
// String encodes the wrapped value as a string.
func (d *Duration) String() string {
return d.Load().String()
}

62
vendor/go.uber.org/atomic/error.go generated vendored
View File

@ -1,62 +0,0 @@
// @generated Code generated by gen-atomicwrapper.
// Copyright (c) 2020-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.
package atomic
// Error is an atomic type-safe wrapper for error values.
type Error struct {
_ nocmp // disallow non-atomic comparison
v Value
}
var _zeroError error
// NewError creates a new Error.
func NewError(val error) *Error {
x := &Error{}
if val != _zeroError {
x.Store(val)
}
return x
}
// Load atomically loads the wrapped error.
func (x *Error) Load() error {
return unpackError(x.v.Load())
}
// Store atomically stores the passed error.
func (x *Error) Store(val error) {
x.v.Store(packError(val))
}
// CompareAndSwap is an atomic compare-and-swap for error values.
func (x *Error) CompareAndSwap(old, new error) (swapped bool) {
return x.v.CompareAndSwap(packError(old), packError(new))
}
// Swap atomically stores the given error and returns the old
// value.
func (x *Error) Swap(val error) (old error) {
return unpackError(x.v.Swap(packError(val)))
}

View File

@ -1,39 +0,0 @@
// Copyright (c) 2020-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.
package atomic
// atomic.Value panics on nil inputs, or if the underlying type changes.
// Stabilize by always storing a custom struct that we control.
//go:generate bin/gen-atomicwrapper -name=Error -type=error -wrapped=Value -pack=packError -unpack=unpackError -compareandswap -swap -file=error.go
type packedError struct{ Value error }
func packError(v error) interface{} {
return packedError{v}
}
func unpackError(v interface{}) error {
if err, ok := v.(packedError); ok {
return err.Value
}
return nil
}

77
vendor/go.uber.org/atomic/float32.go generated vendored
View File

@ -1,77 +0,0 @@
// @generated Code generated by gen-atomicwrapper.
// Copyright (c) 2020-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.
package atomic
import (
"encoding/json"
"math"
)
// Float32 is an atomic type-safe wrapper for float32 values.
type Float32 struct {
_ nocmp // disallow non-atomic comparison
v Uint32
}
var _zeroFloat32 float32
// NewFloat32 creates a new Float32.
func NewFloat32(val float32) *Float32 {
x := &Float32{}
if val != _zeroFloat32 {
x.Store(val)
}
return x
}
// Load atomically loads the wrapped float32.
func (x *Float32) Load() float32 {
return math.Float32frombits(x.v.Load())
}
// Store atomically stores the passed float32.
func (x *Float32) Store(val float32) {
x.v.Store(math.Float32bits(val))
}
// Swap atomically stores the given float32 and returns the old
// value.
func (x *Float32) Swap(val float32) (old float32) {
return math.Float32frombits(x.v.Swap(math.Float32bits(val)))
}
// MarshalJSON encodes the wrapped float32 into JSON.
func (x *Float32) MarshalJSON() ([]byte, error) {
return json.Marshal(x.Load())
}
// UnmarshalJSON decodes a float32 from JSON.
func (x *Float32) UnmarshalJSON(b []byte) error {
var v float32
if err := json.Unmarshal(b, &v); err != nil {
return err
}
x.Store(v)
return nil
}

View File

@ -1,76 +0,0 @@
// Copyright (c) 2020-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.
package atomic
import (
"math"
"strconv"
)
//go:generate bin/gen-atomicwrapper -name=Float32 -type=float32 -wrapped=Uint32 -pack=math.Float32bits -unpack=math.Float32frombits -swap -json -imports math -file=float32.go
// Add atomically adds to the wrapped float32 and returns the new value.
func (f *Float32) Add(delta float32) float32 {
for {
old := f.Load()
new := old + delta
if f.CAS(old, new) {
return new
}
}
}
// Sub atomically subtracts from the wrapped float32 and returns the new value.
func (f *Float32) Sub(delta float32) float32 {
return f.Add(-delta)
}
// CAS is an atomic compare-and-swap for float32 values.
//
// Deprecated: Use CompareAndSwap
func (f *Float32) CAS(old, new float32) (swapped bool) {
return f.CompareAndSwap(old, new)
}
// CompareAndSwap is an atomic compare-and-swap for float32 values.
//
// Note: CompareAndSwap handles NaN incorrectly. NaN != NaN using Go's inbuilt operators
// but CompareAndSwap allows a stored NaN to compare equal to a passed in NaN.
// This avoids typical CompareAndSwap loops from blocking forever, e.g.,
//
// for {
// old := atom.Load()
// new = f(old)
// if atom.CompareAndSwap(old, new) {
// break
// }
// }
//
// If CompareAndSwap did not match NaN to match, then the above would loop forever.
func (f *Float32) CompareAndSwap(old, new float32) (swapped bool) {
return f.v.CompareAndSwap(math.Float32bits(old), math.Float32bits(new))
}
// String encodes the wrapped value as a string.
func (f *Float32) String() string {
// 'g' is the behavior for floats with %v.
return strconv.FormatFloat(float64(f.Load()), 'g', -1, 32)
}

77
vendor/go.uber.org/atomic/float64.go generated vendored
View File

@ -1,77 +0,0 @@
// @generated Code generated by gen-atomicwrapper.
// Copyright (c) 2020-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.
package atomic
import (
"encoding/json"
"math"
)
// Float64 is an atomic type-safe wrapper for float64 values.
type Float64 struct {
_ nocmp // disallow non-atomic comparison
v Uint64
}
var _zeroFloat64 float64
// NewFloat64 creates a new Float64.
func NewFloat64(val float64) *Float64 {
x := &Float64{}
if val != _zeroFloat64 {
x.Store(val)
}
return x
}
// Load atomically loads the wrapped float64.
func (x *Float64) Load() float64 {
return math.Float64frombits(x.v.Load())
}
// Store atomically stores the passed float64.
func (x *Float64) Store(val float64) {
x.v.Store(math.Float64bits(val))
}
// Swap atomically stores the given float64 and returns the old
// value.
func (x *Float64) Swap(val float64) (old float64) {
return math.Float64frombits(x.v.Swap(math.Float64bits(val)))
}
// MarshalJSON encodes the wrapped float64 into JSON.
func (x *Float64) MarshalJSON() ([]byte, error) {
return json.Marshal(x.Load())
}
// UnmarshalJSON decodes a float64 from JSON.
func (x *Float64) UnmarshalJSON(b []byte) error {
var v float64
if err := json.Unmarshal(b, &v); err != nil {
return err
}
x.Store(v)
return nil
}

View File

@ -1,76 +0,0 @@
// Copyright (c) 2020-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.
package atomic
import (
"math"
"strconv"
)
//go:generate bin/gen-atomicwrapper -name=Float64 -type=float64 -wrapped=Uint64 -pack=math.Float64bits -unpack=math.Float64frombits -swap -json -imports math -file=float64.go
// Add atomically adds to the wrapped float64 and returns the new value.
func (f *Float64) Add(delta float64) float64 {
for {
old := f.Load()
new := old + delta
if f.CAS(old, new) {
return new
}
}
}
// Sub atomically subtracts from the wrapped float64 and returns the new value.
func (f *Float64) Sub(delta float64) float64 {
return f.Add(-delta)
}
// CAS is an atomic compare-and-swap for float64 values.
//
// Deprecated: Use CompareAndSwap
func (f *Float64) CAS(old, new float64) (swapped bool) {
return f.CompareAndSwap(old, new)
}
// CompareAndSwap is an atomic compare-and-swap for float64 values.
//
// Note: CompareAndSwap handles NaN incorrectly. NaN != NaN using Go's inbuilt operators
// but CompareAndSwap allows a stored NaN to compare equal to a passed in NaN.
// This avoids typical CompareAndSwap loops from blocking forever, e.g.,
//
// for {
// old := atom.Load()
// new = f(old)
// if atom.CompareAndSwap(old, new) {
// break
// }
// }
//
// If CompareAndSwap did not match NaN to match, then the above would loop forever.
func (f *Float64) CompareAndSwap(old, new float64) (swapped bool) {
return f.v.CompareAndSwap(math.Float64bits(old), math.Float64bits(new))
}
// String encodes the wrapped value as a string.
func (f *Float64) String() string {
// 'g' is the behavior for floats with %v.
return strconv.FormatFloat(f.Load(), 'g', -1, 64)
}

27
vendor/go.uber.org/atomic/gen.go generated vendored
View File

@ -1,27 +0,0 @@
// Copyright (c) 2020 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 atomic
//go:generate bin/gen-atomicint -name=Int32 -wrapped=int32 -file=int32.go
//go:generate bin/gen-atomicint -name=Int64 -wrapped=int64 -file=int64.go
//go:generate bin/gen-atomicint -name=Uint32 -wrapped=uint32 -unsigned -file=uint32.go
//go:generate bin/gen-atomicint -name=Uint64 -wrapped=uint64 -unsigned -file=uint64.go
//go:generate bin/gen-atomicint -name=Uintptr -wrapped=uintptr -unsigned -file=uintptr.go

109
vendor/go.uber.org/atomic/int32.go generated vendored
View File

@ -1,109 +0,0 @@
// @generated Code generated by gen-atomicint.
// Copyright (c) 2020-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.
package atomic
import (
"encoding/json"
"strconv"
"sync/atomic"
)
// Int32 is an atomic wrapper around int32.
type Int32 struct {
_ nocmp // disallow non-atomic comparison
v int32
}
// NewInt32 creates a new Int32.
func NewInt32(val int32) *Int32 {
return &Int32{v: val}
}
// Load atomically loads the wrapped value.
func (i *Int32) Load() int32 {
return atomic.LoadInt32(&i.v)
}
// Add atomically adds to the wrapped int32 and returns the new value.
func (i *Int32) Add(delta int32) int32 {
return atomic.AddInt32(&i.v, delta)
}
// Sub atomically subtracts from the wrapped int32 and returns the new value.
func (i *Int32) Sub(delta int32) int32 {
return atomic.AddInt32(&i.v, -delta)
}
// Inc atomically increments the wrapped int32 and returns the new value.
func (i *Int32) Inc() int32 {
return i.Add(1)
}
// Dec atomically decrements the wrapped int32 and returns the new value.
func (i *Int32) Dec() int32 {
return i.Sub(1)
}
// CAS is an atomic compare-and-swap.
//
// Deprecated: Use CompareAndSwap.
func (i *Int32) CAS(old, new int32) (swapped bool) {
return i.CompareAndSwap(old, new)
}
// CompareAndSwap is an atomic compare-and-swap.
func (i *Int32) CompareAndSwap(old, new int32) (swapped bool) {
return atomic.CompareAndSwapInt32(&i.v, old, new)
}
// Store atomically stores the passed value.
func (i *Int32) Store(val int32) {
atomic.StoreInt32(&i.v, val)
}
// Swap atomically swaps the wrapped int32 and returns the old value.
func (i *Int32) Swap(val int32) (old int32) {
return atomic.SwapInt32(&i.v, val)
}
// MarshalJSON encodes the wrapped int32 into JSON.
func (i *Int32) MarshalJSON() ([]byte, error) {
return json.Marshal(i.Load())
}
// UnmarshalJSON decodes JSON into the wrapped int32.
func (i *Int32) UnmarshalJSON(b []byte) error {
var v int32
if err := json.Unmarshal(b, &v); err != nil {
return err
}
i.Store(v)
return nil
}
// String encodes the wrapped value as a string.
func (i *Int32) String() string {
v := i.Load()
return strconv.FormatInt(int64(v), 10)
}

109
vendor/go.uber.org/atomic/int64.go generated vendored
View File

@ -1,109 +0,0 @@
// @generated Code generated by gen-atomicint.
// Copyright (c) 2020-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.
package atomic
import (
"encoding/json"
"strconv"
"sync/atomic"
)
// Int64 is an atomic wrapper around int64.
type Int64 struct {
_ nocmp // disallow non-atomic comparison
v int64
}
// NewInt64 creates a new Int64.
func NewInt64(val int64) *Int64 {
return &Int64{v: val}
}
// Load atomically loads the wrapped value.
func (i *Int64) Load() int64 {
return atomic.LoadInt64(&i.v)
}
// Add atomically adds to the wrapped int64 and returns the new value.
func (i *Int64) Add(delta int64) int64 {
return atomic.AddInt64(&i.v, delta)
}
// Sub atomically subtracts from the wrapped int64 and returns the new value.
func (i *Int64) Sub(delta int64) int64 {
return atomic.AddInt64(&i.v, -delta)
}
// Inc atomically increments the wrapped int64 and returns the new value.
func (i *Int64) Inc() int64 {
return i.Add(1)
}
// Dec atomically decrements the wrapped int64 and returns the new value.
func (i *Int64) Dec() int64 {
return i.Sub(1)
}
// CAS is an atomic compare-and-swap.
//
// Deprecated: Use CompareAndSwap.
func (i *Int64) CAS(old, new int64) (swapped bool) {
return i.CompareAndSwap(old, new)
}
// CompareAndSwap is an atomic compare-and-swap.
func (i *Int64) CompareAndSwap(old, new int64) (swapped bool) {
return atomic.CompareAndSwapInt64(&i.v, old, new)
}
// Store atomically stores the passed value.
func (i *Int64) Store(val int64) {
atomic.StoreInt64(&i.v, val)
}
// Swap atomically swaps the wrapped int64 and returns the old value.
func (i *Int64) Swap(val int64) (old int64) {
return atomic.SwapInt64(&i.v, val)
}
// MarshalJSON encodes the wrapped int64 into JSON.
func (i *Int64) MarshalJSON() ([]byte, error) {
return json.Marshal(i.Load())
}
// UnmarshalJSON decodes JSON into the wrapped int64.
func (i *Int64) UnmarshalJSON(b []byte) error {
var v int64
if err := json.Unmarshal(b, &v); err != nil {
return err
}
i.Store(v)
return nil
}
// String encodes the wrapped value as a string.
func (i *Int64) String() string {
v := i.Load()
return strconv.FormatInt(int64(v), 10)
}

35
vendor/go.uber.org/atomic/nocmp.go generated vendored
View File

@ -1,35 +0,0 @@
// Copyright (c) 2020 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 atomic
// nocmp is an uncomparable struct. Embed this inside another struct to make
// it uncomparable.
//
// type Foo struct {
// nocmp
// // ...
// }
//
// This DOES NOT:
//
// - Disallow shallow copies of structs
// - Disallow comparison of pointers to uncomparable structs
type nocmp [0]func()

View File

@ -1,60 +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 go1.18 && !go1.19
// +build go1.18,!go1.19
package atomic
import "unsafe"
type Pointer[T any] struct {
_ nocmp // disallow non-atomic comparison
p UnsafePointer
}
// NewPointer creates a new Pointer.
func NewPointer[T any](v *T) *Pointer[T] {
var p Pointer[T]
if v != nil {
p.p.Store(unsafe.Pointer(v))
}
return &p
}
// Load atomically loads the wrapped value.
func (p *Pointer[T]) Load() *T {
return (*T)(p.p.Load())
}
// Store atomically stores the passed value.
func (p *Pointer[T]) Store(val *T) {
p.p.Store(unsafe.Pointer(val))
}
// Swap atomically swaps the wrapped pointer and returns the old value.
func (p *Pointer[T]) Swap(val *T) (old *T) {
return (*T)(p.p.Swap(unsafe.Pointer(val)))
}
// CompareAndSwap is an atomic compare-and-swap.
func (p *Pointer[T]) CompareAndSwap(old, new *T) (swapped bool) {
return p.p.CompareAndSwap(unsafe.Pointer(old), unsafe.Pointer(new))
}

View File

@ -1,61 +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 go1.19
// +build go1.19
package atomic
import "sync/atomic"
// Pointer is an atomic pointer of type *T.
type Pointer[T any] struct {
_ nocmp // disallow non-atomic comparison
p atomic.Pointer[T]
}
// NewPointer creates a new Pointer.
func NewPointer[T any](v *T) *Pointer[T] {
var p Pointer[T]
if v != nil {
p.p.Store(v)
}
return &p
}
// Load atomically loads the wrapped value.
func (p *Pointer[T]) Load() *T {
return p.p.Load()
}
// Store atomically stores the passed value.
func (p *Pointer[T]) Store(val *T) {
p.p.Store(val)
}
// Swap atomically swaps the wrapped pointer and returns the old value.
func (p *Pointer[T]) Swap(val *T) (old *T) {
return p.p.Swap(val)
}
// CompareAndSwap is an atomic compare-and-swap.
func (p *Pointer[T]) CompareAndSwap(old, new *T) (swapped bool) {
return p.p.CompareAndSwap(old, new)
}

65
vendor/go.uber.org/atomic/string.go generated vendored
View File

@ -1,65 +0,0 @@
// @generated Code generated by gen-atomicwrapper.
// Copyright (c) 2020-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.
package atomic
// String is an atomic type-safe wrapper for string values.
type String struct {
_ nocmp // disallow non-atomic comparison
v Value
}
var _zeroString string
// NewString creates a new String.
func NewString(val string) *String {
x := &String{}
if val != _zeroString {
x.Store(val)
}
return x
}
// Load atomically loads the wrapped string.
func (x *String) Load() string {
if v := x.v.Load(); v != nil {
return v.(string)
}
return _zeroString
}
// Store atomically stores the passed string.
func (x *String) Store(val string) {
x.v.Store(val)
}
// CompareAndSwap is an atomic compare-and-swap for string values.
func (x *String) CompareAndSwap(old, new string) (swapped bool) {
return x.v.CompareAndSwap(old, new)
}
// Swap atomically stores the given string and returns the old
// value.
func (x *String) Swap(val string) (old string) {
return x.v.Swap(val).(string)
}

View File

@ -1,43 +0,0 @@
// Copyright (c) 2020-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.
package atomic
//go:generate bin/gen-atomicwrapper -name=String -type=string -wrapped=Value -compareandswap -swap -file=string.go
// String returns the wrapped value.
func (s *String) String() string {
return s.Load()
}
// MarshalText encodes the wrapped string into a textual form.
//
// This makes it encodable as JSON, YAML, XML, and more.
func (s *String) MarshalText() ([]byte, error) {
return []byte(s.Load()), nil
}
// UnmarshalText decodes text and replaces the wrapped string with it.
//
// This makes it decodable from JSON, YAML, XML, and more.
func (s *String) UnmarshalText(b []byte) error {
s.Store(string(b))
return nil
}

View File

@ -1,36 +0,0 @@
// Copyright (c) 2021 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 atomic
import "time"
//go:generate bin/gen-atomicwrapper -name=Time -type=time.Time -wrapped=Value -pack=packTime -unpack=unpackTime -imports time -file=time.go
func packTime(t time.Time) interface{} {
return t
}
func unpackTime(v interface{}) time.Time {
if t, ok := v.(time.Time); ok {
return t
}
return time.Time{}
}

109
vendor/go.uber.org/atomic/uint32.go generated vendored
View File

@ -1,109 +0,0 @@
// @generated Code generated by gen-atomicint.
// Copyright (c) 2020-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.
package atomic
import (
"encoding/json"
"strconv"
"sync/atomic"
)
// Uint32 is an atomic wrapper around uint32.
type Uint32 struct {
_ nocmp // disallow non-atomic comparison
v uint32
}
// NewUint32 creates a new Uint32.
func NewUint32(val uint32) *Uint32 {
return &Uint32{v: val}
}
// Load atomically loads the wrapped value.
func (i *Uint32) Load() uint32 {
return atomic.LoadUint32(&i.v)
}
// Add atomically adds to the wrapped uint32 and returns the new value.
func (i *Uint32) Add(delta uint32) uint32 {
return atomic.AddUint32(&i.v, delta)
}
// Sub atomically subtracts from the wrapped uint32 and returns the new value.
func (i *Uint32) Sub(delta uint32) uint32 {
return atomic.AddUint32(&i.v, ^(delta - 1))
}
// Inc atomically increments the wrapped uint32 and returns the new value.
func (i *Uint32) Inc() uint32 {
return i.Add(1)
}
// Dec atomically decrements the wrapped uint32 and returns the new value.
func (i *Uint32) Dec() uint32 {
return i.Sub(1)
}
// CAS is an atomic compare-and-swap.
//
// Deprecated: Use CompareAndSwap.
func (i *Uint32) CAS(old, new uint32) (swapped bool) {
return i.CompareAndSwap(old, new)
}
// CompareAndSwap is an atomic compare-and-swap.
func (i *Uint32) CompareAndSwap(old, new uint32) (swapped bool) {
return atomic.CompareAndSwapUint32(&i.v, old, new)
}
// Store atomically stores the passed value.
func (i *Uint32) Store(val uint32) {
atomic.StoreUint32(&i.v, val)
}
// Swap atomically swaps the wrapped uint32 and returns the old value.
func (i *Uint32) Swap(val uint32) (old uint32) {
return atomic.SwapUint32(&i.v, val)
}
// MarshalJSON encodes the wrapped uint32 into JSON.
func (i *Uint32) MarshalJSON() ([]byte, error) {
return json.Marshal(i.Load())
}
// UnmarshalJSON decodes JSON into the wrapped uint32.
func (i *Uint32) UnmarshalJSON(b []byte) error {
var v uint32
if err := json.Unmarshal(b, &v); err != nil {
return err
}
i.Store(v)
return nil
}
// String encodes the wrapped value as a string.
func (i *Uint32) String() string {
v := i.Load()
return strconv.FormatUint(uint64(v), 10)
}

109
vendor/go.uber.org/atomic/uint64.go generated vendored
View File

@ -1,109 +0,0 @@
// @generated Code generated by gen-atomicint.
// Copyright (c) 2020-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.
package atomic
import (
"encoding/json"
"strconv"
"sync/atomic"
)
// Uint64 is an atomic wrapper around uint64.
type Uint64 struct {
_ nocmp // disallow non-atomic comparison
v uint64
}
// NewUint64 creates a new Uint64.
func NewUint64(val uint64) *Uint64 {
return &Uint64{v: val}
}
// Load atomically loads the wrapped value.
func (i *Uint64) Load() uint64 {
return atomic.LoadUint64(&i.v)
}
// Add atomically adds to the wrapped uint64 and returns the new value.
func (i *Uint64) Add(delta uint64) uint64 {
return atomic.AddUint64(&i.v, delta)
}
// Sub atomically subtracts from the wrapped uint64 and returns the new value.
func (i *Uint64) Sub(delta uint64) uint64 {
return atomic.AddUint64(&i.v, ^(delta - 1))
}
// Inc atomically increments the wrapped uint64 and returns the new value.
func (i *Uint64) Inc() uint64 {
return i.Add(1)
}
// Dec atomically decrements the wrapped uint64 and returns the new value.
func (i *Uint64) Dec() uint64 {
return i.Sub(1)
}
// CAS is an atomic compare-and-swap.
//
// Deprecated: Use CompareAndSwap.
func (i *Uint64) CAS(old, new uint64) (swapped bool) {
return i.CompareAndSwap(old, new)
}
// CompareAndSwap is an atomic compare-and-swap.
func (i *Uint64) CompareAndSwap(old, new uint64) (swapped bool) {
return atomic.CompareAndSwapUint64(&i.v, old, new)
}
// Store atomically stores the passed value.
func (i *Uint64) Store(val uint64) {
atomic.StoreUint64(&i.v, val)
}
// Swap atomically swaps the wrapped uint64 and returns the old value.
func (i *Uint64) Swap(val uint64) (old uint64) {
return atomic.SwapUint64(&i.v, val)
}
// MarshalJSON encodes the wrapped uint64 into JSON.
func (i *Uint64) MarshalJSON() ([]byte, error) {
return json.Marshal(i.Load())
}
// UnmarshalJSON decodes JSON into the wrapped uint64.
func (i *Uint64) UnmarshalJSON(b []byte) error {
var v uint64
if err := json.Unmarshal(b, &v); err != nil {
return err
}
i.Store(v)
return nil
}
// String encodes the wrapped value as a string.
func (i *Uint64) String() string {
v := i.Load()
return strconv.FormatUint(uint64(v), 10)
}

109
vendor/go.uber.org/atomic/uintptr.go generated vendored
View File

@ -1,109 +0,0 @@
// @generated Code generated by gen-atomicint.
// Copyright (c) 2020-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.
package atomic
import (
"encoding/json"
"strconv"
"sync/atomic"
)
// Uintptr is an atomic wrapper around uintptr.
type Uintptr struct {
_ nocmp // disallow non-atomic comparison
v uintptr
}
// NewUintptr creates a new Uintptr.
func NewUintptr(val uintptr) *Uintptr {
return &Uintptr{v: val}
}
// Load atomically loads the wrapped value.
func (i *Uintptr) Load() uintptr {
return atomic.LoadUintptr(&i.v)
}
// Add atomically adds to the wrapped uintptr and returns the new value.
func (i *Uintptr) Add(delta uintptr) uintptr {
return atomic.AddUintptr(&i.v, delta)
}
// Sub atomically subtracts from the wrapped uintptr and returns the new value.
func (i *Uintptr) Sub(delta uintptr) uintptr {
return atomic.AddUintptr(&i.v, ^(delta - 1))
}
// Inc atomically increments the wrapped uintptr and returns the new value.
func (i *Uintptr) Inc() uintptr {
return i.Add(1)
}
// Dec atomically decrements the wrapped uintptr and returns the new value.
func (i *Uintptr) Dec() uintptr {
return i.Sub(1)
}
// CAS is an atomic compare-and-swap.
//
// Deprecated: Use CompareAndSwap.
func (i *Uintptr) CAS(old, new uintptr) (swapped bool) {
return i.CompareAndSwap(old, new)
}
// CompareAndSwap is an atomic compare-and-swap.
func (i *Uintptr) CompareAndSwap(old, new uintptr) (swapped bool) {
return atomic.CompareAndSwapUintptr(&i.v, old, new)
}
// Store atomically stores the passed value.
func (i *Uintptr) Store(val uintptr) {
atomic.StoreUintptr(&i.v, val)
}
// Swap atomically swaps the wrapped uintptr and returns the old value.
func (i *Uintptr) Swap(val uintptr) (old uintptr) {
return atomic.SwapUintptr(&i.v, val)
}
// MarshalJSON encodes the wrapped uintptr into JSON.
func (i *Uintptr) MarshalJSON() ([]byte, error) {
return json.Marshal(i.Load())
}
// UnmarshalJSON decodes JSON into the wrapped uintptr.
func (i *Uintptr) UnmarshalJSON(b []byte) error {
var v uintptr
if err := json.Unmarshal(b, &v); err != nil {
return err
}
i.Store(v)
return nil
}
// String encodes the wrapped value as a string.
func (i *Uintptr) String() string {
v := i.Load()
return strconv.FormatUint(uint64(v), 10)
}

View File

@ -1,65 +0,0 @@
// Copyright (c) 2021-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.
package atomic
import (
"sync/atomic"
"unsafe"
)
// UnsafePointer is an atomic wrapper around unsafe.Pointer.
type UnsafePointer struct {
_ nocmp // disallow non-atomic comparison
v unsafe.Pointer
}
// NewUnsafePointer creates a new UnsafePointer.
func NewUnsafePointer(val unsafe.Pointer) *UnsafePointer {
return &UnsafePointer{v: val}
}
// Load atomically loads the wrapped value.
func (p *UnsafePointer) Load() unsafe.Pointer {
return atomic.LoadPointer(&p.v)
}
// Store atomically stores the passed value.
func (p *UnsafePointer) Store(val unsafe.Pointer) {
atomic.StorePointer(&p.v, val)
}
// Swap atomically swaps the wrapped unsafe.Pointer and returns the old value.
func (p *UnsafePointer) Swap(val unsafe.Pointer) (old unsafe.Pointer) {
return atomic.SwapPointer(&p.v, val)
}
// CAS is an atomic compare-and-swap.
//
// Deprecated: Use CompareAndSwap
func (p *UnsafePointer) CAS(old, new unsafe.Pointer) (swapped bool) {
return p.CompareAndSwap(old, new)
}
// CompareAndSwap is an atomic compare-and-swap.
func (p *UnsafePointer) CompareAndSwap(old, new unsafe.Pointer) (swapped bool) {
return atomic.CompareAndSwapPointer(&p.v, old, new)
}

31
vendor/go.uber.org/atomic/value.go generated vendored
View File

@ -1,31 +0,0 @@
// Copyright (c) 2020 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 atomic
import "sync/atomic"
// Value shadows the type of the same name from sync/atomic
// https://godoc.org/sync/atomic#Value
type Value struct {
_ nocmp // disallow non-atomic comparison
atomic.Value
}

21
vendor/go.uber.org/zap/CHANGELOG.md generated vendored
View File

@ -3,6 +3,27 @@ All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## 1.25.0 (1 Aug 2023)
This release contains several improvements including performance, API additions,
and two new experimental packages whose APIs are unstable and may change in the
future.
Enhancements:
* [#1246][]: Add `zap/exp/zapslog` package for integration with slog.
* [#1273][]: Add `Name` to `Logger` which returns the Logger's name if one is set.
* [#1281][]: Add `zap/exp/expfield` package which contains helper methods
`Str` and `Strs` for constructing String-like zap.Fields.
* [#1310][]: Reduce stack size on `Any`.
Thanks to @knight42, @dzakaammar, @bcspragu, and @rexywork for their contributions
to this release.
[#1246]: https://github.com/uber-go/zap/pull/1246
[#1273]: https://github.com/uber-go/zap/pull/1273
[#1281]: https://github.com/uber-go/zap/pull/1281
[#1310]: https://github.com/uber-go/zap/pull/1310
## 1.24.0 (30 Nov 2022)
Enhancements:

21
vendor/go.uber.org/zap/Makefile generated vendored
View File

@ -1,13 +1,14 @@
export GOBIN ?= $(shell pwd)/bin
GOLINT = $(GOBIN)/golint
REVIVE = $(GOBIN)/revive
STATICCHECK = $(GOBIN)/staticcheck
GOVULNCHECK = $(GOBIN)/govulncheck
BENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem
# Directories containing independent Go modules.
#
# We track coverage only for the main module.
MODULE_DIRS = . ./benchmarks ./zapgrpc/internal/test
MODULE_DIRS = . ./exp ./benchmarks ./zapgrpc/internal/test
# Many Go tools take file globs or directories as arguments instead of packages.
GO_FILES := $(shell \
@ -18,14 +19,15 @@ GO_FILES := $(shell \
all: lint test
.PHONY: lint
lint: $(GOLINT) $(STATICCHECK)
lint: $(REVIVE) $(STATICCHECK)
@rm -rf lint.log
@echo "Checking formatting..."
@gofmt -d -s $(GO_FILES) 2>&1 | tee lint.log
@echo "Checking vet..."
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go vet ./... 2>&1) &&) true | tee -a lint.log
@echo "Checking lint..."
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && $(GOLINT) ./... 2>&1) &&) true | tee -a lint.log
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && \
$(REVIVE) -set_exit_status ./... 2>&1) &&) true | tee -a lint.log
@echo "Checking staticcheck..."
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && $(STATICCHECK) ./... 2>&1) &&) true | tee -a lint.log
@echo "Checking for unresolved FIXMEs..."
@ -40,8 +42,11 @@ lint: $(GOLINT) $(STATICCHECK)
git --no-pager diff; \
fi
$(GOLINT):
cd tools && go install golang.org/x/lint/golint
$(REVIVE):
cd tools && go install github.com/mgechev/revive
$(GOVULNCHECK):
cd tools && go install golang.org/x/vuln/cmd/govulncheck
$(STATICCHECK):
cd tools && go install honnef.co/go/tools/cmd/staticcheck
@ -71,3 +76,7 @@ updatereadme:
.PHONY: tidy
tidy:
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go mod tidy) &&) true
.PHONY: vulncheck
vulncheck: $(GOVULNCHECK)
$(GOVULNCHECK) ./...

62
vendor/go.uber.org/zap/README.md generated vendored
View File

@ -54,7 +54,7 @@ and make many small allocations. Put differently, using `encoding/json` and
Zap takes a different approach. It includes a reflection-free, zero-allocation
JSON encoder, and the base `Logger` strives to avoid serialization overhead
and allocations wherever possible. By building the high-level `SugaredLogger`
on that foundation, zap lets users _choose_ when they need to count every
on that foundation, zap lets users *choose* when they need to count every
allocation and when they'd prefer a more familiar, loosely typed API.
As measured by its own [benchmarking suite][], not only is zap more performant
@ -64,40 +64,43 @@ id="anchor-versions">[1](#footnote-versions)</sup>
Log a message and 10 fields:
| Package | Time | Time % to zap | Objects Allocated |
| :------------------ | :---------: | :-----------: | :---------------: |
| :zap: zap | 2900 ns/op | +0% | 5 allocs/op |
| :zap: zap (sugared) | 3475 ns/op | +20% | 10 allocs/op |
| zerolog | 10639 ns/op | +267% | 32 allocs/op |
| go-kit | 14434 ns/op | +398% | 59 allocs/op |
| logrus | 17104 ns/op | +490% | 81 allocs/op |
| apex/log | 32424 ns/op | +1018% | 66 allocs/op |
| log15 | 33579 ns/op | +1058% | 76 allocs/op |
| Package | Time | Time % to zap | Objects Allocated |
| :------ | :--: | :-----------: | :---------------: |
| :zap: zap | 1744 ns/op | +0% | 5 allocs/op
| :zap: zap (sugared) | 2483 ns/op | +42% | 10 allocs/op
| zerolog | 918 ns/op | -47% | 1 allocs/op
| go-kit | 5590 ns/op | +221% | 57 allocs/op
| slog | 5640 ns/op | +223% | 40 allocs/op
| apex/log | 21184 ns/op | +1115% | 63 allocs/op
| logrus | 24338 ns/op | +1296% | 79 allocs/op
| log15 | 26054 ns/op | +1394% | 74 allocs/op
Log a message with a logger that already has 10 fields of context:
| Package | Time | Time % to zap | Objects Allocated |
| :------------------ | :---------: | :-----------: | :---------------: |
| :zap: zap | 373 ns/op | +0% | 0 allocs/op |
| :zap: zap (sugared) | 452 ns/op | +21% | 1 allocs/op |
| zerolog | 288 ns/op | -23% | 0 allocs/op |
| go-kit | 11785 ns/op | +3060% | 58 allocs/op |
| logrus | 19629 ns/op | +5162% | 70 allocs/op |
| log15 | 21866 ns/op | +5762% | 72 allocs/op |
| apex/log | 30890 ns/op | +8182% | 55 allocs/op |
| Package | Time | Time % to zap | Objects Allocated |
| :------ | :--: | :-----------: | :---------------: |
| :zap: zap | 193 ns/op | +0% | 0 allocs/op
| :zap: zap (sugared) | 227 ns/op | +18% | 1 allocs/op
| zerolog | 81 ns/op | -58% | 0 allocs/op
| slog | 322 ns/op | +67% | 0 allocs/op
| go-kit | 5377 ns/op | +2686% | 56 allocs/op
| apex/log | 19518 ns/op | +10013% | 53 allocs/op
| log15 | 19812 ns/op | +10165% | 70 allocs/op
| logrus | 21997 ns/op | +11297% | 68 allocs/op
Log a static string, without any context or `printf`-style templating:
| Package | Time | Time % to zap | Objects Allocated |
| :------------------ | :--------: | :-----------: | :---------------: |
| :zap: zap | 381 ns/op | +0% | 0 allocs/op |
| :zap: zap (sugared) | 410 ns/op | +8% | 1 allocs/op |
| zerolog | 369 ns/op | -3% | 0 allocs/op |
| standard library | 385 ns/op | +1% | 2 allocs/op |
| go-kit | 606 ns/op | +59% | 11 allocs/op |
| logrus | 1730 ns/op | +354% | 25 allocs/op |
| apex/log | 1998 ns/op | +424% | 7 allocs/op |
| log15 | 4546 ns/op | +1093% | 22 allocs/op |
| Package | Time | Time % to zap | Objects Allocated |
| :------ | :--: | :-----------: | :---------------: |
| :zap: zap | 165 ns/op | +0% | 0 allocs/op
| :zap: zap (sugared) | 212 ns/op | +28% | 1 allocs/op
| zerolog | 95 ns/op | -42% | 0 allocs/op
| slog | 296 ns/op | +79% | 0 allocs/op
| go-kit | 415 ns/op | +152% | 9 allocs/op
| standard library | 422 ns/op | +156% | 2 allocs/op
| apex/log | 1601 ns/op | +870% | 5 allocs/op
| logrus | 3017 ns/op | +1728% | 23 allocs/op
| log15 | 3469 ns/op | +2002% | 20 allocs/op
## Development Status: Stable
@ -131,3 +134,4 @@ pinned in the [benchmarks/go.mod][] file. [↩](#anchor-versions)
[cov]: https://codecov.io/gh/uber-go/zap
[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks
[benchmarks/go.mod]: https://github.com/uber-go/zap/blob/master/benchmarks/go.mod

View File

@ -20,25 +20,29 @@
package buffer
import "sync"
import (
"go.uber.org/zap/internal/pool"
)
// A Pool is a type-safe wrapper around a sync.Pool.
type Pool struct {
p *sync.Pool
p *pool.Pool[*Buffer]
}
// NewPool constructs a new Pool.
func NewPool() Pool {
return Pool{p: &sync.Pool{
New: func() interface{} {
return &Buffer{bs: make([]byte, 0, _size)}
},
}}
return Pool{
p: pool.New(func() *Buffer {
return &Buffer{
bs: make([]byte, 0, _size),
}
}),
}
}
// Get retrieves a Buffer from the pool, creating one if necessary.
func (p Pool) Get() *Buffer {
buf := p.p.Get().(*Buffer)
buf := p.p.Get()
buf.Reset()
buf.pool = p
return buf

84
vendor/go.uber.org/zap/config.go generated vendored
View File

@ -95,6 +95,32 @@ type Config struct {
// NewProductionEncoderConfig returns an opinionated EncoderConfig for
// production environments.
//
// Messages encoded with this configuration will be JSON-formatted
// and will have the following keys by default:
//
// - "level": The logging level (e.g. "info", "error").
// - "ts": The current time in number of seconds since the Unix epoch.
// - "msg": The message passed to the log statement.
// - "caller": If available, a short path to the file and line number
// where the log statement was issued.
// The logger configuration determines whether this field is captured.
// - "stacktrace": If available, a stack trace from the line
// where the log statement was issued.
// The logger configuration determines whether this field is captured.
//
// By default, the following formats are used for different types:
//
// - Time is formatted as floating-point number of seconds since the Unix
// epoch.
// - Duration is formatted as floating-point number of seconds.
//
// You may change these by setting the appropriate fields in the returned
// object.
// For example, use the following to change the time encoding format:
//
// cfg := zap.NewProductionEncoderConfig()
// cfg.EncodeTime = zapcore.ISO8601TimeEncoder
func NewProductionEncoderConfig() zapcore.EncoderConfig {
return zapcore.EncoderConfig{
TimeKey: "ts",
@ -112,11 +138,22 @@ func NewProductionEncoderConfig() zapcore.EncoderConfig {
}
}
// NewProductionConfig is a reasonable production logging configuration.
// Logging is enabled at InfoLevel and above.
// NewProductionConfig builds a reasonable default production logging
// configuration.
// Logging is enabled at InfoLevel and above, and uses a JSON encoder.
// Logs are written to standard error.
// Stacktraces are included on logs of ErrorLevel and above.
// DPanicLevel logs will not panic, but will write a stacktrace.
//
// It uses a JSON encoder, writes to standard error, and enables sampling.
// Stacktraces are automatically included on logs of ErrorLevel and above.
// Sampling is enabled at 100:100 by default,
// meaning that after the first 100 log entries
// with the same level and message in the same second,
// it will log every 100th entry
// with the same level and message in the same second.
// You may disable this behavior by setting Sampling to nil.
//
// See [NewProductionEncoderConfig] for information
// on the default encoder configuration.
func NewProductionConfig() Config {
return Config{
Level: NewAtomicLevelAt(InfoLevel),
@ -134,6 +171,32 @@ func NewProductionConfig() Config {
// NewDevelopmentEncoderConfig returns an opinionated EncoderConfig for
// development environments.
//
// Messages encoded with this configuration will use Zap's console encoder
// intended to print human-readable output.
// It will print log messages with the following information:
//
// - The log level (e.g. "INFO", "ERROR").
// - The time in ISO8601 format (e.g. "2017-01-01T12:00:00Z").
// - The message passed to the log statement.
// - If available, a short path to the file and line number
// where the log statement was issued.
// The logger configuration determines whether this field is captured.
// - If available, a stacktrace from the line
// where the log statement was issued.
// The logger configuration determines whether this field is captured.
//
// By default, the following formats are used for different types:
//
// - Time is formatted in ISO8601 format (e.g. "2017-01-01T12:00:00Z").
// - Duration is formatted as a string (e.g. "1.234s").
//
// You may change these by setting the appropriate fields in the returned
// object.
// For example, use the following to change the time encoding format:
//
// cfg := zap.NewDevelopmentEncoderConfig()
// cfg.EncodeTime = zapcore.ISO8601TimeEncoder
func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {
return zapcore.EncoderConfig{
// Keys can be anything except the empty string.
@ -152,12 +215,15 @@ func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {
}
}
// NewDevelopmentConfig is a reasonable development logging configuration.
// Logging is enabled at DebugLevel and above.
// NewDevelopmentConfig builds a reasonable default development logging
// configuration.
// Logging is enabled at DebugLevel and above, and uses a console encoder.
// Logs are written to standard error.
// Stacktraces are included on logs of WarnLevel and above.
// DPanicLevel logs will panic.
//
// It enables development mode (which makes DPanicLevel logs panic), uses a
// console encoder, writes to standard error, and disables sampling.
// Stacktraces are automatically included on logs of WarnLevel and above.
// See [NewDevelopmentEncoderConfig] for information
// on the default encoder configuration.
func NewDevelopmentConfig() Config {
return Config{
Level: NewAtomicLevelAt(DebugLevel),

9
vendor/go.uber.org/zap/error.go generated vendored
View File

@ -21,14 +21,13 @@
package zap
import (
"sync"
"go.uber.org/zap/internal/pool"
"go.uber.org/zap/zapcore"
)
var _errArrayElemPool = sync.Pool{New: func() interface{} {
var _errArrayElemPool = pool.New(func() *errArrayElem {
return &errArrayElem{}
}}
})
// Error is shorthand for the common idiom NamedError("error", err).
func Error(err error) Field {
@ -60,7 +59,7 @@ func (errs errArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
// potentially an "errorVerbose" attribute, we need to wrap it in a
// type that implements LogObjectMarshaler. To prevent this from
// allocating, pool the wrapper type.
elem := _errArrayElemPool.Get().(*errArrayElem)
elem := _errArrayElemPool.Get()
elem.error = errs[i]
arr.AppendObject(elem)
elem.error = nil

169
vendor/go.uber.org/zap/field.go generated vendored
View File

@ -410,6 +410,43 @@ func Inline(val zapcore.ObjectMarshaler) Field {
}
}
// We discovered an issue where zap.Any can cause a performance degradation
// when used in new goroutines.
//
// This happens because the compiler assigns 4.8kb (one zap.Field per arm of
// switch statement) of stack space for zap.Any when it takes the form:
//
// switch v := v.(type) {
// case string:
// return String(key, v)
// case int:
// return Int(key, v)
// // ...
// default:
// return Reflect(key, v)
// }
//
// To avoid this, we use the type switch to assign a value to a single local variable
// and then call a function on it.
// The local variable is just a function reference so it doesn't allocate
// when converted to an interface{}.
//
// A fair bit of experimentation went into this.
// See also:
//
// - https://github.com/uber-go/zap/pull/1301
// - https://github.com/uber-go/zap/pull/1303
// - https://github.com/uber-go/zap/pull/1304
// - https://github.com/uber-go/zap/pull/1305
// - https://github.com/uber-go/zap/pull/1308
type anyFieldC[T any] func(string, T) Field
func (f anyFieldC[T]) Any(key string, val any) Field {
v, _ := val.(T)
// val is guaranteed to be a T, except when it's nil.
return f(key, v)
}
// Any takes a key and an arbitrary value and chooses the best way to represent
// them as a field, falling back to a reflection-based approach only if
// necessary.
@ -418,132 +455,136 @@ func Inline(val zapcore.ObjectMarshaler) Field {
// them. To minimize surprises, []byte values are treated as binary blobs, byte
// values are treated as uint8, and runes are always treated as integers.
func Any(key string, value interface{}) Field {
switch val := value.(type) {
var c interface{ Any(string, any) Field }
switch value.(type) {
case zapcore.ObjectMarshaler:
return Object(key, val)
c = anyFieldC[zapcore.ObjectMarshaler](Object)
case zapcore.ArrayMarshaler:
return Array(key, val)
c = anyFieldC[zapcore.ArrayMarshaler](Array)
case bool:
return Bool(key, val)
c = anyFieldC[bool](Bool)
case *bool:
return Boolp(key, val)
c = anyFieldC[*bool](Boolp)
case []bool:
return Bools(key, val)
c = anyFieldC[[]bool](Bools)
case complex128:
return Complex128(key, val)
c = anyFieldC[complex128](Complex128)
case *complex128:
return Complex128p(key, val)
c = anyFieldC[*complex128](Complex128p)
case []complex128:
return Complex128s(key, val)
c = anyFieldC[[]complex128](Complex128s)
case complex64:
return Complex64(key, val)
c = anyFieldC[complex64](Complex64)
case *complex64:
return Complex64p(key, val)
c = anyFieldC[*complex64](Complex64p)
case []complex64:
return Complex64s(key, val)
c = anyFieldC[[]complex64](Complex64s)
case float64:
return Float64(key, val)
c = anyFieldC[float64](Float64)
case *float64:
return Float64p(key, val)
c = anyFieldC[*float64](Float64p)
case []float64:
return Float64s(key, val)
c = anyFieldC[[]float64](Float64s)
case float32:
return Float32(key, val)
c = anyFieldC[float32](Float32)
case *float32:
return Float32p(key, val)
c = anyFieldC[*float32](Float32p)
case []float32:
return Float32s(key, val)
c = anyFieldC[[]float32](Float32s)
case int:
return Int(key, val)
c = anyFieldC[int](Int)
case *int:
return Intp(key, val)
c = anyFieldC[*int](Intp)
case []int:
return Ints(key, val)
c = anyFieldC[[]int](Ints)
case int64:
return Int64(key, val)
c = anyFieldC[int64](Int64)
case *int64:
return Int64p(key, val)
c = anyFieldC[*int64](Int64p)
case []int64:
return Int64s(key, val)
c = anyFieldC[[]int64](Int64s)
case int32:
return Int32(key, val)
c = anyFieldC[int32](Int32)
case *int32:
return Int32p(key, val)
c = anyFieldC[*int32](Int32p)
case []int32:
return Int32s(key, val)
c = anyFieldC[[]int32](Int32s)
case int16:
return Int16(key, val)
c = anyFieldC[int16](Int16)
case *int16:
return Int16p(key, val)
c = anyFieldC[*int16](Int16p)
case []int16:
return Int16s(key, val)
c = anyFieldC[[]int16](Int16s)
case int8:
return Int8(key, val)
c = anyFieldC[int8](Int8)
case *int8:
return Int8p(key, val)
c = anyFieldC[*int8](Int8p)
case []int8:
return Int8s(key, val)
c = anyFieldC[[]int8](Int8s)
case string:
return String(key, val)
c = anyFieldC[string](String)
case *string:
return Stringp(key, val)
c = anyFieldC[*string](Stringp)
case []string:
return Strings(key, val)
c = anyFieldC[[]string](Strings)
case uint:
return Uint(key, val)
c = anyFieldC[uint](Uint)
case *uint:
return Uintp(key, val)
c = anyFieldC[*uint](Uintp)
case []uint:
return Uints(key, val)
c = anyFieldC[[]uint](Uints)
case uint64:
return Uint64(key, val)
c = anyFieldC[uint64](Uint64)
case *uint64:
return Uint64p(key, val)
c = anyFieldC[*uint64](Uint64p)
case []uint64:
return Uint64s(key, val)
c = anyFieldC[[]uint64](Uint64s)
case uint32:
return Uint32(key, val)
c = anyFieldC[uint32](Uint32)
case *uint32:
return Uint32p(key, val)
c = anyFieldC[*uint32](Uint32p)
case []uint32:
return Uint32s(key, val)
c = anyFieldC[[]uint32](Uint32s)
case uint16:
return Uint16(key, val)
c = anyFieldC[uint16](Uint16)
case *uint16:
return Uint16p(key, val)
c = anyFieldC[*uint16](Uint16p)
case []uint16:
return Uint16s(key, val)
c = anyFieldC[[]uint16](Uint16s)
case uint8:
return Uint8(key, val)
c = anyFieldC[uint8](Uint8)
case *uint8:
return Uint8p(key, val)
c = anyFieldC[*uint8](Uint8p)
case []byte:
return Binary(key, val)
c = anyFieldC[[]byte](Binary)
case uintptr:
return Uintptr(key, val)
c = anyFieldC[uintptr](Uintptr)
case *uintptr:
return Uintptrp(key, val)
c = anyFieldC[*uintptr](Uintptrp)
case []uintptr:
return Uintptrs(key, val)
c = anyFieldC[[]uintptr](Uintptrs)
case time.Time:
return Time(key, val)
c = anyFieldC[time.Time](Time)
case *time.Time:
return Timep(key, val)
c = anyFieldC[*time.Time](Timep)
case []time.Time:
return Times(key, val)
c = anyFieldC[[]time.Time](Times)
case time.Duration:
return Duration(key, val)
c = anyFieldC[time.Duration](Duration)
case *time.Duration:
return Durationp(key, val)
c = anyFieldC[*time.Duration](Durationp)
case []time.Duration:
return Durations(key, val)
c = anyFieldC[[]time.Duration](Durations)
case error:
return NamedError(key, val)
c = anyFieldC[error](NamedError)
case []error:
return Errors(key, val)
c = anyFieldC[[]error](Errors)
case fmt.Stringer:
return Stringer(key, val)
c = anyFieldC[fmt.Stringer](Stringer)
default:
return Reflect(key, val)
c = anyFieldC[any](Reflect)
}
return c.Any(key, value)
}

View File

@ -18,6 +18,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// Package internal and its subpackages hold types and functionality
// that are not part of Zap's public API.
package internal
import "go.uber.org/zap/zapcore"

View File

@ -1,6 +1,4 @@
// @generated Code generated by gen-atomicwrapper.
// Copyright (c) 2020-2022 Uber Technologies, Inc.
// Copyright (c) 2023 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
@ -20,36 +18,41 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package atomic
// Package pool provides internal pool utilities.
package pool
import (
"time"
"sync"
)
// Time is an atomic type-safe wrapper for time.Time values.
type Time struct {
_ nocmp // disallow non-atomic comparison
v Value
// A Pool is a generic wrapper around [sync.Pool] to provide strongly-typed
// object pooling.
//
// Note that SA6002 (ref: https://staticcheck.io/docs/checks/#SA6002) will
// not be detected, so all internal pool use must take care to only store
// pointer types.
type Pool[T any] struct {
pool sync.Pool
}
var _zeroTime time.Time
// NewTime creates a new Time.
func NewTime(val time.Time) *Time {
x := &Time{}
if val != _zeroTime {
x.Store(val)
// New returns a new [Pool] for T, and will use fn to construct new Ts when
// the pool is empty.
func New[T any](fn func() T) *Pool[T] {
return &Pool[T]{
pool: sync.Pool{
New: func() any {
return fn()
},
},
}
return x
}
// Load atomically loads the wrapped time.Time.
func (x *Time) Load() time.Time {
return unpackTime(x.v.Load())
// Get gets a T from the pool, or creates a new one if the pool is empty.
func (p *Pool[T]) Get() T {
return p.pool.Get().(T)
}
// Store atomically stores the passed time.Time.
func (x *Time) Store(val time.Time) {
x.v.Store(packTime(val))
// Put returns x into the pool.
func (p *Pool[T]) Put(x T) {
p.pool.Put(x)
}

9
vendor/go.uber.org/zap/level.go generated vendored
View File

@ -21,7 +21,8 @@
package zap
import (
"go.uber.org/atomic"
"sync/atomic"
"go.uber.org/zap/internal"
"go.uber.org/zap/zapcore"
)
@ -76,9 +77,9 @@ var _ internal.LeveledEnabler = AtomicLevel{}
// NewAtomicLevel creates an AtomicLevel with InfoLevel and above logging
// enabled.
func NewAtomicLevel() AtomicLevel {
return AtomicLevel{
l: atomic.NewInt32(int32(InfoLevel)),
}
lvl := AtomicLevel{l: new(atomic.Int32)}
lvl.l.Store(int32(InfoLevel))
return lvl
}
// NewAtomicLevelAt is a convenience function that creates an AtomicLevel

6
vendor/go.uber.org/zap/logger.go generated vendored
View File

@ -281,6 +281,12 @@ func (log *Logger) Core() zapcore.Core {
return log.core
}
// Name returns the Logger's underlying name,
// or an empty string if the logger is unnamed.
func (log *Logger) Name() string {
return log.name
}
func (log *Logger) clone() *Logger {
copy := *log
return &copy

16
vendor/go.uber.org/zap/stacktrace.go generated vendored
View File

@ -22,19 +22,17 @@ package zap
import (
"runtime"
"sync"
"go.uber.org/zap/buffer"
"go.uber.org/zap/internal/bufferpool"
"go.uber.org/zap/internal/pool"
)
var _stacktracePool = sync.Pool{
New: func() interface{} {
return &stacktrace{
storage: make([]uintptr, 64),
}
},
}
var _stacktracePool = pool.New(func() *stacktrace {
return &stacktrace{
storage: make([]uintptr, 64),
}
})
type stacktrace struct {
pcs []uintptr // program counters; always a subslice of storage
@ -68,7 +66,7 @@ const (
//
// The caller must call Free on the returned stacktrace after using it.
func captureStacktrace(skip int, depth stacktraceDepth) *stacktrace {
stack := _stacktracePool.Get().(*stacktrace)
stack := _stacktracePool.Get()
switch depth {
case stacktraceFirst:

69
vendor/go.uber.org/zap/sugar.go generated vendored
View File

@ -122,74 +122,88 @@ func (s *SugaredLogger) Level() zapcore.Level {
return zapcore.LevelOf(s.base.core)
}
// Debug uses fmt.Sprint to construct and log a message.
// Debug logs the provided arguments at [DebugLevel].
// Spaces are added between arguments when neither is a string.
func (s *SugaredLogger) Debug(args ...interface{}) {
s.log(DebugLevel, "", args, nil)
}
// Info uses fmt.Sprint to construct and log a message.
// Info logs the provided arguments at [InfoLevel].
// Spaces are added between arguments when neither is a string.
func (s *SugaredLogger) Info(args ...interface{}) {
s.log(InfoLevel, "", args, nil)
}
// Warn uses fmt.Sprint to construct and log a message.
// Warn logs the provided arguments at [WarnLevel].
// Spaces are added between arguments when neither is a string.
func (s *SugaredLogger) Warn(args ...interface{}) {
s.log(WarnLevel, "", args, nil)
}
// Error uses fmt.Sprint to construct and log a message.
// Error logs the provided arguments at [ErrorLevel].
// Spaces are added between arguments when neither is a string.
func (s *SugaredLogger) Error(args ...interface{}) {
s.log(ErrorLevel, "", args, nil)
}
// DPanic uses fmt.Sprint to construct and log a message. In development, the
// logger then panics. (See DPanicLevel for details.)
// DPanic logs the provided arguments at [DPanicLevel].
// In development, the logger then panics. (See [DPanicLevel] for details.)
// Spaces are added between arguments when neither is a string.
func (s *SugaredLogger) DPanic(args ...interface{}) {
s.log(DPanicLevel, "", args, nil)
}
// Panic uses fmt.Sprint to construct and log a message, then panics.
// Panic constructs a message with the provided arguments and panics.
// Spaces are added between arguments when neither is a string.
func (s *SugaredLogger) Panic(args ...interface{}) {
s.log(PanicLevel, "", args, nil)
}
// Fatal uses fmt.Sprint to construct and log a message, then calls os.Exit.
// Fatal constructs a message with the provided arguments and calls os.Exit.
// Spaces are added between arguments when neither is a string.
func (s *SugaredLogger) Fatal(args ...interface{}) {
s.log(FatalLevel, "", args, nil)
}
// Debugf uses fmt.Sprintf to log a templated message.
// Debugf formats the message according to the format specifier
// and logs it at [DebugLevel].
func (s *SugaredLogger) Debugf(template string, args ...interface{}) {
s.log(DebugLevel, template, args, nil)
}
// Infof uses fmt.Sprintf to log a templated message.
// Infof formats the message according to the format specifier
// and logs it at [InfoLevel].
func (s *SugaredLogger) Infof(template string, args ...interface{}) {
s.log(InfoLevel, template, args, nil)
}
// Warnf uses fmt.Sprintf to log a templated message.
// Warnf formats the message according to the format specifier
// and logs it at [WarnLevel].
func (s *SugaredLogger) Warnf(template string, args ...interface{}) {
s.log(WarnLevel, template, args, nil)
}
// Errorf uses fmt.Sprintf to log a templated message.
// Errorf formats the message according to the format specifier
// and logs it at [ErrorLevel].
func (s *SugaredLogger) Errorf(template string, args ...interface{}) {
s.log(ErrorLevel, template, args, nil)
}
// DPanicf uses fmt.Sprintf to log a templated message. In development, the
// logger then panics. (See DPanicLevel for details.)
// DPanicf formats the message according to the format specifier
// and logs it at [DPanicLevel].
// In development, the logger then panics. (See [DPanicLevel] for details.)
func (s *SugaredLogger) DPanicf(template string, args ...interface{}) {
s.log(DPanicLevel, template, args, nil)
}
// Panicf uses fmt.Sprintf to log a templated message, then panics.
// Panicf formats the message according to the format specifier
// and panics.
func (s *SugaredLogger) Panicf(template string, args ...interface{}) {
s.log(PanicLevel, template, args, nil)
}
// Fatalf uses fmt.Sprintf to log a templated message, then calls os.Exit.
// Fatalf formats the message according to the format specifier
// and calls os.Exit.
func (s *SugaredLogger) Fatalf(template string, args ...interface{}) {
s.log(FatalLevel, template, args, nil)
}
@ -241,38 +255,45 @@ func (s *SugaredLogger) Fatalw(msg string, keysAndValues ...interface{}) {
s.log(FatalLevel, msg, nil, keysAndValues)
}
// Debugln uses fmt.Sprintln to construct and log a message.
// Debugln logs a message at [DebugLevel].
// Spaces are always added between arguments.
func (s *SugaredLogger) Debugln(args ...interface{}) {
s.logln(DebugLevel, args, nil)
}
// Infoln uses fmt.Sprintln to construct and log a message.
// Infoln logs a message at [InfoLevel].
// Spaces are always added between arguments.
func (s *SugaredLogger) Infoln(args ...interface{}) {
s.logln(InfoLevel, args, nil)
}
// Warnln uses fmt.Sprintln to construct and log a message.
// Warnln logs a message at [WarnLevel].
// Spaces are always added between arguments.
func (s *SugaredLogger) Warnln(args ...interface{}) {
s.logln(WarnLevel, args, nil)
}
// Errorln uses fmt.Sprintln to construct and log a message.
// Errorln logs a message at [ErrorLevel].
// Spaces are always added between arguments.
func (s *SugaredLogger) Errorln(args ...interface{}) {
s.logln(ErrorLevel, args, nil)
}
// DPanicln uses fmt.Sprintln to construct and log a message. In development, the
// logger then panics. (See DPanicLevel for details.)
// DPanicln logs a message at [DPanicLevel].
// In development, the logger then panics. (See [DPanicLevel] for details.)
// Spaces are always added between arguments.
func (s *SugaredLogger) DPanicln(args ...interface{}) {
s.logln(DPanicLevel, args, nil)
}
// Panicln uses fmt.Sprintln to construct and log a message, then panics.
// Panicln logs a message at [PanicLevel] and panics.
// Spaces are always added between arguments.
func (s *SugaredLogger) Panicln(args ...interface{}) {
s.logln(PanicLevel, args, nil)
}
// Fatalln uses fmt.Sprintln to construct and log a message, then calls os.Exit.
// Fatalln logs a message at [FatalLevel] and calls os.Exit.
// Spaces are always added between arguments.
func (s *SugaredLogger) Fatalln(args ...interface{}) {
s.logln(FatalLevel, args, nil)
}

View File

@ -22,20 +22,20 @@ package zapcore
import (
"fmt"
"sync"
"go.uber.org/zap/buffer"
"go.uber.org/zap/internal/bufferpool"
"go.uber.org/zap/internal/pool"
)
var _sliceEncoderPool = sync.Pool{
New: func() interface{} {
return &sliceArrayEncoder{elems: make([]interface{}, 0, 2)}
},
}
var _sliceEncoderPool = pool.New(func() *sliceArrayEncoder {
return &sliceArrayEncoder{
elems: make([]interface{}, 0, 2),
}
})
func getSliceEncoder() *sliceArrayEncoder {
return _sliceEncoderPool.Get().(*sliceArrayEncoder)
return _sliceEncoderPool.Get()
}
func putSliceEncoder(e *sliceArrayEncoder) {

View File

@ -24,25 +24,23 @@ import (
"fmt"
"runtime"
"strings"
"sync"
"time"
"go.uber.org/multierr"
"go.uber.org/zap/internal/bufferpool"
"go.uber.org/zap/internal/exit"
"go.uber.org/zap/internal/pool"
)
var (
_cePool = sync.Pool{New: func() interface{} {
// Pre-allocate some space for cores.
return &CheckedEntry{
cores: make([]Core, 4),
}
}}
)
var _cePool = pool.New(func() *CheckedEntry {
// Pre-allocate some space for cores.
return &CheckedEntry{
cores: make([]Core, 4),
}
})
func getCheckedEntry() *CheckedEntry {
ce := _cePool.Get().(*CheckedEntry)
ce := _cePool.Get()
ce.reset()
return ce
}

View File

@ -23,7 +23,8 @@ package zapcore
import (
"fmt"
"reflect"
"sync"
"go.uber.org/zap/internal/pool"
)
// Encodes the given error into fields of an object. A field with the given
@ -103,9 +104,9 @@ func (errs errArray) MarshalLogArray(arr ArrayEncoder) error {
return nil
}
var _errArrayElemPool = sync.Pool{New: func() interface{} {
var _errArrayElemPool = pool.New(func() *errArrayElem {
return &errArrayElem{}
}}
})
// Encodes any error into a {"error": ...} re-using the same errors logic.
//
@ -113,7 +114,7 @@ var _errArrayElemPool = sync.Pool{New: func() interface{} {
type errArrayElem struct{ err error }
func newErrArrayElem(err error) *errArrayElem {
e := _errArrayElemPool.Get().(*errArrayElem)
e := _errArrayElemPool.Get()
e.err = err
return e
}

View File

@ -23,24 +23,20 @@ package zapcore
import (
"encoding/base64"
"math"
"sync"
"time"
"unicode/utf8"
"go.uber.org/zap/buffer"
"go.uber.org/zap/internal/bufferpool"
"go.uber.org/zap/internal/pool"
)
// For JSON-escaping; see jsonEncoder.safeAddString below.
const _hex = "0123456789abcdef"
var _jsonPool = sync.Pool{New: func() interface{} {
var _jsonPool = pool.New(func() *jsonEncoder {
return &jsonEncoder{}
}}
func getJSONEncoder() *jsonEncoder {
return _jsonPool.Get().(*jsonEncoder)
}
})
func putJSONEncoder(enc *jsonEncoder) {
if enc.reflectBuf != nil {
@ -354,7 +350,7 @@ func (enc *jsonEncoder) Clone() Encoder {
}
func (enc *jsonEncoder) clone() *jsonEncoder {
clone := getJSONEncoder()
clone := _jsonPool.Get()
clone.EncoderConfig = enc.EncoderConfig
clone.spaced = enc.spaced
clone.openNamespaces = enc.openNamespaces
@ -527,7 +523,7 @@ func (enc *jsonEncoder) tryAddRuneSelf(b byte) bool {
if b >= utf8.RuneSelf {
return false
}
if 0x20 <= b && b != '\\' && b != '"' {
if b >= 0x20 && b != '\\' && b != '"' {
enc.buf.AppendByte(b)
return true
}

View File

@ -21,9 +21,8 @@
package zapcore
import (
"sync/atomic"
"time"
"go.uber.org/atomic"
)
const (
@ -66,16 +65,16 @@ func (c *counter) IncCheckReset(t time.Time, tick time.Duration) uint64 {
tn := t.UnixNano()
resetAfter := c.resetAt.Load()
if resetAfter > tn {
return c.counter.Inc()
return c.counter.Add(1)
}
c.counter.Store(1)
newResetAfter := tn + tick.Nanoseconds()
if !c.resetAt.CAS(resetAfter, newResetAfter) {
if !c.resetAt.CompareAndSwap(resetAfter, newResetAfter) {
// We raced with another goroutine trying to reset, and it also reset
// the counter to 1, so we need to reincrement the counter.
return c.counter.Inc()
return c.counter.Add(1)
}
return 1

View File

@ -30,10 +30,10 @@ import (
// See https://github.com/grpc/grpc-go/blob/v1.35.0/grpclog/loggerv2.go#L77-L86
const (
grpcLvlInfo = 0
grpcLvlWarn = 1
grpcLvlError = 2
grpcLvlFatal = 3
grpcLvlInfo int = iota
grpcLvlWarn
grpcLvlError
grpcLvlFatal
)
var (

94
vendor/golang.org/x/exp/maps/maps.go generated vendored Normal file
View File

@ -0,0 +1,94 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package maps defines various functions useful with maps of any type.
package maps
// Keys returns the keys of the map m.
// The keys will be in an indeterminate order.
func Keys[M ~map[K]V, K comparable, V any](m M) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}
// Values returns the values of the map m.
// The values will be in an indeterminate order.
func Values[M ~map[K]V, K comparable, V any](m M) []V {
r := make([]V, 0, len(m))
for _, v := range m {
r = append(r, v)
}
return r
}
// Equal reports whether two maps contain the same key/value pairs.
// Values are compared using ==.
func Equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool {
if len(m1) != len(m2) {
return false
}
for k, v1 := range m1 {
if v2, ok := m2[k]; !ok || v1 != v2 {
return false
}
}
return true
}
// EqualFunc is like Equal, but compares values using eq.
// Keys are still compared with ==.
func EqualFunc[M1 ~map[K]V1, M2 ~map[K]V2, K comparable, V1, V2 any](m1 M1, m2 M2, eq func(V1, V2) bool) bool {
if len(m1) != len(m2) {
return false
}
for k, v1 := range m1 {
if v2, ok := m2[k]; !ok || !eq(v1, v2) {
return false
}
}
return true
}
// Clear removes all entries from m, leaving it empty.
func Clear[M ~map[K]V, K comparable, V any](m M) {
for k := range m {
delete(m, k)
}
}
// Clone returns a copy of m. This is a shallow clone:
// the new keys and values are set using ordinary assignment.
func Clone[M ~map[K]V, K comparable, V any](m M) M {
// Preserve nil in case it matters.
if m == nil {
return nil
}
r := make(M, len(m))
for k, v := range m {
r[k] = v
}
return r
}
// Copy copies all key/value pairs in src adding them to dst.
// When a key in src is already present in dst,
// the value in dst will be overwritten by the value associated
// with the key in src.
func Copy[M1 ~map[K]V, M2 ~map[K]V, K comparable, V any](dst M1, src M2) {
for k, v := range src {
dst[k] = v
}
}
// DeleteFunc deletes any key/value pairs from m for which del returns true.
func DeleteFunc[M ~map[K]V, K comparable, V any](m M, del func(K, V) bool) {
for k, v := range m {
if del(k, v) {
delete(m, k)
}
}
}

12
vendor/modules.txt vendored
View File

@ -629,13 +629,10 @@ go.opentelemetry.io/proto/otlp/collector/trace/v1
go.opentelemetry.io/proto/otlp/common/v1
go.opentelemetry.io/proto/otlp/resource/v1
go.opentelemetry.io/proto/otlp/trace/v1
# go.uber.org/atomic v1.10.0
## explicit; go 1.18
go.uber.org/atomic
# go.uber.org/multierr v1.11.0
## explicit; go 1.19
go.uber.org/multierr
# go.uber.org/zap v1.24.0
# go.uber.org/zap v1.25.0
## explicit; go 1.19
go.uber.org/zap
go.uber.org/zap/buffer
@ -643,6 +640,7 @@ go.uber.org/zap/internal
go.uber.org/zap/internal/bufferpool
go.uber.org/zap/internal/color
go.uber.org/zap/internal/exit
go.uber.org/zap/internal/pool
go.uber.org/zap/zapcore
go.uber.org/zap/zapgrpc
# golang.org/x/crypto v0.12.0
@ -668,6 +666,7 @@ golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
# golang.org/x/exp v0.0.0-20220827204233-334a2380cb91
## explicit; go 1.18
golang.org/x/exp/constraints
golang.org/x/exp/maps
golang.org/x/exp/slices
# golang.org/x/net v0.14.0
## explicit; go 1.17
@ -741,7 +740,7 @@ golang.org/x/time/rate
## explicit; go 1.18
golang.org/x/tools/go/ast/inspector
golang.org/x/tools/internal/typeparams
# gomodules.xyz/jsonpatch/v2 v2.3.0 => github.com/gomodules/jsonpatch/v2 v2.2.0
# gomodules.xyz/jsonpatch/v2 v2.4.0 => github.com/gomodules/jsonpatch/v2 v2.2.0
## explicit; go 1.12
gomodules.xyz/jsonpatch/v2
# google.golang.org/appengine v1.6.7
@ -1601,7 +1600,7 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client
sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client/metrics
sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/common/metrics
sigs.k8s.io/apiserver-network-proxy/konnectivity-client/proto/client
# sigs.k8s.io/controller-runtime v0.15.1
# sigs.k8s.io/controller-runtime v0.16.0
## explicit; go 1.20
sigs.k8s.io/controller-runtime/pkg/cache
sigs.k8s.io/controller-runtime/pkg/cache/internal
@ -1629,6 +1628,7 @@ sigs.k8s.io/controller-runtime/pkg/log
sigs.k8s.io/controller-runtime/pkg/manager
sigs.k8s.io/controller-runtime/pkg/manager/signals
sigs.k8s.io/controller-runtime/pkg/metrics
sigs.k8s.io/controller-runtime/pkg/metrics/server
sigs.k8s.io/controller-runtime/pkg/predicate
sigs.k8s.io/controller-runtime/pkg/ratelimiter
sigs.k8s.io/controller-runtime/pkg/reconcile

View File

@ -22,8 +22,8 @@ import (
"net/http"
"time"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
@ -31,6 +31,7 @@ import (
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
toolscache "k8s.io/client-go/tools/cache"
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/cache/internal"
"sigs.k8s.io/controller-runtime/pkg/client"
@ -43,14 +44,28 @@ var (
defaultSyncPeriod = 10 * time.Hour
)
// InformerGetOptions defines the behavior of how informers are retrieved.
type InformerGetOptions internal.GetOptions
// InformerGetOption defines an option that alters the behavior of how informers are retrieved.
type InformerGetOption func(*InformerGetOptions)
// BlockUntilSynced determines whether a get request for an informer should block
// until the informer's cache has synced.
func BlockUntilSynced(shouldBlock bool) InformerGetOption {
return func(opts *InformerGetOptions) {
opts.BlockUntilSynced = &shouldBlock
}
}
// Cache knows how to load Kubernetes objects, fetch informers to request
// to receive events for Kubernetes objects (at a low-level),
// and add indices to fields on the objects stored in the cache.
type Cache interface {
// Cache acts as a client to objects stored in the cache.
// Reader acts as a client to objects stored in the cache.
client.Reader
// Cache loads informers and adds field indices.
// Informers loads informers and adds field indices.
Informers
}
@ -60,49 +75,53 @@ type Cache interface {
type Informers interface {
// GetInformer fetches or constructs an informer for the given object that corresponds to a single
// API kind and resource.
GetInformer(ctx context.Context, obj client.Object) (Informer, error)
GetInformer(ctx context.Context, obj client.Object, opts ...InformerGetOption) (Informer, error)
// GetInformerForKind is similar to GetInformer, except that it takes a group-version-kind, instead
// of the underlying object.
GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind) (Informer, error)
GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind, opts ...InformerGetOption) (Informer, error)
// Start runs all the informers known to this cache until the context is closed.
// It blocks.
Start(ctx context.Context) error
// WaitForCacheSync waits for all the caches to sync. Returns false if it could not sync a cache.
// WaitForCacheSync waits for all the caches to sync. Returns false if it could not sync a cache.
WaitForCacheSync(ctx context.Context) bool
// Informers knows how to add indices to the caches (informers) that it manages.
// FieldIndexer adds indices to the managed informers.
client.FieldIndexer
}
// Informer - informer allows you interact with the underlying informer.
// Informer allows you to interact with the underlying informer.
type Informer interface {
// AddEventHandler adds an event handler to the shared informer using the shared informer's resync
// period. Events to a single handler are delivered sequentially, but there is no coordination
// period. Events to a single handler are delivered sequentially, but there is no coordination
// between different handlers.
// It returns a registration handle for the handler that can be used to remove
// the handler again.
// the handler again and an error if the handler cannot be added.
AddEventHandler(handler toolscache.ResourceEventHandler) (toolscache.ResourceEventHandlerRegistration, error)
// AddEventHandlerWithResyncPeriod adds an event handler to the shared informer using the
// specified resync period. Events to a single handler are delivered sequentially, but there is
// specified resync period. Events to a single handler are delivered sequentially, but there is
// no coordination between different handlers.
// It returns a registration handle for the handler that can be used to remove
// the handler again and an error if the handler cannot be added.
AddEventHandlerWithResyncPeriod(handler toolscache.ResourceEventHandler, resyncPeriod time.Duration) (toolscache.ResourceEventHandlerRegistration, error)
// RemoveEventHandler removes a formerly added event handler given by
// RemoveEventHandler removes a previously added event handler given by
// its registration handle.
// This function is guaranteed to be idempotent, and thread-safe.
// This function is guaranteed to be idempotent and thread-safe.
RemoveEventHandler(handle toolscache.ResourceEventHandlerRegistration) error
// AddIndexers adds more indexers to this store. If you call this after you already have data
// AddIndexers adds indexers to this store. If this is called after there is already data
// in the store, the results are undefined.
AddIndexers(indexers toolscache.Indexers) error
// HasSynced return true if the informers underlying store has synced.
HasSynced() bool
}
// Options are the optional arguments for creating a new InformersMap object.
// Options are the optional arguments for creating a new Cache object.
type Options struct {
// HTTPClient is the http client to use for the REST client
HTTPClient *http.Client
@ -140,45 +159,80 @@ type Options struct {
// instead of `reconcile.Result{}`.
SyncPeriod *time.Duration
// Namespaces restricts the cache's ListWatch to the desired namespaces
// Default watches all namespaces
Namespaces []string
// ReaderFailOnMissingInformer configures the cache to return a ErrResourceNotCached error when a user
// requests, using Get() and List(), a resource the cache does not already have an informer for.
//
// This error is distinct from an errors.NotFound.
//
// Defaults to false, which means that the cache will start a new informer
// for every new requested resource.
ReaderFailOnMissingInformer bool
// DefaultLabelSelector will be used as a label selectors for all object types
// unless they have a more specific selector set in ByObject.
// DefaultNamespaces maps namespace names to cache configs. If set, only
// the namespaces in here will be watched and it will by used to default
// ByObject.Namespaces for all objects if that is nil.
//
// The options in the Config that are nil will be defaulted from
// the respective Default* settings.
DefaultNamespaces map[string]Config
// DefaultLabelSelector will be used as a label selector for all objects
// unless there is already one set in ByObject or DefaultNamespaces.
DefaultLabelSelector labels.Selector
// DefaultFieldSelector will be used as a field selectors for all object types
// unless they have a more specific selector set in ByObject.
// DefaultFieldSelector will be used as a field selector for all object types
// unless there is already one set in ByObject or DefaultNamespaces.
DefaultFieldSelector fields.Selector
// DefaultTransform will be used as transform for all object types
// unless they have a more specific transform set in ByObject.
// unless there is already one set in ByObject or DefaultNamespaces.
DefaultTransform toolscache.TransformFunc
// ByObject restricts the cache's ListWatch to the desired fields per GVK at the specified object.
ByObject map[client.Object]ByObject
// UnsafeDisableDeepCopy indicates not to deep copy objects during get or
// list objects for EVERY object.
// DefaultUnsafeDisableDeepCopy is the default for UnsafeDisableDeepCopy
// for everything that doesn't specify this.
//
// Be very careful with this, when enabled you must DeepCopy any object before mutating it,
// otherwise you will mutate the object in the cache.
//
// This is a global setting for all objects, and can be overridden by the ByObject setting.
UnsafeDisableDeepCopy *bool
// This will be used for all object types, unless it is set in ByObject or
// DefaultNamespaces.
DefaultUnsafeDisableDeepCopy *bool
// ByObject restricts the cache's ListWatch to the desired fields per GVK at the specified object.
// object, this will fall through to Default* settings.
ByObject map[client.Object]ByObject
// newInformer allows overriding of NewSharedIndexInformer for testing.
newInformer *func(toolscache.ListerWatcher, runtime.Object, time.Duration, toolscache.Indexers) toolscache.SharedIndexInformer
}
// ByObject offers more fine-grained control over the cache's ListWatch by object.
type ByObject struct {
// Namespaces maps a namespace name to cache configs. If set, only the
// namespaces in this map will be cached.
//
// Settings in the map value that are unset will be defaulted.
// Use an empty value for the specific setting to prevent that.
//
// A nil map allows to default this to the cache's DefaultNamespaces setting.
// An empty map prevents this and means that all namespaces will be cached.
//
// The defaulting follows the following precedence order:
// 1. ByObject
// 2. DefaultNamespaces[namespace]
// 3. Default*
//
// This must be unset for cluster-scoped objects.
Namespaces map[string]Config
// Label represents a label selector for the object.
Label labels.Selector
// Field represents a field selector for the object.
Field fields.Selector
// Transform is a map from objects to transformer functions which
// get applied when objects of the transformation are about to be committed
// to cache.
// Transform is a transformer function for the object which gets applied
// when objects of the transformation are about to be committed to the cache.
//
// This function is called both for new objects to enter the cache,
// and for updated objects.
@ -191,48 +245,120 @@ type ByObject struct {
UnsafeDisableDeepCopy *bool
}
// Config describes all potential options for a given watch.
type Config struct {
// LabelSelector specifies a label selector. A nil value allows to
// default this.
//
// Set to labels.Everything() if you don't want this defaulted.
LabelSelector labels.Selector
// FieldSelector specifics a field selector. A nil value allows to
// default this.
//
// Set to fields.Everything() if you don't want this defaulted.
FieldSelector fields.Selector
// Transform specifies a transform func. A nil value allows to default
// this.
//
// Set to an empty func to prevent this:
// func(in interface{}) (interface{}, error) { return in, nil }
Transform toolscache.TransformFunc
// UnsafeDisableDeepCopy specifies if List and Get requests against the
// cache should not DeepCopy. A nil value allows to default this.
UnsafeDisableDeepCopy *bool
}
// NewCacheFunc - Function for creating a new cache from the options and a rest config.
type NewCacheFunc func(config *rest.Config, opts Options) (Cache, error)
// New initializes and returns a new Cache.
func New(config *rest.Config, opts Options) (Cache, error) {
if len(opts.Namespaces) == 0 {
opts.Namespaces = []string{metav1.NamespaceAll}
}
if len(opts.Namespaces) > 1 {
return newMultiNamespaceCache(config, opts)
}
opts, err := defaultOpts(config, opts)
func New(cfg *rest.Config, opts Options) (Cache, error) {
opts, err := defaultOpts(cfg, opts)
if err != nil {
return nil, err
}
byGVK, err := convertToInformerOptsByGVK(opts.ByObject, opts.Scheme)
if err != nil {
return nil, err
newCacheFunc := newCache(cfg, opts)
var defaultCache Cache
if len(opts.DefaultNamespaces) > 0 {
defaultConfig := optionDefaultsToConfig(&opts)
defaultCache = newMultiNamespaceCache(newCacheFunc, opts.Scheme, opts.Mapper, opts.DefaultNamespaces, &defaultConfig)
} else {
defaultCache = newCacheFunc(optionDefaultsToConfig(&opts), corev1.NamespaceAll)
}
// Set the default selector and transform.
byGVK[schema.GroupVersionKind{}] = internal.InformersOptsByGVK{
Selector: internal.Selector{
Label: opts.DefaultLabelSelector,
Field: opts.DefaultFieldSelector,
},
if len(opts.ByObject) == 0 {
return defaultCache, nil
}
delegating := &delegatingByGVKCache{
scheme: opts.Scheme,
caches: make(map[schema.GroupVersionKind]Cache, len(opts.ByObject)),
defaultCache: defaultCache,
}
for obj, config := range opts.ByObject {
gvk, err := apiutil.GVKForObject(obj, opts.Scheme)
if err != nil {
return nil, fmt.Errorf("failed to get GVK for type %T: %w", obj, err)
}
var cache Cache
if len(config.Namespaces) > 0 {
cache = newMultiNamespaceCache(newCacheFunc, opts.Scheme, opts.Mapper, config.Namespaces, nil)
} else {
cache = newCacheFunc(byObjectToConfig(config), corev1.NamespaceAll)
}
delegating.caches[gvk] = cache
}
return delegating, nil
}
func optionDefaultsToConfig(opts *Options) Config {
return Config{
LabelSelector: opts.DefaultLabelSelector,
FieldSelector: opts.DefaultFieldSelector,
Transform: opts.DefaultTransform,
UnsafeDisableDeepCopy: opts.UnsafeDisableDeepCopy,
UnsafeDisableDeepCopy: opts.DefaultUnsafeDisableDeepCopy,
}
}
return &informerCache{
scheme: opts.Scheme,
Informers: internal.NewInformers(config, &internal.InformersOpts{
HTTPClient: opts.HTTPClient,
Scheme: opts.Scheme,
Mapper: opts.Mapper,
ResyncPeriod: *opts.SyncPeriod,
Namespace: opts.Namespaces[0],
ByGVK: byGVK,
}),
}, nil
func byObjectToConfig(byObject ByObject) Config {
return Config{
LabelSelector: byObject.Label,
FieldSelector: byObject.Field,
Transform: byObject.Transform,
UnsafeDisableDeepCopy: byObject.UnsafeDisableDeepCopy,
}
}
type newCacheFunc func(config Config, namespace string) Cache
func newCache(restConfig *rest.Config, opts Options) newCacheFunc {
return func(config Config, namespace string) Cache {
return &informerCache{
scheme: opts.Scheme,
Informers: internal.NewInformers(restConfig, &internal.InformersOpts{
HTTPClient: opts.HTTPClient,
Scheme: opts.Scheme,
Mapper: opts.Mapper,
ResyncPeriod: *opts.SyncPeriod,
Namespace: namespace,
Selector: internal.Selector{
Label: config.LabelSelector,
Field: config.FieldSelector,
},
Transform: config.Transform,
UnsafeDisableDeepCopy: pointer.BoolDeref(config.UnsafeDisableDeepCopy, false),
NewInformer: opts.newInformer,
}),
readerFailOnMissingInformer: opts.ReaderFailOnMissingInformer,
}
}
}
func defaultOpts(config *rest.Config, opts Options) (Options, error) {
@ -241,15 +367,12 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
config.UserAgent = rest.DefaultKubernetesUserAgent()
}
logger := log.WithName("setup")
// Use the rest HTTP client for the provided config if unset
if opts.HTTPClient == nil {
var err error
opts.HTTPClient, err = rest.HTTPClientFor(config)
if err != nil {
logger.Error(err, "Failed to get HTTP client")
return opts, fmt.Errorf("could not create HTTP client from config: %w", err)
return Options{}, fmt.Errorf("could not create HTTP client from config: %w", err)
}
}
@ -263,11 +386,54 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
var err error
opts.Mapper, err = apiutil.NewDiscoveryRESTMapper(config, opts.HTTPClient)
if err != nil {
logger.Error(err, "Failed to get API Group-Resources")
return opts, fmt.Errorf("could not create RESTMapper from config: %w", err)
return Options{}, fmt.Errorf("could not create RESTMapper from config: %w", err)
}
}
for namespace, cfg := range opts.DefaultNamespaces {
cfg = defaultConfig(cfg, optionDefaultsToConfig(&opts))
opts.DefaultNamespaces[namespace] = cfg
}
for obj, byObject := range opts.ByObject {
isNamespaced, err := apiutil.IsObjectNamespaced(obj, opts.Scheme, opts.Mapper)
if err != nil {
return opts, fmt.Errorf("failed to determine if %T is namespaced: %w", obj, err)
}
if !isNamespaced && byObject.Namespaces != nil {
return opts, fmt.Errorf("type %T is not namespaced, but its ByObject.Namespaces setting is not nil", obj)
}
// Default the namespace-level configs first, because they need to use the undefaulted type-level config.
for namespace, config := range byObject.Namespaces {
// 1. Default from the undefaulted type-level config
config = defaultConfig(config, byObjectToConfig(byObject))
// 2. Default from the namespace-level config. This was defaulted from the global default config earlier, but
// might not have an entry for the current namespace.
if defaultNamespaceSettings, hasDefaultNamespace := opts.DefaultNamespaces[namespace]; hasDefaultNamespace {
config = defaultConfig(config, defaultNamespaceSettings)
}
// 3. Default from the global defaults
config = defaultConfig(config, optionDefaultsToConfig(&opts))
byObject.Namespaces[namespace] = config
}
defaultedConfig := defaultConfig(byObjectToConfig(byObject), optionDefaultsToConfig(&opts))
byObject.Label = defaultedConfig.LabelSelector
byObject.Field = defaultedConfig.FieldSelector
byObject.Transform = defaultedConfig.Transform
byObject.UnsafeDisableDeepCopy = defaultedConfig.UnsafeDisableDeepCopy
if byObject.Namespaces == nil {
byObject.Namespaces = opts.DefaultNamespaces
}
opts.ByObject[obj] = byObject
}
// Default the resync period to 10 hours if unset
if opts.SyncPeriod == nil {
opts.SyncPeriod = &defaultSyncPeriod
@ -275,24 +441,19 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) {
return opts, nil
}
func convertToInformerOptsByGVK(in map[client.Object]ByObject, scheme *runtime.Scheme) (map[schema.GroupVersionKind]internal.InformersOptsByGVK, error) {
out := map[schema.GroupVersionKind]internal.InformersOptsByGVK{}
for object, byObject := range in {
gvk, err := apiutil.GVKForObject(object, scheme)
if err != nil {
return nil, err
}
if _, ok := out[gvk]; ok {
return nil, fmt.Errorf("duplicate cache options for GVK %v, cache.Options.ByObject has multiple types with the same GroupVersionKind", gvk)
}
out[gvk] = internal.InformersOptsByGVK{
Selector: internal.Selector{
Field: byObject.Field,
Label: byObject.Label,
},
Transform: byObject.Transform,
UnsafeDisableDeepCopy: byObject.UnsafeDisableDeepCopy,
}
func defaultConfig(toDefault, defaultFrom Config) Config {
if toDefault.LabelSelector == nil {
toDefault.LabelSelector = defaultFrom.LabelSelector
}
return out, nil
if toDefault.FieldSelector == nil {
toDefault.FieldSelector = defaultFrom.FieldSelector
}
if toDefault.Transform == nil {
toDefault.Transform = defaultFrom.Transform
}
if toDefault.UnsafeDisableDeepCopy == nil {
toDefault.UnsafeDisableDeepCopy = defaultFrom.UnsafeDisableDeepCopy
}
return toDefault
}

View File

@ -0,0 +1,127 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cache
import (
"context"
"strings"
"sync"
"golang.org/x/exp/maps"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
)
// delegatingByGVKCache delegates to a type-specific cache if present
// and uses the defaultCache otherwise.
type delegatingByGVKCache struct {
scheme *runtime.Scheme
caches map[schema.GroupVersionKind]Cache
defaultCache Cache
}
func (dbt *delegatingByGVKCache) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
cache, err := dbt.cacheForObject(obj)
if err != nil {
return err
}
return cache.Get(ctx, key, obj, opts...)
}
func (dbt *delegatingByGVKCache) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
cache, err := dbt.cacheForObject(list)
if err != nil {
return err
}
return cache.List(ctx, list, opts...)
}
func (dbt *delegatingByGVKCache) GetInformer(ctx context.Context, obj client.Object, opts ...InformerGetOption) (Informer, error) {
cache, err := dbt.cacheForObject(obj)
if err != nil {
return nil, err
}
return cache.GetInformer(ctx, obj, opts...)
}
func (dbt *delegatingByGVKCache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind, opts ...InformerGetOption) (Informer, error) {
return dbt.cacheForGVK(gvk).GetInformerForKind(ctx, gvk, opts...)
}
func (dbt *delegatingByGVKCache) Start(ctx context.Context) error {
allCaches := maps.Values(dbt.caches)
allCaches = append(allCaches, dbt.defaultCache)
wg := &sync.WaitGroup{}
errs := make(chan error)
for idx := range allCaches {
cache := allCaches[idx]
wg.Add(1)
go func() {
defer wg.Done()
if err := cache.Start(ctx); err != nil {
errs <- err
}
}()
}
select {
case err := <-errs:
return err
case <-ctx.Done():
wg.Wait()
return nil
}
}
func (dbt *delegatingByGVKCache) WaitForCacheSync(ctx context.Context) bool {
synced := true
for _, cache := range append(maps.Values(dbt.caches), dbt.defaultCache) {
if !cache.WaitForCacheSync(ctx) {
synced = false
}
}
return synced
}
func (dbt *delegatingByGVKCache) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error {
cache, err := dbt.cacheForObject(obj)
if err != nil {
return err
}
return cache.IndexField(ctx, obj, field, extractValue)
}
func (dbt *delegatingByGVKCache) cacheForObject(o runtime.Object) (Cache, error) {
gvk, err := apiutil.GVKForObject(o, dbt.scheme)
if err != nil {
return nil, err
}
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
return dbt.cacheForGVK(gvk), nil
}
func (dbt *delegatingByGVKCache) cacheForGVK(gvk schema.GroupVersionKind) Cache {
if specific, hasSpecific := dbt.caches[gvk]; hasSpecific {
return specific
}
return dbt.defaultCache
}

View File

@ -27,6 +27,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/tools/cache"
"sigs.k8s.io/controller-runtime/pkg/cache/internal"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
@ -45,11 +46,28 @@ func (*ErrCacheNotStarted) Error() string {
return "the cache is not started, can not read objects"
}
var _ error = (*ErrCacheNotStarted)(nil)
// ErrResourceNotCached indicates that the resource type
// the client asked the cache for is not cached, i.e. the
// corresponding informer does not exist yet.
type ErrResourceNotCached struct {
GVK schema.GroupVersionKind
}
// Error returns the error
func (r ErrResourceNotCached) Error() string {
return fmt.Sprintf("%s is not cached", r.GVK.String())
}
var _ error = (*ErrResourceNotCached)(nil)
// informerCache is a Kubernetes Object cache populated from internal.Informers.
// informerCache wraps internal.Informers.
type informerCache struct {
scheme *runtime.Scheme
*internal.Informers
readerFailOnMissingInformer bool
}
// Get implements Reader.
@ -59,7 +77,7 @@ func (ic *informerCache) Get(ctx context.Context, key client.ObjectKey, out clie
return err
}
started, cache, err := ic.Informers.Get(ctx, gvk, out)
started, cache, err := ic.getInformerForKind(ctx, gvk, out)
if err != nil {
return err
}
@ -67,7 +85,7 @@ func (ic *informerCache) Get(ctx context.Context, key client.ObjectKey, out clie
if !started {
return &ErrCacheNotStarted{}
}
return cache.Reader.Get(ctx, key, out)
return cache.Reader.Get(ctx, key, out, opts...)
}
// List implements Reader.
@ -77,7 +95,7 @@ func (ic *informerCache) List(ctx context.Context, out client.ObjectList, opts .
return err
}
started, cache, err := ic.Informers.Get(ctx, *gvk, cacheTypeObj)
started, cache, err := ic.getInformerForKind(ctx, *gvk, cacheTypeObj)
if err != nil {
return err
}
@ -123,33 +141,53 @@ func (ic *informerCache) objectTypeForListObject(list client.ObjectList) (*schem
return &gvk, cacheTypeObj, nil
}
// GetInformerForKind returns the informer for the GroupVersionKind.
func (ic *informerCache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind) (Informer, error) {
func applyGetOptions(opts ...InformerGetOption) *internal.GetOptions {
cfg := &InformerGetOptions{}
for _, opt := range opts {
opt(cfg)
}
return (*internal.GetOptions)(cfg)
}
// GetInformerForKind returns the informer for the GroupVersionKind. If no informer exists, one will be started.
func (ic *informerCache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind, opts ...InformerGetOption) (Informer, error) {
// Map the gvk to an object
obj, err := ic.scheme.New(gvk)
if err != nil {
return nil, err
}
_, i, err := ic.Informers.Get(ctx, gvk, obj)
_, i, err := ic.Informers.Get(ctx, gvk, obj, applyGetOptions(opts...))
if err != nil {
return nil, err
}
return i.Informer, err
return i.Informer, nil
}
// GetInformer returns the informer for the obj.
func (ic *informerCache) GetInformer(ctx context.Context, obj client.Object) (Informer, error) {
// GetInformer returns the informer for the obj. If no informer exists, one will be started.
func (ic *informerCache) GetInformer(ctx context.Context, obj client.Object, opts ...InformerGetOption) (Informer, error) {
gvk, err := apiutil.GVKForObject(obj, ic.scheme)
if err != nil {
return nil, err
}
_, i, err := ic.Informers.Get(ctx, gvk, obj)
_, i, err := ic.Informers.Get(ctx, gvk, obj, applyGetOptions(opts...))
if err != nil {
return nil, err
}
return i.Informer, err
return i.Informer, nil
}
func (ic *informerCache) getInformerForKind(ctx context.Context, gvk schema.GroupVersionKind, obj runtime.Object) (bool, *internal.Cache, error) {
if ic.readerFailOnMissingInformer {
cache, started, ok := ic.Informers.Peek(gvk, obj)
if !ok {
return false, nil, &ErrResourceNotCached{GVK: gvk}
}
return started, cache, nil
}
return ic.Informers.Get(ctx, gvk, obj, &internal.GetOptions{})
}
// NeedLeaderElection implements the LeaderElectionRunnable interface
@ -158,11 +196,11 @@ func (ic *informerCache) NeedLeaderElection() bool {
return false
}
// IndexField adds an indexer to the underlying cache, using extraction function to get
// value(s) from the given field. This index can then be used by passing a field selector
// IndexField adds an indexer to the underlying informer, using extractValue function to get
// value(s) from the given field. This index can then be used by passing a field selector
// to List. For one-to-one compatibility with "normal" field selectors, only return one value.
// The values may be anything. They will automatically be prefixed with the namespace of the
// given object, if present. The objects passed are guaranteed to be objects of the correct type.
// The values may be anything. They will automatically be prefixed with the namespace of the
// given object, if present. The objects passed are guaranteed to be objects of the correct type.
func (ic *informerCache) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error {
informer, err := ic.GetInformer(ctx, obj)
if err != nil {
@ -171,7 +209,7 @@ func (ic *informerCache) IndexField(ctx context.Context, obj client.Object, fiel
return indexByField(informer, field, extractValue)
}
func indexByField(indexer Informer, field string, extractor client.IndexerFunc) error {
func indexByField(informer Informer, field string, extractValue client.IndexerFunc) error {
indexFunc := func(objRaw interface{}) ([]string, error) {
// TODO(directxman12): check if this is the correct type?
obj, isObj := objRaw.(client.Object)
@ -184,7 +222,7 @@ func indexByField(indexer Informer, field string, extractor client.IndexerFunc)
}
ns := meta.GetNamespace()
rawVals := extractor(obj)
rawVals := extractValue(obj)
var vals []string
if ns == "" {
// if we're not doubling the keys for the namespaced case, just create a new slice with same length
@ -207,5 +245,5 @@ func indexByField(indexer Informer, field string, extractor client.IndexerFunc)
return vals, nil
}
return indexer.AddIndexers(cache.Indexers{internal.FieldIndexName(field): indexFunc})
return informer.AddIndexers(cache.Indexers{internal.FieldIndexName(field): indexFunc})
}

View File

@ -53,7 +53,7 @@ type CacheReader struct {
}
// Get checks the indexer for the object and writes a copy of it if found.
func (c *CacheReader) Get(_ context.Context, key client.ObjectKey, out client.Object, opts ...client.GetOption) error {
func (c *CacheReader) Get(_ context.Context, key client.ObjectKey, out client.Object, _ ...client.GetOption) error {
if c.scopeName == apimeta.RESTScopeNameRoot {
key.Namespace = ""
}
@ -67,9 +67,9 @@ func (c *CacheReader) Get(_ context.Context, key client.ObjectKey, out client.Ob
// Not found, return an error
if !exists {
// Resource gets transformed into Kind in the error anyway, so this is fine
return apierrors.NewNotFound(schema.GroupResource{
Group: c.groupVersionKind.Group,
Group: c.groupVersionKind.Group,
// Resource gets set as Kind in the error so this is fine
Resource: c.groupVersionKind.Kind,
}, key.Name)
}
@ -111,6 +111,10 @@ func (c *CacheReader) List(_ context.Context, out client.ObjectList, opts ...cli
listOpts := client.ListOptions{}
listOpts.ApplyOptions(opts)
if listOpts.Continue != "" {
return fmt.Errorf("continue list option is not supported by the cache")
}
switch {
case listOpts.FieldSelector != nil:
// TODO(directxman12): support more complicated field selectors by
@ -119,8 +123,8 @@ func (c *CacheReader) List(_ context.Context, out client.ObjectList, opts ...cli
if !requiresExact {
return fmt.Errorf("non-exact field matches are not supported by the cache")
}
// list all objects by the field selector. If this is namespaced and we have one, ask for the
// namespaced index key. Otherwise, ask for the non-namespaced variant by using the fake "all namespaces"
// list all objects by the field selector. If this is namespaced and we have one, ask for the
// namespaced index key. Otherwise, ask for the non-namespaced variant by using the fake "all namespaces"
// namespace.
objs, err = c.indexer.ByIndex(FieldIndexName(field), KeyToNamespacedKey(listOpts.Namespace, val))
case listOpts.Namespace != "":
@ -175,7 +179,7 @@ func (c *CacheReader) List(_ context.Context, out client.ObjectList, opts ...cli
}
// objectKeyToStorageKey converts an object key to store key.
// It's akin to MetaNamespaceKeyFunc. It's separate from
// It's akin to MetaNamespaceKeyFunc. It's separate from
// String to allow keeping the key format easily in sync with
// MetaNamespaceKeyFunc.
func objectKeyToStoreKey(k client.ObjectKey) string {
@ -191,7 +195,7 @@ func FieldIndexName(field string) string {
return "field:" + field
}
// noNamespaceNamespace is used as the "namespace" when we want to list across all namespaces.
// allNamespacesNamespace is used as the "namespace" when we want to list across all namespaces.
const allNamespacesNamespace = "__all_namespaces"
// KeyToNamespacedKey prefixes the given index key with a namespace

View File

@ -40,24 +40,23 @@ import (
// InformersOpts configures an InformerMap.
type InformersOpts struct {
HTTPClient *http.Client
Scheme *runtime.Scheme
Mapper meta.RESTMapper
ResyncPeriod time.Duration
Namespace string
ByGVK map[schema.GroupVersionKind]InformersOptsByGVK
}
// InformersOptsByGVK configured additional by group version kind (or object)
// in an InformerMap.
type InformersOptsByGVK struct {
HTTPClient *http.Client
Scheme *runtime.Scheme
Mapper meta.RESTMapper
ResyncPeriod time.Duration
Namespace string
NewInformer *func(cache.ListerWatcher, runtime.Object, time.Duration, cache.Indexers) cache.SharedIndexInformer
Selector Selector
Transform cache.TransformFunc
UnsafeDisableDeepCopy *bool
UnsafeDisableDeepCopy bool
}
// NewInformers creates a new InformersMap that can create informers under the hood.
func NewInformers(config *rest.Config, options *InformersOpts) *Informers {
newInformer := cache.NewSharedIndexInformer
if options.NewInformer != nil {
newInformer = *options.NewInformer
}
return &Informers{
config: config,
httpClient: options.HTTPClient,
@ -68,12 +67,15 @@ func NewInformers(config *rest.Config, options *InformersOpts) *Informers {
Unstructured: make(map[schema.GroupVersionKind]*Cache),
Metadata: make(map[schema.GroupVersionKind]*Cache),
},
codecs: serializer.NewCodecFactory(options.Scheme),
paramCodec: runtime.NewParameterCodec(options.Scheme),
resync: options.ResyncPeriod,
startWait: make(chan struct{}),
namespace: options.Namespace,
byGVK: options.ByGVK,
codecs: serializer.NewCodecFactory(options.Scheme),
paramCodec: runtime.NewParameterCodec(options.Scheme),
resync: options.ResyncPeriod,
startWait: make(chan struct{}),
namespace: options.Namespace,
selector: options.Selector,
transform: options.Transform,
unsafeDisableDeepCopy: options.UnsafeDisableDeepCopy,
newInformer: newInformer,
}
}
@ -92,6 +94,13 @@ type tracker struct {
Metadata map[schema.GroupVersionKind]*Cache
}
// GetOptions provides configuration to customize the behavior when
// getting an informer.
type GetOptions struct {
// BlockUntilSynced controls if the informer retrieval will block until the informer is synced. Defaults to `true`.
BlockUntilSynced *bool
}
// Informers create and caches Informers for (runtime.Object, schema.GroupVersionKind) pairs.
// It uses a standard parameter codec constructed based on the given generated Scheme.
type Informers struct {
@ -144,49 +153,15 @@ type Informers struct {
// default or empty string means all namespaces
namespace string
byGVK map[schema.GroupVersionKind]InformersOptsByGVK
selector Selector
transform cache.TransformFunc
unsafeDisableDeepCopy bool
// NewInformer allows overriding of the shared index informer constructor for testing.
newInformer func(cache.ListerWatcher, runtime.Object, time.Duration, cache.Indexers) cache.SharedIndexInformer
}
func (ip *Informers) getSelector(gvk schema.GroupVersionKind) Selector {
if ip.byGVK == nil {
return Selector{}
}
if res, ok := ip.byGVK[gvk]; ok {
return res.Selector
}
if res, ok := ip.byGVK[schema.GroupVersionKind{}]; ok {
return res.Selector
}
return Selector{}
}
func (ip *Informers) getTransform(gvk schema.GroupVersionKind) cache.TransformFunc {
if ip.byGVK == nil {
return nil
}
if res, ok := ip.byGVK[gvk]; ok {
return res.Transform
}
if res, ok := ip.byGVK[schema.GroupVersionKind{}]; ok {
return res.Transform
}
return nil
}
func (ip *Informers) getDisableDeepCopy(gvk schema.GroupVersionKind) bool {
if ip.byGVK == nil {
return false
}
if res, ok := ip.byGVK[gvk]; ok && res.UnsafeDisableDeepCopy != nil {
return *res.UnsafeDisableDeepCopy
}
if res, ok := ip.byGVK[schema.GroupVersionKind{}]; ok && res.UnsafeDisableDeepCopy != nil {
return *res.UnsafeDisableDeepCopy
}
return false
}
// Start calls Run on each of the informers and sets started to true. Blocks on the context.
// Start calls Run on each of the informers and sets started to true. Blocks on the context.
// It doesn't return start because it can't return an error, and it's not a runnable directly.
func (ip *Informers) Start(ctx context.Context) error {
func() {
@ -271,18 +246,19 @@ func (ip *Informers) WaitForCacheSync(ctx context.Context) bool {
return cache.WaitForCacheSync(ctx.Done(), ip.getHasSyncedFuncs()...)
}
func (ip *Informers) get(gvk schema.GroupVersionKind, obj runtime.Object) (res *Cache, started bool, ok bool) {
// Peek attempts to get the informer for the GVK, but does not start one if one does not exist.
func (ip *Informers) Peek(gvk schema.GroupVersionKind, obj runtime.Object) (res *Cache, started bool, ok bool) {
ip.mu.RLock()
defer ip.mu.RUnlock()
i, ok := ip.informersByType(obj)[gvk]
return i, ip.started, ok
}
// Get will create a new Informer and add it to the map of specificInformersMap if none exists. Returns
// Get will create a new Informer and add it to the map of specificInformersMap if none exists. Returns
// the Informer from the map.
func (ip *Informers) Get(ctx context.Context, gvk schema.GroupVersionKind, obj runtime.Object) (bool, *Cache, error) {
func (ip *Informers) Get(ctx context.Context, gvk schema.GroupVersionKind, obj runtime.Object, opts *GetOptions) (bool, *Cache, error) {
// Return the informer if it is found
i, started, ok := ip.get(gvk, obj)
i, started, ok := ip.Peek(gvk, obj)
if !ok {
var err error
if i, started, err = ip.addInformerToMap(gvk, obj); err != nil {
@ -290,7 +266,12 @@ func (ip *Informers) Get(ctx context.Context, gvk schema.GroupVersionKind, obj r
}
}
if started && !i.Informer.HasSynced() {
shouldBlock := true
if opts.BlockUntilSynced != nil {
shouldBlock = *opts.BlockUntilSynced
}
if shouldBlock && started && !i.Informer.HasSynced() {
// Wait for it to sync before returning the Informer so that folks don't read from a stale cache.
if !cache.WaitForCacheSync(ctx.Done(), i.Informer.HasSynced) {
return started, nil, apierrors.NewTimeoutError(fmt.Sprintf("failed waiting for %T Informer to sync", obj), 0)
@ -311,11 +292,12 @@ func (ip *Informers) informersByType(obj runtime.Object) map[schema.GroupVersion
}
}
// addInformerToMap either returns an existing informer or creates a new informer, adds it to the map and returns it.
func (ip *Informers) addInformerToMap(gvk schema.GroupVersionKind, obj runtime.Object) (*Cache, bool, error) {
ip.mu.Lock()
defer ip.mu.Unlock()
// Check the cache to see if we already have an Informer. If we do, return the Informer.
// Check the cache to see if we already have an Informer. If we do, return the Informer.
// This is for the case where 2 routines tried to get the informer when it wasn't in the map
// so neither returned early, but the first one created it.
if i, ok := ip.informersByType(obj)[gvk]; ok {
@ -327,13 +309,13 @@ func (ip *Informers) addInformerToMap(gvk schema.GroupVersionKind, obj runtime.O
if err != nil {
return nil, false, err
}
sharedIndexInformer := cache.NewSharedIndexInformer(&cache.ListWatch{
sharedIndexInformer := ip.newInformer(&cache.ListWatch{
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
ip.getSelector(gvk).ApplyToList(&opts)
ip.selector.ApplyToList(&opts)
return listWatcher.ListFunc(opts)
},
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
ip.getSelector(gvk).ApplyToList(&opts)
ip.selector.ApplyToList(&opts)
opts.Watch = true // Watch needs to be set to true separately
return listWatcher.WatchFunc(opts)
},
@ -342,7 +324,7 @@ func (ip *Informers) addInformerToMap(gvk schema.GroupVersionKind, obj runtime.O
})
// Check to see if there is a transformer for this gvk
if err := sharedIndexInformer.SetTransform(ip.getTransform(gvk)); err != nil {
if err := sharedIndexInformer.SetTransform(ip.transform); err != nil {
return nil, false, err
}
@ -358,7 +340,7 @@ func (ip *Informers) addInformerToMap(gvk schema.GroupVersionKind, obj runtime.O
indexer: sharedIndexInformer.GetIndexer(),
groupVersionKind: gvk,
scopeName: mapping.Scope.Name(),
disableDeepCopy: ip.getDisableDeepCopy(gvk),
disableDeepCopy: ip.unsafeDisableDeepCopy,
},
}
ip.informersByType(obj)[gvk] = i
@ -382,7 +364,7 @@ func (ip *Informers) makeListWatcher(gvk schema.GroupVersionKind, obj runtime.Ob
// Figure out if the GVK we're dealing with is global, or namespace scoped.
var namespace string
if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
namespace = restrictNamespaceBySelector(ip.namespace, ip.getSelector(gvk))
namespace = restrictNamespaceBySelector(ip.namespace, ip.selector)
}
switch obj.(type) {

View File

@ -1,55 +0,0 @@
package internal
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/tools/cache"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
)
// TransformFuncByGVK provides access to the correct transform function for
// any given GVK.
type TransformFuncByGVK interface {
Set(runtime.Object, *runtime.Scheme, cache.TransformFunc) error
Get(schema.GroupVersionKind) cache.TransformFunc
SetDefault(transformer cache.TransformFunc)
}
type transformFuncByGVK struct {
defaultTransform cache.TransformFunc
transformers map[schema.GroupVersionKind]cache.TransformFunc
}
// TransformFuncByGVKFromMap creates a TransformFuncByGVK from a map that
// maps GVKs to TransformFuncs.
func TransformFuncByGVKFromMap(in map[schema.GroupVersionKind]cache.TransformFunc) TransformFuncByGVK {
byGVK := &transformFuncByGVK{}
if defaultFunc, hasDefault := in[schema.GroupVersionKind{}]; hasDefault {
byGVK.defaultTransform = defaultFunc
}
delete(in, schema.GroupVersionKind{})
byGVK.transformers = in
return byGVK
}
func (t *transformFuncByGVK) SetDefault(transformer cache.TransformFunc) {
t.defaultTransform = transformer
}
func (t *transformFuncByGVK) Set(obj runtime.Object, scheme *runtime.Scheme, transformer cache.TransformFunc) error {
gvk, err := apiutil.GVKForObject(obj, scheme)
if err != nil {
return err
}
t.transformers[gvk] = transformer
return nil
}
func (t transformFuncByGVK) Get(gvk schema.GroupVersionKind) cache.TransformFunc {
if val, ok := t.transformers[gvk]; ok {
return val
}
return t.defaultTransform
}

View File

@ -25,8 +25,8 @@ import (
apimeta "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest"
toolscache "k8s.io/client-go/tools/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
)
@ -34,49 +34,31 @@ import (
// a new global namespaced cache to handle cluster scoped resources.
const globalCache = "_cluster-scope"
// MultiNamespacedCacheBuilder - Builder function to create a new multi-namespaced cache.
// This will scope the cache to a list of namespaces. Listing for all namespaces
// will list for all the namespaces that this knows about. By default this will create
// a global cache for cluster scoped resource. Note that this is not intended
// to be used for excluding namespaces, this is better done via a Predicate. Also note that
// you may face performance issues when using this with a high number of namespaces.
//
// Deprecated: Use cache.Options.Namespaces instead.
func MultiNamespacedCacheBuilder(namespaces []string) NewCacheFunc {
return func(config *rest.Config, opts Options) (Cache, error) {
opts.Namespaces = namespaces
return newMultiNamespaceCache(config, opts)
}
}
func newMultiNamespaceCache(config *rest.Config, opts Options) (Cache, error) {
if len(opts.Namespaces) < 2 {
return nil, fmt.Errorf("must specify more than one namespace to use multi-namespace cache")
}
opts, err := defaultOpts(config, opts)
if err != nil {
return nil, err
}
func newMultiNamespaceCache(
newCache newCacheFunc,
scheme *runtime.Scheme,
restMapper apimeta.RESTMapper,
namespaces map[string]Config,
globalConfig *Config, // may be nil in which case no cache for cluster-scoped objects will be created
) Cache {
// Create every namespace cache.
caches := map[string]Cache{}
for _, ns := range opts.Namespaces {
opts.Namespaces = []string{ns}
c, err := New(config, opts)
if err != nil {
return nil, err
}
caches[ns] = c
for namespace, config := range namespaces {
caches[namespace] = newCache(config, namespace)
}
// Create a cache for cluster scoped resources.
opts.Namespaces = []string{}
gCache, err := New(config, opts)
if err != nil {
return nil, fmt.Errorf("error creating global cache: %w", err)
// Create a cache for cluster scoped resources if requested
var clusterCache Cache
if globalConfig != nil {
clusterCache = newCache(*globalConfig, corev1.NamespaceAll)
}
return &multiNamespaceCache{namespaceToCache: caches, Scheme: opts.Scheme, RESTMapper: opts.Mapper, clusterCache: gCache}, nil
return &multiNamespaceCache{
namespaceToCache: caches,
Scheme: scheme,
RESTMapper: restMapper,
clusterCache: clusterCache,
}
}
// multiNamespaceCache knows how to handle multiple namespaced caches
@ -84,90 +66,96 @@ func newMultiNamespaceCache(config *rest.Config, opts Options) (Cache, error) {
// operator to a list of namespaces instead of watching every namespace
// in the cluster.
type multiNamespaceCache struct {
namespaceToCache map[string]Cache
Scheme *runtime.Scheme
RESTMapper apimeta.RESTMapper
namespaceToCache map[string]Cache
clusterCache Cache
}
var _ Cache = &multiNamespaceCache{}
// Methods for multiNamespaceCache to conform to the Informers interface.
func (c *multiNamespaceCache) GetInformer(ctx context.Context, obj client.Object) (Informer, error) {
informers := map[string]Informer{}
// If the object is clusterscoped, get the informer from clusterCache,
func (c *multiNamespaceCache) GetInformer(ctx context.Context, obj client.Object, opts ...InformerGetOption) (Informer, error) {
// If the object is cluster scoped, get the informer from clusterCache,
// if not use the namespaced caches.
isNamespaced, err := apiutil.IsObjectNamespaced(obj, c.Scheme, c.RESTMapper)
if err != nil {
return nil, err
}
if !isNamespaced {
clusterCacheInf, err := c.clusterCache.GetInformer(ctx, obj)
clusterCacheInformer, err := c.clusterCache.GetInformer(ctx, obj, opts...)
if err != nil {
return nil, err
}
informers[globalCache] = clusterCacheInf
return &multiNamespaceInformer{namespaceToInformer: informers}, nil
return &multiNamespaceInformer{
namespaceToInformer: map[string]Informer{
globalCache: clusterCacheInformer,
},
}, nil
}
namespaceToInformer := map[string]Informer{}
for ns, cache := range c.namespaceToCache {
informer, err := cache.GetInformer(ctx, obj)
informer, err := cache.GetInformer(ctx, obj, opts...)
if err != nil {
return nil, err
}
informers[ns] = informer
namespaceToInformer[ns] = informer
}
return &multiNamespaceInformer{namespaceToInformer: informers}, nil
return &multiNamespaceInformer{namespaceToInformer: namespaceToInformer}, nil
}
func (c *multiNamespaceCache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind) (Informer, error) {
informers := map[string]Informer{}
// If the object is clusterscoped, get the informer from clusterCache,
func (c *multiNamespaceCache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind, opts ...InformerGetOption) (Informer, error) {
// If the object is cluster scoped, get the informer from clusterCache,
// if not use the namespaced caches.
isNamespaced, err := apiutil.IsGVKNamespaced(gvk, c.RESTMapper)
if err != nil {
return nil, err
}
if !isNamespaced {
clusterCacheInf, err := c.clusterCache.GetInformerForKind(ctx, gvk)
clusterCacheInformer, err := c.clusterCache.GetInformerForKind(ctx, gvk, opts...)
if err != nil {
return nil, err
}
informers[globalCache] = clusterCacheInf
return &multiNamespaceInformer{namespaceToInformer: informers}, nil
return &multiNamespaceInformer{
namespaceToInformer: map[string]Informer{
globalCache: clusterCacheInformer,
},
}, nil
}
namespaceToInformer := map[string]Informer{}
for ns, cache := range c.namespaceToCache {
informer, err := cache.GetInformerForKind(ctx, gvk)
informer, err := cache.GetInformerForKind(ctx, gvk, opts...)
if err != nil {
return nil, err
}
informers[ns] = informer
namespaceToInformer[ns] = informer
}
return &multiNamespaceInformer{namespaceToInformer: informers}, nil
return &multiNamespaceInformer{namespaceToInformer: namespaceToInformer}, nil
}
func (c *multiNamespaceCache) Start(ctx context.Context) error {
// start global cache
go func() {
err := c.clusterCache.Start(ctx)
if err != nil {
log.Error(err, "cluster scoped cache failed to start")
}
}()
if c.clusterCache != nil {
go func() {
err := c.clusterCache.Start(ctx)
if err != nil {
log.Error(err, "cluster scoped cache failed to start")
}
}()
}
// start namespaced caches
for ns, cache := range c.namespaceToCache {
go func(ns string, cache Cache) {
err := cache.Start(ctx)
if err != nil {
log.Error(err, "multinamespace cache failed to start namespaced informer", "namespace", ns)
if err := cache.Start(ctx); err != nil {
log.Error(err, "multi-namespace cache failed to start namespaced informer", "namespace", ns)
}
}(ns, cache)
}
@ -179,13 +167,13 @@ func (c *multiNamespaceCache) Start(ctx context.Context) error {
func (c *multiNamespaceCache) WaitForCacheSync(ctx context.Context) bool {
synced := true
for _, cache := range c.namespaceToCache {
if s := cache.WaitForCacheSync(ctx); !s {
synced = s
if !cache.WaitForCacheSync(ctx) {
synced = false
}
}
// check if cluster scoped cache has synced
if !c.clusterCache.WaitForCacheSync(ctx) {
if c.clusterCache != nil && !c.clusterCache.WaitForCacheSync(ctx) {
synced = false
}
return synced
@ -224,7 +212,7 @@ func (c *multiNamespaceCache) Get(ctx context.Context, key client.ObjectKey, obj
if !ok {
return fmt.Errorf("unable to get: %v because of unknown namespace for the cache", key)
}
return cache.Get(ctx, key, obj)
return cache.Get(ctx, key, obj, opts...)
}
// List multi namespace cache will get all the objects in the namespaces that the cache is watching if asked for all namespaces.
@ -245,7 +233,7 @@ func (c *multiNamespaceCache) List(ctx context.Context, list client.ObjectList,
if listOpts.Namespace != corev1.NamespaceAll {
cache, ok := c.namespaceToCache[listOpts.Namespace]
if !ok {
return fmt.Errorf("unable to get: %v because of unknown namespace for the cache", listOpts.Namespace)
return fmt.Errorf("unable to list: %v because of unknown namespace for the cache", listOpts.Namespace)
}
return cache.List(ctx, list, opts...)
}
@ -278,12 +266,14 @@ func (c *multiNamespaceCache) List(ctx context.Context, list client.ObjectList,
return fmt.Errorf("object: %T must be a list type", list)
}
allItems = append(allItems, items...)
// The last list call should have the most correct resource version.
resourceVersion = accessor.GetResourceVersion()
if limitSet {
// decrement Limit by the number of items
// fetched from the current namespace.
listOpts.Limit -= int64(len(items))
// if a Limit was set and the number of
// items read has reached this set limit,
// then stop reading.
@ -325,9 +315,12 @@ func (h handlerRegistration) HasSynced() bool {
var _ Informer = &multiNamespaceInformer{}
// AddEventHandler adds the handler to each namespaced informer.
// AddEventHandler adds the handler to each informer.
func (i *multiNamespaceInformer) AddEventHandler(handler toolscache.ResourceEventHandler) (toolscache.ResourceEventHandlerRegistration, error) {
handles := handlerRegistration{handles: make(map[string]toolscache.ResourceEventHandlerRegistration, len(i.namespaceToInformer))}
handles := handlerRegistration{
handles: make(map[string]toolscache.ResourceEventHandlerRegistration, len(i.namespaceToInformer)),
}
for ns, informer := range i.namespaceToInformer {
registration, err := informer.AddEventHandler(handler)
if err != nil {
@ -335,12 +328,16 @@ func (i *multiNamespaceInformer) AddEventHandler(handler toolscache.ResourceEven
}
handles.handles[ns] = registration
}
return handles, nil
}
// AddEventHandlerWithResyncPeriod adds the handler with a resync period to each namespaced informer.
func (i *multiNamespaceInformer) AddEventHandlerWithResyncPeriod(handler toolscache.ResourceEventHandler, resyncPeriod time.Duration) (toolscache.ResourceEventHandlerRegistration, error) {
handles := handlerRegistration{handles: make(map[string]toolscache.ResourceEventHandlerRegistration, len(i.namespaceToInformer))}
handles := handlerRegistration{
handles: make(map[string]toolscache.ResourceEventHandlerRegistration, len(i.namespaceToInformer)),
}
for ns, informer := range i.namespaceToInformer {
registration, err := informer.AddEventHandlerWithResyncPeriod(handler, resyncPeriod)
if err != nil {
@ -348,14 +345,15 @@ func (i *multiNamespaceInformer) AddEventHandlerWithResyncPeriod(handler toolsca
}
handles.handles[ns] = registration
}
return handles, nil
}
// RemoveEventHandler removes a formerly added event handler given by its registration handle.
// RemoveEventHandler removes a previously added event handler given by its registration handle.
func (i *multiNamespaceInformer) RemoveEventHandler(h toolscache.ResourceEventHandlerRegistration) error {
handles, ok := h.(handlerRegistration)
if !ok {
return fmt.Errorf("it is not the registration returned by multiNamespaceInformer")
return fmt.Errorf("registration is not a registration returned by multiNamespaceInformer")
}
for ns, informer := range i.namespaceToInformer {
registration, ok := handles.handles[ns]
@ -369,7 +367,7 @@ func (i *multiNamespaceInformer) RemoveEventHandler(h toolscache.ResourceEventHa
return nil
}
// AddIndexers adds the indexer for each namespaced informer.
// AddIndexers adds the indexers to each informer.
func (i *multiNamespaceInformer) AddIndexers(indexers toolscache.Indexers) error {
for _, informer := range i.namespaceToInformer {
err := informer.AddIndexers(indexers)
@ -380,11 +378,11 @@ func (i *multiNamespaceInformer) AddIndexers(indexers toolscache.Indexers) error
return nil
}
// HasSynced checks if each namespaced informer has synced.
// HasSynced checks if each informer has synced.
func (i *multiNamespaceInformer) HasSynced() bool {
for _, informer := range i.namespaceToInformer {
if ok := informer.HasSynced(); !ok {
return ok
if !informer.HasSynced() {
return false
}
}
return true

View File

@ -77,10 +77,12 @@ type CacheOptions struct {
// Reader is a cache-backed reader that will be used to read objects from the cache.
// +required
Reader Reader
// DisableFor is a list of objects that should not be read from the cache.
// DisableFor is a list of objects that should never be read from the cache.
// Objects configured here always result in a live lookup.
DisableFor []Object
// Unstructured is a flag that indicates whether the cache-backed client should
// read unstructured objects or lists from the cache.
// If false, unstructured objects will always result in a live lookup.
Unstructured bool
}
@ -342,9 +344,11 @@ func (c *client) Get(ctx context.Context, key ObjectKey, obj Object, opts ...Get
if isUncached, err := c.shouldBypassCache(obj); err != nil {
return err
} else if !isUncached {
// Attempt to get from the cache.
return c.cache.Get(ctx, key, obj, opts...)
}
// Perform a live lookup.
switch obj.(type) {
case runtime.Unstructured:
return c.unstructuredClient.Get(ctx, key, obj, opts...)
@ -362,9 +366,11 @@ func (c *client) List(ctx context.Context, obj ObjectList, opts ...ListOption) e
if isUncached, err := c.shouldBypassCache(obj); err != nil {
return err
} else if !isUncached {
// Attempt to get from the cache.
return c.cache.List(ctx, obj, opts...)
}
// Perform a live lookup.
switch x := obj.(type) {
case runtime.Unstructured:
return c.unstructuredClient.List(ctx, obj, opts...)

View File

@ -28,12 +28,11 @@ import (
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/record"
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
intrec "sigs.k8s.io/controller-runtime/pkg/internal/recorder"
)
@ -95,18 +94,10 @@ type Options struct {
// value only if you know what you are doing. Defaults to 10 hours if unset.
// there will a 10 percent jitter between the SyncPeriod of all controllers
// so that all controllers will not send list requests simultaneously.
//
// Deprecated: Use Cache.SyncPeriod instead.
SyncPeriod *time.Duration
// Namespace if specified restricts the manager's cache to watch objects in
// the desired namespace Defaults to all namespaces
//
// Note: If a namespace is specified, controllers can still Watch for a
// cluster-scoped resource (e.g Node). For namespaced resources the cache
// will only hold objects from the desired namespace.
//
// Deprecated: Use Cache.Namespaces instead.
Namespace string
// HTTPClient is the http client that will be used to create the default
// Cache and Client. If not set the rest.HTTPClientFor function will be used
// to create the http client.
@ -141,18 +132,6 @@ type Options struct {
// Only use a custom NewClient if you know what you are doing.
NewClient client.NewClientFunc
// ClientDisableCacheFor tells the client that, if any cache is used, to bypass it
// for the given objects.
//
// Deprecated: Use Client.Cache.DisableFor instead.
ClientDisableCacheFor []client.Object
// DryRunClient specifies whether the client should be configured to enforce
// dryRun mode.
//
// Deprecated: Use Client.DryRun instead.
DryRunClient bool
// EventBroadcaster records Events emitted by the manager and sends them to the Kubernetes API
// Use this to customize the event correlator and spam filter
//
@ -218,9 +197,6 @@ func New(config *rest.Config, opts ...Option) (Cluster, error) {
if cacheOpts.SyncPeriod == nil {
cacheOpts.SyncPeriod = options.SyncPeriod
}
if len(cacheOpts.Namespaces) == 0 && options.Namespace != "" {
cacheOpts.Namespaces = []string{options.Namespace}
}
}
cache, err := options.NewCache(config, cacheOpts)
if err != nil {
@ -247,16 +223,6 @@ func New(config *rest.Config, opts ...Option) (Cluster, error) {
if clientOpts.Cache.Reader == nil {
clientOpts.Cache.Reader = cache
}
// For backward compatibility, the ClientDisableCacheFor option should
// be appended to the DisableFor option in the client.
clientOpts.Cache.DisableFor = append(clientOpts.Cache.DisableFor, options.ClientDisableCacheFor...)
if clientOpts.DryRun == nil && options.DryRunClient {
// For backward compatibility, the DryRunClient (if set) option should override
// the DryRun option in the client (if unset).
clientOpts.DryRun = pointer.Bool(true)
}
}
clientWriter, err := options.NewClient(config, clientOpts)
if err != nil {

View File

@ -1,5 +1,4 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
// Code generated by controller-gen. DO NOT EDIT.

View File

@ -159,7 +159,9 @@ func NewUnmanaged(name string, mgr manager.Manager, options Options) (Controller
return &controller.Controller{
Do: options.Reconciler,
MakeQueue: func() workqueue.RateLimitingInterface {
return workqueue.NewNamedRateLimitingQueue(options.RateLimiter, name)
return workqueue.NewRateLimitingQueueWithConfig(options.RateLimiter, workqueue.RateLimitingQueueConfig{
Name: name,
})
},
MaxConcurrentReconciles: options.MaxConcurrentReconciles,
CacheSyncTimeout: options.CacheSyncTimeout,

View File

@ -28,6 +28,7 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/uuid"
"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/handler"
ctrlmetrics "sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics"
logf "sigs.k8s.io/controller-runtime/pkg/log"
@ -311,6 +312,7 @@ func (c *Controller) reconcileHandler(ctx context.Context, obj interface{}) {
// RunInformersAndControllers the syncHandler, passing it the Namespace/Name string of the
// resource to be synced.
log.V(5).Info("Reconciling")
result, err := c.Reconcile(ctx, req)
switch {
case err != nil:
@ -321,8 +323,12 @@ func (c *Controller) reconcileHandler(ctx context.Context, obj interface{}) {
}
ctrlmetrics.ReconcileErrors.WithLabelValues(c.Name).Inc()
ctrlmetrics.ReconcileTotal.WithLabelValues(c.Name, labelError).Inc()
if !result.IsZero() {
log.Info("Warning: Reconciler returned both a non-zero result and a non-nil error. The result will always be ignored if the error is non-nil and the non-nil error causes reqeueuing with exponential backoff. For more details, see: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/reconcile#Reconciler")
}
log.Error(err, "Reconciler error")
case result.RequeueAfter > 0:
log.V(5).Info(fmt.Sprintf("Reconcile done, requeueing after %s", result.RequeueAfter))
// The result.RequeueAfter request will be lost, if it is returned
// along with a non-nil error. But this is intended as
// We need to drive to stable reconcile loops before queuing due
@ -331,9 +337,11 @@ func (c *Controller) reconcileHandler(ctx context.Context, obj interface{}) {
c.Queue.AddAfter(req, result.RequeueAfter)
ctrlmetrics.ReconcileTotal.WithLabelValues(c.Name, labelRequeueAfter).Inc()
case result.Requeue:
log.V(5).Info("Reconcile done, requeueing")
c.Queue.AddRateLimited(req)
ctrlmetrics.ReconcileTotal.WithLabelValues(c.Name, labelRequeue).Inc()
default:
log.V(5).Info("Reconcile successful")
// Finally, if no error occurs we Forget this item so it does not
// get queued again until another change happens.
c.Queue.Forget(obj)

View File

@ -188,6 +188,9 @@ func (l *delegatingLogSink) WithValues(tags ...interface{}) logr.LogSink {
// provided, instead of the temporary initial one, if this method
// has not been previously called.
func (l *delegatingLogSink) Fulfill(actual logr.LogSink) {
if actual == nil {
actual = NullLogSink{}
}
if l.promise != nil {
l.promise.Fulfill(actual)
}

View File

@ -34,6 +34,7 @@ limitations under the License.
package log
import (
"bytes"
"context"
"fmt"
"os"
@ -56,7 +57,15 @@ func eventuallyFulfillRoot() {
}
if time.Since(rootLogCreated).Seconds() >= 30 {
if logFullfilled.CompareAndSwap(false, true) {
fmt.Fprintf(os.Stderr, "[controller-runtime] log.SetLogger(...) was never called, logs will not be displayed:\n%s", debug.Stack())
stack := debug.Stack()
stackLines := bytes.Count(stack, []byte{'\n'})
sep := []byte{'\n', '\t', '>', ' ', ' '}
fmt.Fprintf(os.Stderr,
"[controller-runtime] log.SetLogger(...) was never called; logs will not be displayed.\nDetected at:%s%s", sep,
// prefix every line, so it's clear this is a stack trace related to the above message
bytes.Replace(stack, []byte{'\n'}, sep, stackLines-1),
)
SetLogger(logr.New(NullLogSink{}))
}
}

View File

@ -28,7 +28,6 @@ import (
"time"
"github.com/go-logr/logr"
"github.com/prometheus/client_golang/prometheus/promhttp"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
kerrors "k8s.io/apimachinery/pkg/util/errors"
@ -44,7 +43,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/internal/httpserver"
intrec "sigs.k8s.io/controller-runtime/pkg/internal/recorder"
"sigs.k8s.io/controller-runtime/pkg/metrics"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)
@ -57,7 +56,6 @@ const (
defaultReadinessEndpoint = "/readyz"
defaultLivenessEndpoint = "/healthz"
defaultMetricsEndpoint = "/metrics"
)
var _ Runnable = &controllerManager{}
@ -84,11 +82,8 @@ type controllerManager struct {
// on shutdown
leaderElectionReleaseOnCancel bool
// metricsListener is used to serve prometheus metrics
metricsListener net.Listener
// metricsExtraHandlers contains extra handlers to register on http server that serves metrics.
metricsExtraHandlers map[string]http.Handler
// metricsServer is used to serve prometheus metrics
metricsServer metricsserver.Server
// healthProbeListener is used to serve liveness probe
healthProbeListener net.Listener
@ -184,28 +179,6 @@ func (cm *controllerManager) add(r Runnable) error {
return cm.runnables.Add(r)
}
// AddMetricsExtraHandler adds extra handler served on path to the http server that serves metrics.
func (cm *controllerManager) AddMetricsExtraHandler(path string, handler http.Handler) error {
cm.Lock()
defer cm.Unlock()
if cm.started {
return fmt.Errorf("unable to add new metrics handler because metrics endpoint has already been created")
}
if path == defaultMetricsEndpoint {
return fmt.Errorf("overriding builtin %s endpoint is not allowed", defaultMetricsEndpoint)
}
if _, found := cm.metricsExtraHandlers[path]; found {
return fmt.Errorf("can't register extra handler by duplicate path %q on metrics http server", path)
}
cm.metricsExtraHandlers[path] = handler
cm.logger.V(2).Info("Registering metrics http server extra handler", "path", path)
return nil
}
// AddHealthzCheck allows you to add Healthz checker.
func (cm *controllerManager) AddHealthzCheck(name string, check healthz.Checker) error {
cm.Lock()
@ -296,31 +269,10 @@ func (cm *controllerManager) GetControllerOptions() config.Controller {
return cm.controllerConfig
}
func (cm *controllerManager) addMetricsServer() error {
func (cm *controllerManager) addHealthProbeServer() error {
mux := http.NewServeMux()
srv := httpserver.New(mux)
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{
ErrorHandling: promhttp.HTTPErrorOnError,
})
// TODO(JoelSpeed): Use existing Kubernetes machinery for serving metrics
mux.Handle(defaultMetricsEndpoint, handler)
for path, extraHandler := range cm.metricsExtraHandlers {
mux.Handle(path, extraHandler)
}
return cm.add(&server{
Kind: "metrics",
Log: cm.logger.WithValues("path", defaultMetricsEndpoint),
Server: srv,
Listener: cm.metricsListener,
})
}
func (cm *controllerManager) serveHealthProbes() {
mux := http.NewServeMux()
server := httpserver.New(mux)
if cm.readyzHandler != nil {
mux.Handle(cm.readinessEndpointName, http.StripPrefix(cm.readinessEndpointName, cm.readyzHandler))
// Append '/' suffix to handle subpaths
@ -332,7 +284,12 @@ func (cm *controllerManager) serveHealthProbes() {
mux.Handle(cm.livenessEndpointName+"/", http.StripPrefix(cm.livenessEndpointName, cm.healthzHandler))
}
go cm.httpServe("health probe", cm.logger, server, cm.healthProbeListener)
return cm.add(&server{
Kind: "health probe",
Log: cm.logger,
Server: srv,
Listener: cm.healthProbeListener,
})
}
func (cm *controllerManager) addPprofServer() error {
@ -353,42 +310,6 @@ func (cm *controllerManager) addPprofServer() error {
})
}
func (cm *controllerManager) httpServe(kind string, log logr.Logger, server *http.Server, ln net.Listener) {
log = log.WithValues("kind", kind, "addr", ln.Addr())
go func() {
log.Info("Starting server")
if err := server.Serve(ln); err != nil {
if errors.Is(err, http.ErrServerClosed) {
return
}
if atomic.LoadInt64(cm.stopProcedureEngaged) > 0 {
// There might be cases where connections are still open and we try to shutdown
// but not having enough time to close the connection causes an error in Serve
//
// In that case we want to avoid returning an error to the main error channel.
log.Error(err, "error on Serve after stop has been engaged")
return
}
cm.errChan <- err
}
}()
// Shutdown the server when stop is closed.
<-cm.internalProceduresStop
if err := server.Shutdown(cm.shutdownCtx); err != nil {
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
// Avoid logging context related errors.
return
}
if atomic.LoadInt64(cm.stopProcedureEngaged) > 0 {
cm.logger.Error(err, "error on Shutdown after stop has been engaged")
return
}
cm.errChan <- err
}
}
// Start starts the manager and waits indefinitely.
// There is only two ways to have start return:
// An error has occurred during in one of the internal operations,
@ -441,15 +362,19 @@ func (cm *controllerManager) Start(ctx context.Context) (err error) {
// Metrics should be served whether the controller is leader or not.
// (If we don't serve metrics for non-leaders, prometheus will still scrape
// the pod but will get a connection refused).
if cm.metricsListener != nil {
if err := cm.addMetricsServer(); err != nil {
if cm.metricsServer != nil {
// Note: We are adding the metrics server directly to HTTPServers here as matching on the
// metricsserver.Server interface in cm.runnables.Add would be very brittle.
if err := cm.runnables.HTTPServers.Add(cm.metricsServer, nil); err != nil {
return fmt.Errorf("failed to add metrics server: %w", err)
}
}
// Serve health probes.
if cm.healthProbeListener != nil {
cm.serveHealthProbes()
if err := cm.addHealthProbeServer(); err != nil {
return fmt.Errorf("failed to add health probe server: %w", err)
}
}
// Add pprof server
@ -459,7 +384,17 @@ func (cm *controllerManager) Start(ctx context.Context) (err error) {
}
}
// First start any webhook servers, which includes conversion, validation, and defaulting
// First start any internal HTTP servers, which includes health probes, metrics and profiling if enabled.
//
// WARNING: Internal HTTP servers MUST start before any cache is populated, otherwise it would block
// conversion webhooks to be ready for serving which make the cache never get ready.
if err := cm.runnables.HTTPServers.Start(cm.internalCtx); err != nil {
if err != nil {
return fmt.Errorf("failed to start HTTP servers: %w", err)
}
}
// Start any webhook servers, which includes conversion, validation, and defaulting
// webhooks that are registered.
//
// WARNING: Webhooks MUST start before any cache is populated, otherwise there is a race condition
@ -591,10 +526,13 @@ func (cm *controllerManager) engageStopProcedure(stopComplete <-chan struct{}) e
cm.logger.Info("Stopping and waiting for caches")
cm.runnables.Caches.StopAndWait(cm.shutdownCtx)
// Webhooks should come last, as they might be still serving some requests.
// Webhooks and internal HTTP servers should come last, as they might be still serving some requests.
cm.logger.Info("Stopping and waiting for webhooks")
cm.runnables.Webhooks.StopAndWait(cm.shutdownCtx)
cm.logger.Info("Stopping and waiting for HTTP servers")
cm.runnables.HTTPServers.StopAndWait(cm.shutdownCtx)
// Proceed to close the manager and overall shutdown context.
cm.logger.Info("Wait completed, proceeding to shutdown the manager")
shutdownCancel()

View File

@ -18,7 +18,6 @@ package manager
import (
"context"
"crypto/tls"
"errors"
"fmt"
"net"
@ -34,6 +33,7 @@ import (
"k8s.io/client-go/tools/leaderelection/resourcelock"
"k8s.io/client-go/tools/record"
"k8s.io/utils/pointer"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
@ -44,7 +44,6 @@ import (
intrec "sigs.k8s.io/controller-runtime/pkg/internal/recorder"
"sigs.k8s.io/controller-runtime/pkg/leaderelection"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/metrics"
"sigs.k8s.io/controller-runtime/pkg/recorder"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)
@ -66,13 +65,6 @@ type Manager interface {
// election was configured.
Elected() <-chan struct{}
// AddMetricsExtraHandler adds an extra handler served on path to the http server that serves metrics.
// Might be useful to register some diagnostic endpoints e.g. pprof. Note that these endpoints meant to be
// sensitive and shouldn't be exposed publicly.
// If the simple path -> handler mapping offered here is not enough, a new http server/listener should be added as
// Runnable to the manager via Add method.
AddMetricsExtraHandler(path string, handler http.Handler) error
// AddHealthzCheck allows you to add Healthz checker
AddHealthzCheck(name string, check healthz.Checker) error
@ -141,35 +133,6 @@ type Options struct {
// Only use a custom NewClient if you know what you are doing.
NewClient client.NewClientFunc
// SyncPeriod determines the minimum frequency at which watched resources are
// reconciled. A lower period will correct entropy more quickly, but reduce
// responsiveness to change if there are many watched resources. Change this
// value only if you know what you are doing. Defaults to 10 hours if unset.
// there will a 10 percent jitter between the SyncPeriod of all controllers
// so that all controllers will not send list requests simultaneously.
//
// This applies to all controllers.
//
// A period sync happens for two reasons:
// 1. To insure against a bug in the controller that causes an object to not
// be requeued, when it otherwise should be requeued.
// 2. To insure against an unknown bug in controller-runtime, or its dependencies,
// that causes an object to not be requeued, when it otherwise should be
// requeued, or to be removed from the queue, when it otherwise should not
// be removed.
//
// If you want
// 1. to insure against missed watch events, or
// 2. to poll services that cannot be watched,
// then we recommend that, instead of changing the default period, the
// controller requeue, with a constant duration `t`, whenever the controller
// is "done" with an object, and would otherwise not requeue it, i.e., we
// recommend the `Reconcile` function return `reconcile.Result{RequeueAfter: t}`,
// instead of `reconcile.Result{}`.
//
// Deprecated: Use Cache.SyncPeriod instead.
SyncPeriod *time.Duration
// Logger is the logger that should be used by this manager.
// If none is set, it defaults to log.Log global logger.
Logger logr.Logger
@ -240,27 +203,17 @@ type Options struct {
// wait to force acquire leadership. This is measured against time of
// last observed ack. Default is 15 seconds.
LeaseDuration *time.Duration
// RenewDeadline is the duration that the acting controlplane will retry
// refreshing leadership before giving up. Default is 10 seconds.
RenewDeadline *time.Duration
// RetryPeriod is the duration the LeaderElector clients should wait
// between tries of actions. Default is 2 seconds.
RetryPeriod *time.Duration
// Namespace, if specified, restricts the manager's cache to watch objects in
// the desired namespace. Defaults to all namespaces.
//
// Note: If a namespace is specified, controllers can still Watch for a
// cluster-scoped resource (e.g Node). For namespaced resources, the cache
// will only hold objects from the desired namespace.
//
// Deprecated: Use Cache.Namespaces instead.
Namespace string
// MetricsBindAddress is the TCP address that the controller should bind to
// for serving prometheus metrics.
// It can be set to "0" to disable the metrics serving.
MetricsBindAddress string
// Metrics are the metricsserver.Options that will be used to create the metricsserver.Server.
Metrics metricsserver.Options
// HealthProbeBindAddress is the TCP address that the controller should bind to
// for serving health probes
@ -280,34 +233,9 @@ type Options struct {
// before exposing it to public.
PprofBindAddress string
// Port is the port that the webhook server serves at.
// It is used to set webhook.Server.Port if WebhookServer is not set.
//
// Deprecated: Use WebhookServer instead. A WebhookServer can be created via webhook.NewServer.
Port int
// Host is the hostname that the webhook server binds to.
// It is used to set webhook.Server.Host if WebhookServer is not set.
//
// Deprecated: Use WebhookServer instead. A WebhookServer can be created via webhook.NewServer.
Host string
// CertDir is the directory that contains the server key and certificate.
// If not set, webhook server would look up the server key and certificate in
// {TempDir}/k8s-webhook-server/serving-certs. The server key and certificate
// must be named tls.key and tls.crt, respectively.
// It is used to set webhook.Server.CertDir if WebhookServer is not set.
//
// Deprecated: Use WebhookServer instead. A WebhookServer can be created via webhook.NewServer.
CertDir string
// TLSOpts is used to allow configuring the TLS config used for the webhook server.
//
// Deprecated: Use WebhookServer instead. A WebhookServer can be created via webhook.NewServer.
TLSOpts []func(*tls.Config)
// WebhookServer is an externally configured webhook.Server. By default,
// a Manager will create a default server using Port, Host, and CertDir;
// if this is set, the Manager will use this server instead.
// a Manager will create a server via webhook.NewServer with default settings.
// If this is set, the Manager will use this server instead.
WebhookServer webhook.Server
// BaseContext is the function that provides Context values to Runnables
@ -315,18 +243,6 @@ type Options struct {
// will receive a new Background Context instead.
BaseContext BaseContextFunc
// ClientDisableCacheFor tells the client that, if any cache is used, to bypass it
// for the given objects.
//
// Deprecated: Use Client.Cache.DisableCacheFor instead.
ClientDisableCacheFor []client.Object
// DryRunClient specifies whether the client should be configured to enforce
// dryRun mode.
//
// Deprecated: Use Client.DryRun instead.
DryRunClient bool
// EventBroadcaster records Events emitted by the manager and sends them to the Kubernetes API
// Use this to customize the event correlator and spam filter
//
@ -354,7 +270,7 @@ type Options struct {
// Dependency injection for testing
newRecorderProvider func(config *rest.Config, httpClient *http.Client, scheme *runtime.Scheme, logger logr.Logger, makeBroadcaster intrec.EventBroadcasterProducer) (*intrec.Provider, error)
newResourceLock func(config *rest.Config, recorderProvider recorder.Provider, options leaderelection.Options) (resourcelock.Interface, error)
newMetricsListener func(addr string) (net.Listener, error)
newMetricsServer func(options metricsserver.Options, config *rest.Config, httpClient *http.Client) (metricsserver.Server, error)
newHealthProbeListener func(addr string) (net.Listener, error)
newPprofListener func(addr string) (net.Listener, error)
}
@ -391,6 +307,9 @@ type LeaderElectionRunnable interface {
}
// New returns a new Manager for creating Controllers.
// Note that if ContentType in the given config is not set, "application/vnd.kubernetes.protobuf"
// will be used for all built-in resources of Kubernetes, and "application/json" is for other types
// including all CRD resources.
func New(config *rest.Config, options Options) (Manager, error) {
if config == nil {
return nil, errors.New("must specify Config")
@ -402,15 +321,11 @@ func New(config *rest.Config, options Options) (Manager, error) {
clusterOptions.Scheme = options.Scheme
clusterOptions.MapperProvider = options.MapperProvider
clusterOptions.Logger = options.Logger
clusterOptions.SyncPeriod = options.SyncPeriod
clusterOptions.NewCache = options.NewCache
clusterOptions.NewClient = options.NewClient
clusterOptions.Cache = options.Cache
clusterOptions.Client = options.Client
clusterOptions.Namespace = options.Namespace //nolint:staticcheck
clusterOptions.ClientDisableCacheFor = options.ClientDisableCacheFor //nolint:staticcheck
clusterOptions.DryRunClient = options.DryRunClient //nolint:staticcheck
clusterOptions.EventBroadcaster = options.EventBroadcaster //nolint:staticcheck
clusterOptions.EventBroadcaster = options.EventBroadcaster //nolint:staticcheck
})
if err != nil {
return nil, err
@ -459,16 +374,12 @@ func New(config *rest.Config, options Options) (Manager, error) {
}
}
// Create the metrics listener. This will throw an error if the metrics bind
// address is invalid or already in use.
metricsListener, err := options.newMetricsListener(options.MetricsBindAddress)
// Create the metrics server.
metricsServer, err := options.newMetricsServer(options.Metrics, config, cluster.GetHTTPClient())
if err != nil {
return nil, err
}
// By default we have no extra endpoints to expose on metrics http server.
metricsExtraHandlers := make(map[string]http.Handler)
// Create health probes listener. This will throw an error if the bind
// address is invalid or already in use.
healthProbeListener, err := options.newHealthProbeListener(options.HealthProbeBindAddress)
@ -493,8 +404,7 @@ func New(config *rest.Config, options Options) (Manager, error) {
errChan: errChan,
recorderProvider: recorderProvider,
resourceLock: resourceLock,
metricsListener: metricsListener,
metricsExtraHandlers: metricsExtraHandlers,
metricsServer: metricsServer,
controllerConfig: options.Controller,
logger: options.Logger,
elected: make(chan struct{}),
@ -532,16 +442,16 @@ func (o Options) AndFrom(loader config.ControllerManagerConfiguration) (Options,
o = o.setLeaderElectionConfig(newObj)
if o.SyncPeriod == nil && newObj.SyncPeriod != nil {
o.SyncPeriod = &newObj.SyncPeriod.Duration
if o.Cache.SyncPeriod == nil && newObj.SyncPeriod != nil {
o.Cache.SyncPeriod = &newObj.SyncPeriod.Duration
}
if o.Namespace == "" && newObj.CacheNamespace != "" {
o.Namespace = newObj.CacheNamespace
if len(o.Cache.DefaultNamespaces) == 0 && newObj.CacheNamespace != "" {
o.Cache.DefaultNamespaces = map[string]cache.Config{newObj.CacheNamespace: {}}
}
if o.MetricsBindAddress == "" && newObj.Metrics.BindAddress != "" {
o.MetricsBindAddress = newObj.Metrics.BindAddress
if o.Metrics.BindAddress == "" && newObj.Metrics.BindAddress != "" {
o.Metrics.BindAddress = newObj.Metrics.BindAddress
}
if o.HealthProbeBindAddress == "" && newObj.Health.HealthProbeBindAddress != "" {
@ -556,20 +466,15 @@ func (o Options) AndFrom(loader config.ControllerManagerConfiguration) (Options,
o.LivenessEndpointName = newObj.Health.LivenessEndpointName
}
if o.Port == 0 && newObj.Webhook.Port != nil {
o.Port = *newObj.Webhook.Port
}
if o.Host == "" && newObj.Webhook.Host != "" {
o.Host = newObj.Webhook.Host
}
if o.CertDir == "" && newObj.Webhook.CertDir != "" {
o.CertDir = newObj.Webhook.CertDir
}
if o.WebhookServer == nil {
port := 0
if newObj.Webhook.Port != nil {
port = *newObj.Webhook.Port
}
o.WebhookServer = webhook.NewServer(webhook.Options{
Port: o.Port,
Host: o.Host,
CertDir: o.CertDir,
Port: port,
Host: newObj.Webhook.Host,
CertDir: newObj.Webhook.CertDir,
})
}
@ -697,8 +602,8 @@ func setOptionsDefaults(options Options) Options {
}
}
if options.newMetricsListener == nil {
options.newMetricsListener = metrics.NewListener
if options.newMetricsServer == nil {
options.newMetricsServer = metricsserver.NewServer
}
leaseDuration, renewDeadline, retryPeriod := defaultLeaseDuration, defaultRenewDeadline, defaultRetryPeriod
if options.LeaseDuration == nil {
@ -743,12 +648,7 @@ func setOptionsDefaults(options Options) Options {
}
if options.WebhookServer == nil {
options.WebhookServer = webhook.NewServer(webhook.Options{
Host: options.Host,
Port: options.Port,
CertDir: options.CertDir,
TLSOpts: options.TLSOpts,
})
options.WebhookServer = webhook.NewServer(webhook.Options{})
}
return options

View File

@ -28,6 +28,7 @@ type runnableCheck func(ctx context.Context) bool
// runnables handles all the runnables for a manager by grouping them accordingly to their
// type (webhooks, caches etc.).
type runnables struct {
HTTPServers *runnableGroup
Webhooks *runnableGroup
Caches *runnableGroup
LeaderElection *runnableGroup
@ -37,6 +38,7 @@ type runnables struct {
// newRunnables creates a new runnables object.
func newRunnables(baseContext BaseContextFunc, errChan chan error) *runnables {
return &runnables{
HTTPServers: newRunnableGroup(baseContext, errChan),
Webhooks: newRunnableGroup(baseContext, errChan),
Caches: newRunnableGroup(baseContext, errChan),
LeaderElection: newRunnableGroup(baseContext, errChan),
@ -52,6 +54,8 @@ func newRunnables(baseContext BaseContextFunc, errChan chan error) *runnables {
// The runnables added after Start are started directly.
func (r *runnables) Add(fn Runnable) error {
switch runnable := fn.(type) {
case *server:
return r.HTTPServers.Add(fn, nil)
case hasCache:
return r.Caches.Add(fn, func(ctx context.Context) bool {
return runnable.GetCache().WaitForCacheSync(ctx)

View File

@ -1,52 +0,0 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package metrics
import (
"fmt"
"net"
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
)
var log = logf.RuntimeLog.WithName("metrics")
// DefaultBindAddress sets the default bind address for the metrics listener
// The metrics is on by default.
var DefaultBindAddress = ":8080"
// NewListener creates a new TCP listener bound to the given address.
func NewListener(addr string) (net.Listener, error) {
if addr == "" {
// If the metrics bind address is empty, default to ":8080"
addr = DefaultBindAddress
}
// Add a case to disable metrics altogether
if addr == "0" {
return nil, nil
}
log.Info("Metrics server is starting to listen", "addr", addr)
ln, err := net.Listen("tcp", addr)
if err != nil {
er := fmt.Errorf("error listening on %s: %w", addr, err)
log.Error(er, "metrics server failed to listen. You may want to disable the metrics server or use another port if it is due to conflicts")
return nil, er
}
return ln, nil
}

View File

@ -0,0 +1,26 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Package server provides the metrics server implementation.
*/
package server
import (
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
)
var log = logf.RuntimeLog.WithName("metrics")

View File

@ -0,0 +1,312 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package server
import (
"context"
"crypto/tls"
"fmt"
"net"
"net/http"
"os"
"path/filepath"
"sync"
"time"
"github.com/go-logr/logr"
"github.com/prometheus/client_golang/prometheus/promhttp"
"k8s.io/client-go/rest"
certutil "k8s.io/client-go/util/cert"
"sigs.k8s.io/controller-runtime/pkg/certwatcher"
"sigs.k8s.io/controller-runtime/pkg/internal/httpserver"
"sigs.k8s.io/controller-runtime/pkg/metrics"
)
const (
defaultMetricsEndpoint = "/metrics"
)
// DefaultBindAddress is the default bind address for the metrics server.
var DefaultBindAddress = ":8080"
// Server is a server that serves metrics.
type Server interface {
// NeedLeaderElection implements the LeaderElectionRunnable interface, which indicates
// the metrics server doesn't need leader election.
NeedLeaderElection() bool
// Start runs the server.
// It will install the metrics related resources depending on the server configuration.
Start(ctx context.Context) error
}
// Options are all available options for the metrics.Server
type Options struct {
// SecureServing enables serving metrics via https.
// Per default metrics will be served via http.
SecureServing bool
// BindAddress is the bind address for the metrics server.
// It will be defaulted to ":8080" if unspecified.
// Set this to "0" to disable the metrics server.
BindAddress string
// ExtraHandlers contains a map of handlers (by path) which will be added to the metrics server.
// This might be useful to register diagnostic endpoints e.g. pprof.
// Note that pprof endpoints are meant to be sensitive and shouldn't be exposed publicly.
// If the simple path -> handler mapping offered here is not enough, a new http
// server/listener should be added as Runnable to the manager via the Add method.
ExtraHandlers map[string]http.Handler
// FilterProvider provides a filter which is a func that is added around
// the metrics and the extra handlers on the metrics server.
// This can be e.g. used to enforce authentication and authorization on the handlers
// endpoint by setting this field to filters.WithAuthenticationAndAuthorization.
FilterProvider func(c *rest.Config, httpClient *http.Client) (Filter, error)
// CertDir is the directory that contains the server key and certificate. Defaults to
// <temp-dir>/k8s-metrics-server/serving-certs.
//
// Note: This option is only used when TLSOpts does not set GetCertificate.
// Note: If certificate or key doesn't exist a self-signed certificate will be used.
CertDir string
// CertName is the server certificate name. Defaults to tls.crt.
//
// Note: This option is only used when TLSOpts does not set GetCertificate.
// Note: If certificate or key doesn't exist a self-signed certificate will be used.
CertName string
// KeyName is the server key name. Defaults to tls.key.
//
// Note: This option is only used when TLSOpts does not set GetCertificate.
// Note: If certificate or key doesn't exist a self-signed certificate will be used.
KeyName string
// TLSOpts is used to allow configuring the TLS config used for the server.
// This also allows providing a certificate via GetCertificate.
TLSOpts []func(*tls.Config)
}
// Filter is a func that is added around metrics and extra handlers on the metrics server.
type Filter func(log logr.Logger, handler http.Handler) (http.Handler, error)
// NewServer constructs a new metrics.Server from the provided options.
func NewServer(o Options, config *rest.Config, httpClient *http.Client) (Server, error) {
o.setDefaults()
// Skip server creation if metrics are disabled.
if o.BindAddress == "0" {
return nil, nil
}
// Validate that ExtraHandlers is not overwriting the default /metrics endpoint.
if o.ExtraHandlers != nil {
if _, ok := o.ExtraHandlers[defaultMetricsEndpoint]; ok {
return nil, fmt.Errorf("overriding builtin %s endpoint is not allowed", defaultMetricsEndpoint)
}
}
// Create the metrics filter if a FilterProvider is set.
var metricsFilter Filter
if o.FilterProvider != nil {
var err error
metricsFilter, err = o.FilterProvider(config, httpClient)
if err != nil {
return nil, fmt.Errorf("filter provider failed to create filter for the metrics server: %w", err)
}
}
return &defaultServer{
metricsFilter: metricsFilter,
options: o,
}, nil
}
// defaultServer is the default implementation used for Server.
type defaultServer struct {
options Options
// metricsFilter is a filter which is added around
// the metrics and the extra handlers on the metrics server.
metricsFilter Filter
// mu protects access to the bindAddr field.
mu sync.RWMutex
// bindAddr is used to store the bindAddr after the listener has been created.
// This is used during testing to figure out the port that has been chosen randomly.
bindAddr string
}
// setDefaults does defaulting for the Server.
func (o *Options) setDefaults() {
if o.BindAddress == "" {
o.BindAddress = DefaultBindAddress
}
if len(o.CertDir) == 0 {
o.CertDir = filepath.Join(os.TempDir(), "k8s-metrics-server", "serving-certs")
}
if len(o.CertName) == 0 {
o.CertName = "tls.crt"
}
if len(o.KeyName) == 0 {
o.KeyName = "tls.key"
}
}
// NeedLeaderElection implements the LeaderElectionRunnable interface, which indicates
// the metrics server doesn't need leader election.
func (*defaultServer) NeedLeaderElection() bool {
return false
}
// Start runs the server.
// It will install the metrics related resources depend on the server configuration.
func (s *defaultServer) Start(ctx context.Context) error {
log.Info("Starting metrics server")
listener, err := s.createListener(ctx, log)
if err != nil {
return fmt.Errorf("failed to start metrics server: failed to create listener: %w", err)
}
// Storing bindAddr here so we can retrieve it during testing via GetBindAddr.
s.mu.Lock()
s.bindAddr = listener.Addr().String()
s.mu.Unlock()
mux := http.NewServeMux()
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{
ErrorHandling: promhttp.HTTPErrorOnError,
})
if s.metricsFilter != nil {
log := log.WithValues("path", defaultMetricsEndpoint)
var err error
handler, err = s.metricsFilter(log, handler)
if err != nil {
return fmt.Errorf("failed to start metrics server: failed to add metrics filter: %w", err)
}
}
// TODO(JoelSpeed): Use existing Kubernetes machinery for serving metrics
mux.Handle(defaultMetricsEndpoint, handler)
for path, extraHandler := range s.options.ExtraHandlers {
if s.metricsFilter != nil {
log := log.WithValues("path", path)
var err error
extraHandler, err = s.metricsFilter(log, extraHandler)
if err != nil {
return fmt.Errorf("failed to start metrics server: failed to add metrics filter to extra handler for path %s: %w", path, err)
}
}
mux.Handle(path, extraHandler)
}
log.Info("Serving metrics server", "bindAddress", s.options.BindAddress, "secure", s.options.SecureServing)
srv := httpserver.New(mux)
idleConnsClosed := make(chan struct{})
go func() {
<-ctx.Done()
log.Info("Shutting down metrics server with timeout of 1 minute")
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
// Error from closing listeners, or context timeout
log.Error(err, "error shutting down the HTTP server")
}
close(idleConnsClosed)
}()
if err := srv.Serve(listener); err != nil && err != http.ErrServerClosed {
return err
}
<-idleConnsClosed
return nil
}
func (s *defaultServer) createListener(ctx context.Context, log logr.Logger) (net.Listener, error) {
if !s.options.SecureServing {
return net.Listen("tcp", s.options.BindAddress)
}
cfg := &tls.Config{ //nolint:gosec
NextProtos: []string{"h2"},
}
// fallback TLS config ready, will now mutate if passer wants full control over it
for _, op := range s.options.TLSOpts {
op(cfg)
}
if cfg.GetCertificate == nil {
certPath := filepath.Join(s.options.CertDir, s.options.CertName)
keyPath := filepath.Join(s.options.CertDir, s.options.KeyName)
_, certErr := os.Stat(certPath)
certExists := !os.IsNotExist(certErr)
_, keyErr := os.Stat(keyPath)
keyExists := !os.IsNotExist(keyErr)
if certExists && keyExists {
// Create the certificate watcher and
// set the config's GetCertificate on the TLSConfig
certWatcher, err := certwatcher.New(certPath, keyPath)
if err != nil {
return nil, err
}
cfg.GetCertificate = certWatcher.GetCertificate
go func() {
if err := certWatcher.Start(ctx); err != nil {
log.Error(err, "certificate watcher error")
}
}()
}
}
// If cfg.GetCertificate is still nil, i.e. we didn't configure a cert watcher, fallback to a self-signed certificate.
if cfg.GetCertificate == nil {
// Note: Using self-signed certificates here should be good enough. It's just important that we
// encrypt the communication. For example kube-controller-manager also uses a self-signed certificate
// for the metrics endpoint per default.
cert, key, err := certutil.GenerateSelfSignedCertKeyWithFixtures("localhost", []net.IP{{127, 0, 0, 1}}, nil, "")
if err != nil {
return nil, fmt.Errorf("failed to generate self-signed certificate for metrics server: %w", err)
}
keyPair, err := tls.X509KeyPair(cert, key)
if err != nil {
return nil, fmt.Errorf("failed to create self-signed key pair for metrics server: %w", err)
}
cfg.Certificates = []tls.Certificate{keyPair}
}
return tls.Listen("tcp", s.options.BindAddress, cfg)
}
func (s *defaultServer) GetBindAddr() string {
s.mu.RLock()
defer s.mu.RUnlock()
return s.bindAddr
}

View File

@ -89,8 +89,16 @@ instead the reconcile function observes this when reading the cluster state and
*/
type Reconciler interface {
// Reconcile performs a full reconciliation for the object referred to by the Request.
// The Controller will requeue the Request to be processed again if an error is non-nil or
// Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
//
// If the returned error is non-nil, the Result is ignored and the request will be
// requeued using exponential backoff. The only exception is if the error is a
// TerminalError in which case no requeuing happens.
//
// If the error is nil and the returned Result has a non-zero result.RequeueAfter, the request
// will be requeued after the specified duration.
//
// If the error is nil and result.RequeueAfter is zero and result.Reque is true, the request
// will be requeued using exponential backoff.
Reconcile(context.Context, Request) (Result, error)
}
@ -112,11 +120,15 @@ type terminalError struct {
err error
}
// This function will return nil if te.err is nil.
func (te *terminalError) Unwrap() error {
return te.err
}
func (te *terminalError) Error() string {
if te.err == nil {
return "nil terminal error"
}
return "terminal error: " + te.err.Error()
}

View File

@ -71,6 +71,7 @@ func (d *Decoder) DecodeRaw(rawObj runtime.RawExtension, into runtime.Object) er
return err
}
unstructuredInto.SetUnstructuredContent(object)
return nil
}
deserializer := d.codecs.UniversalDeserializer()

View File

@ -93,7 +93,7 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
wh.writeResponse(w, reviewResponse)
return
}
wh.getLogger(&req).V(4).Info("received request")
wh.getLogger(&req).V(5).Info("received request")
reviewResponse = wh.Handle(ctx, req)
wh.writeResponseTyped(w, reviewResponse, actualAdmRevGVK)
@ -136,11 +136,11 @@ func (wh *Webhook) writeAdmissionResponse(w io.Writer, ar v1.AdmissionReview) {
}
} else {
res := ar.Response
if log := wh.getLogger(nil); log.V(4).Enabled() {
if log := wh.getLogger(nil); log.V(5).Enabled() {
if res.Result != nil {
log = log.WithValues("code", res.Result.Code, "reason", res.Result.Reason, "message", res.Result.Message)
}
log.V(4).Info("wrote response", "requestID", res.UID, "allowed", res.Allowed)
log.V(5).Info("wrote response", "requestID", res.UID, "allowed", res.Allowed)
}
}
}

View File

@ -77,37 +77,33 @@ type Options struct {
// It will be defaulted to 9443 if unspecified.
Port int
// CertDir is the directory that contains the server key and certificate. The
// server key and certificate.
// CertDir is the directory that contains the server key and certificate. Defaults to
// <temp-dir>/k8s-webhook-server/serving-certs.
CertDir string
// CertName is the server certificate name. Defaults to tls.crt.
//
// Note: This option should only be set when TLSOpts does not override GetCertificate.
// Note: This option is only used when TLSOpts does not set GetCertificate.
CertName string
// KeyName is the server key name. Defaults to tls.key.
//
// Note: This option should only be set when TLSOpts does not override GetCertificate.
// Note: This option is only used when TLSOpts does not set GetCertificate.
KeyName string
// ClientCAName is the CA certificate name which server used to verify remote(client)'s certificate.
// Defaults to "", which means server does not verify client's certificate.
ClientCAName string
// TLSVersion is the minimum version of TLS supported. Accepts
// "", "1.0", "1.1", "1.2" and "1.3" only ("" is equivalent to "1.0" for backwards compatibility)
// Deprecated: Use TLSOpts instead.
TLSMinVersion string
// TLSOpts is used to allow configuring the TLS config used for the server
// TLSOpts is used to allow configuring the TLS config used for the server.
// This also allows providing a certificate via GetCertificate.
TLSOpts []func(*tls.Config)
// WebhookMux is the multiplexer that handles different webhooks.
WebhookMux *http.ServeMux
}
// NewServer constructs a new Server from the provided options.
// NewServer constructs a new webhook.Server from the provided options.
func NewServer(o Options) Server {
return &DefaultServer{
Options: o,
@ -187,42 +183,15 @@ func (s *DefaultServer) Register(path string, hook http.Handler) {
regLog.Info("Registering webhook")
}
// tlsVersion converts from human-readable TLS version (for example "1.1")
// to the values accepted by tls.Config (for example 0x301).
func tlsVersion(version string) (uint16, error) {
switch version {
// default is previous behaviour
case "":
return tls.VersionTLS10, nil
case "1.0":
return tls.VersionTLS10, nil
case "1.1":
return tls.VersionTLS11, nil
case "1.2":
return tls.VersionTLS12, nil
case "1.3":
return tls.VersionTLS13, nil
default:
return 0, fmt.Errorf("invalid TLSMinVersion %v: expects 1.0, 1.1, 1.2, 1.3 or empty", version)
}
}
// Start runs the server.
// It will install the webhook related resources depend on the server configuration.
func (s *DefaultServer) Start(ctx context.Context) error {
s.defaultingOnce.Do(s.setDefaults)
baseHookLog := log.WithName("webhooks")
baseHookLog.Info("Starting webhook server")
tlsMinVersion, err := tlsVersion(s.Options.TLSMinVersion)
if err != nil {
return err
}
log.Info("Starting webhook server")
cfg := &tls.Config{ //nolint:gosec
NextProtos: []string{"h2"},
MinVersion: tlsMinVersion,
}
// fallback TLS config ready, will now mutate if passer wants full control over it
for _, op := range s.Options.TLSOpts {