diff --git a/.github/workflows/benchmark-action.yaml b/.github/workflows/benchmark-action.yaml index b88bfe0bf..ff9f87f35 100644 --- a/.github/workflows/benchmark-action.yaml +++ b/.github/workflows/benchmark-action.yaml @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@v3.3.0 - uses: actions/setup-go@v3 # default version of go is 1.10 with: - go-version: 1.20.10 + go-version: 1.20.11 - name: Install Carvel Tools run: ./hack/install-deps.sh # Run benchmark with `go test -bench` and stores the output to a file diff --git a/.github/workflows/dependency-updater.yml b/.github/workflows/dependency-updater.yml index 256ed12c1..1c27e7555 100644 --- a/.github/workflows/dependency-updater.yml +++ b/.github/workflows/dependency-updater.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v3 with: - go-version: 1.20.10 + go-version: 1.20.11 - name: Update Dependencies File run: go run ./hack/dependencies.go update - name: Create Pull Request diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index b1aa6c02e..217590738 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -21,7 +21,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v3 with: - go-version: 1.20.10 + go-version: 1.20.11 - uses: actions/checkout@v3.5.2 with: fetch-depth: '0' diff --git a/.github/workflows/release-process.yml b/.github/workflows/release-process.yml index c52533f8d..196c73af0 100644 --- a/.github/workflows/release-process.yml +++ b/.github/workflows/release-process.yml @@ -35,7 +35,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v3 with: - go-version: 1.20.10 + go-version: 1.20.11 - name: Run release script run: | diff --git a/.github/workflows/test-gh.yml b/.github/workflows/test-gh.yml index 0965377a4..9548f47f8 100644 --- a/.github/workflows/test-gh.yml +++ b/.github/workflows/test-gh.yml @@ -26,7 +26,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v3 with: - go-version: 1.20.10 + go-version: 1.20.11 - name: Check out code uses: actions/checkout@v3.3.0 with: diff --git a/.github/workflows/test-kctrl-gh.yml b/.github/workflows/test-kctrl-gh.yml index 65a565927..5d53c6051 100644 --- a/.github/workflows/test-kctrl-gh.yml +++ b/.github/workflows/test-kctrl-gh.yml @@ -24,7 +24,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v3 with: - go-version: 1.20.10 + go-version: 1.20.11 - name: Check out code uses: actions/checkout@v3.3.0 with: diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index 9a6652a32..2e905c3c6 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -70,7 +70,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v3 with: - go-version: 1.20.10 + go-version: 1.20.11 - name: Build the kapp-controller artifacts run: | ./hack/install-deps.sh diff --git a/.github/workflows/upgrade-testing.yml b/.github/workflows/upgrade-testing.yml index 0f3d7d27f..e879c09ad 100644 --- a/.github/workflows/upgrade-testing.yml +++ b/.github/workflows/upgrade-testing.yml @@ -26,7 +26,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v3 with: - go-version: 1.20.10 + go-version: 1.20.11 - name: Check out code uses: actions/checkout@v3.3.0 - name: Install Carvel Tools diff --git a/Dockerfile b/Dockerfile index a25c3cc05..ed796128b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM golang:1.20.10 AS deps +FROM --platform=$BUILDPLATFORM golang:1.20.11 AS deps ARG TARGETOS TARGETARCH KCTRL_VER=development WORKDIR /workspace diff --git a/cli/go.mod b/cli/go.mod index d2fbd8042..24c920060 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -13,15 +13,15 @@ require ( github.com/otiai10/copy v1.0.2 github.com/spf13/cobra v1.4.0 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.7.5 + github.com/stretchr/testify v1.8.0 github.com/vmware-tanzu/carvel-kapp-controller v0.39.0 github.com/vmware-tanzu/carvel-vendir v0.24.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.25.0 - k8s.io/apiextensions-apiserver v0.25.0 - k8s.io/apimachinery v0.25.0 - k8s.io/client-go v0.25.0 + k8s.io/api v0.25.13 + k8s.io/apiextensions-apiserver v0.25.13 + k8s.io/apimachinery v0.25.13 + k8s.io/client-go v0.25.13 sigs.k8s.io/controller-runtime v0.13.0 sigs.k8s.io/yaml v1.3.0 ) @@ -57,7 +57,7 @@ require ( github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/go-cmp v0.5.8 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/hashicorp/go-version v1.3.0 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect @@ -92,7 +92,7 @@ require ( google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - k8s.io/component-base v0.25.0 // indirect + k8s.io/component-base v0.25.13 // indirect k8s.io/klog/v2 v2.70.1 // indirect k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect diff --git a/cli/go.sum b/cli/go.sum index 8d7b88263..075ced17a 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -461,8 +461,9 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -631,14 +632,14 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= +github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= +github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -772,8 +773,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q= -github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -832,7 +833,7 @@ go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -1356,28 +1357,28 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.25.0 h1:H+Q4ma2U/ww0iGB78ijZx6DRByPz6/733jIuFpX70e0= -k8s.io/api v0.25.0/go.mod h1:ttceV1GyV1i1rnmvzT3BST08N6nGt+dudGrquzVQWPk= -k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= -k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= +k8s.io/api v0.25.13 h1:nOQWK5/ngLIG2CqmVV7uTFDsPCGkDk4kIGJ26t2AwIo= +k8s.io/api v0.25.13/go.mod h1:yGpHyrivZ0enqWqT5s1pN98a4Q834rZkIUEABpleEtw= +k8s.io/apiextensions-apiserver v0.25.13 h1:tdB/ULSvlbZIecU2bDOoCb46Ozk5VyLL6IPysC8gUpE= +k8s.io/apiextensions-apiserver v0.25.13/go.mod h1:uqYfnY2GOYXlfpnT6No9dxrxDbMjaYvh/Bpm0iNMYnA= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU= -k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0= +k8s.io/apimachinery v0.25.13 h1:byRHkSinOOVdo0pvjdblauFYfwAnx+JB8Pqi9w9weik= +k8s.io/apimachinery v0.25.13/go.mod h1:IFwbcNi3gKkfDhuy0VYu3+BwbxbiIov3p6FR8ge1Epc= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E= -k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8= +k8s.io/client-go v0.25.13 h1:Wan/8RXVNxSgFI/wMfWwJjmLglRYuLItytMWNiGo9LY= +k8s.io/client-go v0.25.13/go.mod h1:b2on3RSCwHdmvnUQx4/bkgMAs19M7BlUDze3WJuK0TE= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= -k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y= -k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk= +k8s.io/component-base v0.25.13 h1:Lxpq2nu7Q4SueubBlU8kEcTgdaNXsktYg9bLsqvAV+4= +k8s.io/component-base v0.25.13/go.mod h1:Fh9SqG+Uyumtc+7HNT2FBZCxksIIFaukoL2LjqbBtgY= k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= diff --git a/cli/vendor/github.com/google/uuid/null.go b/cli/vendor/github.com/google/uuid/null.go new file mode 100644 index 000000000..d7fcbf286 --- /dev/null +++ b/cli/vendor/github.com/google/uuid/null.go @@ -0,0 +1,118 @@ +// Copyright 2021 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "database/sql/driver" + "encoding/json" + "fmt" +) + +var jsonNull = []byte("null") + +// NullUUID represents a UUID that may be null. +// NullUUID implements the SQL driver.Scanner interface so +// it can be used as a scan destination: +// +// var u uuid.NullUUID +// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u) +// ... +// if u.Valid { +// // use u.UUID +// } else { +// // NULL value +// } +// +type NullUUID struct { + UUID UUID + Valid bool // Valid is true if UUID is not NULL +} + +// Scan implements the SQL driver.Scanner interface. +func (nu *NullUUID) Scan(value interface{}) error { + if value == nil { + nu.UUID, nu.Valid = Nil, false + return nil + } + + err := nu.UUID.Scan(value) + if err != nil { + nu.Valid = false + return err + } + + nu.Valid = true + return nil +} + +// Value implements the driver Valuer interface. +func (nu NullUUID) Value() (driver.Value, error) { + if !nu.Valid { + return nil, nil + } + // Delegate to UUID Value function + return nu.UUID.Value() +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (nu NullUUID) MarshalBinary() ([]byte, error) { + if nu.Valid { + return nu.UUID[:], nil + } + + return []byte(nil), nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (nu *NullUUID) UnmarshalBinary(data []byte) error { + if len(data) != 16 { + return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) + } + copy(nu.UUID[:], data) + nu.Valid = true + return nil +} + +// MarshalText implements encoding.TextMarshaler. +func (nu NullUUID) MarshalText() ([]byte, error) { + if nu.Valid { + return nu.UUID.MarshalText() + } + + return jsonNull, nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (nu *NullUUID) UnmarshalText(data []byte) error { + id, err := ParseBytes(data) + if err != nil { + nu.Valid = false + return err + } + nu.UUID = id + nu.Valid = true + return nil +} + +// MarshalJSON implements json.Marshaler. +func (nu NullUUID) MarshalJSON() ([]byte, error) { + if nu.Valid { + return json.Marshal(nu.UUID) + } + + return jsonNull, nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (nu *NullUUID) UnmarshalJSON(data []byte) error { + if bytes.Equal(data, jsonNull) { + *nu = NullUUID{} + return nil // valid null UUID + } + err := json.Unmarshal(data, &nu.UUID) + nu.Valid = err == nil + return err +} diff --git a/cli/vendor/github.com/google/uuid/uuid.go b/cli/vendor/github.com/google/uuid/uuid.go index 60d26bb50..a57207aeb 100644 --- a/cli/vendor/github.com/google/uuid/uuid.go +++ b/cli/vendor/github.com/google/uuid/uuid.go @@ -12,6 +12,7 @@ import ( "fmt" "io" "strings" + "sync" ) // A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC @@ -33,7 +34,15 @@ const ( Future // Reserved for future definition. ) -var rander = rand.Reader // random function +const randPoolSize = 16 * 16 + +var ( + rander = rand.Reader // random function + poolEnabled = false + poolMu sync.Mutex + poolPos = randPoolSize // protected with poolMu + pool [randPoolSize]byte // protected with poolMu +) type invalidLengthError struct{ len int } @@ -41,6 +50,12 @@ func (err invalidLengthError) Error() string { return fmt.Sprintf("invalid UUID length: %d", err.len) } +// IsInvalidLengthError is matcher function for custom error invalidLengthError +func IsInvalidLengthError(err error) bool { + _, ok := err.(invalidLengthError) + return ok +} + // Parse decodes s into a UUID or returns an error. Both the standard UUID // forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the @@ -249,3 +264,31 @@ func SetRand(r io.Reader) { } rander = r } + +// EnableRandPool enables internal randomness pool used for Random +// (Version 4) UUID generation. The pool contains random bytes read from +// the random number generator on demand in batches. Enabling the pool +// may improve the UUID generation throughput significantly. +// +// Since the pool is stored on the Go heap, this feature may be a bad fit +// for security sensitive applications. +// +// Both EnableRandPool and DisableRandPool are not thread-safe and should +// only be called when there is no possibility that New or any other +// UUID Version 4 generation function will be called concurrently. +func EnableRandPool() { + poolEnabled = true +} + +// DisableRandPool disables the randomness pool if it was previously +// enabled with EnableRandPool. +// +// Both EnableRandPool and DisableRandPool are not thread-safe and should +// only be called when there is no possibility that New or any other +// UUID Version 4 generation function will be called concurrently. +func DisableRandPool() { + poolEnabled = false + defer poolMu.Unlock() + poolMu.Lock() + poolPos = randPoolSize +} diff --git a/cli/vendor/github.com/google/uuid/version4.go b/cli/vendor/github.com/google/uuid/version4.go index 86160fbd0..7697802e4 100644 --- a/cli/vendor/github.com/google/uuid/version4.go +++ b/cli/vendor/github.com/google/uuid/version4.go @@ -27,6 +27,8 @@ func NewString() string { // The strength of the UUIDs is based on the strength of the crypto/rand // package. // +// Uses the randomness pool if it was enabled with EnableRandPool. +// // A note about uniqueness derived from the UUID Wikipedia entry: // // Randomly generated UUIDs have 122 random bits. One's annual risk of being @@ -35,7 +37,10 @@ func NewString() string { // equivalent to the odds of creating a few tens of trillions of UUIDs in a // year and having one duplicate. func NewRandom() (UUID, error) { - return NewRandomFromReader(rander) + if !poolEnabled { + return NewRandomFromReader(rander) + } + return newRandomFromPool() } // NewRandomFromReader returns a UUID based on bytes read from a given io.Reader. @@ -49,3 +54,23 @@ func NewRandomFromReader(r io.Reader) (UUID, error) { uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 return uuid, nil } + +func newRandomFromPool() (UUID, error) { + var uuid UUID + poolMu.Lock() + if poolPos == randPoolSize { + _, err := io.ReadFull(rander, pool[:]) + if err != nil { + poolMu.Unlock() + return Nil, err + } + poolPos = 0 + } + copy(uuid[:], pool[poolPos:(poolPos+16)]) + poolPos += 16 + poolMu.Unlock() + + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 + return uuid, nil +} diff --git a/cli/vendor/github.com/stretchr/testify/assert/assertion_format.go b/cli/vendor/github.com/stretchr/testify/assert/assertion_format.go index 27e2420ed..7880b8f94 100644 --- a/cli/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/cli/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -736,6 +736,16 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...) } +// WithinRangef asserts that a time is within a time range (inclusive). +// +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return WithinRange(t, actual, start, end, append([]interface{}{msg}, args...)...) +} + // YAMLEqf asserts that two YAML strings are equivalent. func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { diff --git a/cli/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/cli/vendor/github.com/stretchr/testify/assert/assertion_forward.go index d9ea368d0..339515b8b 100644 --- a/cli/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/cli/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -1461,6 +1461,26 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta return WithinDurationf(a.t, expected, actual, delta, msg, args...) } +// WithinRange asserts that a time is within a time range (inclusive). +// +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return WithinRange(a.t, actual, start, end, msgAndArgs...) +} + +// WithinRangef asserts that a time is within a time range (inclusive). +// +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return WithinRangef(a.t, actual, start, end, msg, args...) +} + // YAMLEq asserts that two YAML strings are equivalent. func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { diff --git a/cli/vendor/github.com/stretchr/testify/assert/assertions.go b/cli/vendor/github.com/stretchr/testify/assert/assertions.go index 580fdea4c..fa1245b18 100644 --- a/cli/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/cli/vendor/github.com/stretchr/testify/assert/assertions.go @@ -8,6 +8,7 @@ import ( "fmt" "math" "os" + "path/filepath" "reflect" "regexp" "runtime" @@ -144,7 +145,8 @@ func CallerInfo() []string { if len(parts) > 1 { dir := parts[len(parts)-2] if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" { - callers = append(callers, fmt.Sprintf("%s:%d", file, line)) + path, _ := filepath.Abs(file) + callers = append(callers, fmt.Sprintf("%s:%d", path, line)) } } @@ -816,7 +818,6 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok return true // we consider nil to be equal to the nil set } - subsetValue := reflect.ValueOf(subset) defer func() { if e := recover(); e != nil { ok = false @@ -826,14 +827,32 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok listKind := reflect.TypeOf(list).Kind() subsetKind := reflect.TypeOf(subset).Kind() - if listKind != reflect.Array && listKind != reflect.Slice { + if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) } - if subsetKind != reflect.Array && subsetKind != reflect.Slice { + if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } + subsetValue := reflect.ValueOf(subset) + if subsetKind == reflect.Map && listKind == reflect.Map { + listValue := reflect.ValueOf(list) + subsetKeys := subsetValue.MapKeys() + + for i := 0; i < len(subsetKeys); i++ { + subsetKey := subsetKeys[i] + subsetElement := subsetValue.MapIndex(subsetKey).Interface() + listElement := listValue.MapIndex(subsetKey).Interface() + + if !ObjectsAreEqual(subsetElement, listElement) { + return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, subsetElement), msgAndArgs...) + } + } + + return true + } + for i := 0; i < subsetValue.Len(); i++ { element := subsetValue.Index(i).Interface() ok, found := containsElement(list, element) @@ -860,7 +879,6 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...) } - subsetValue := reflect.ValueOf(subset) defer func() { if e := recover(); e != nil { ok = false @@ -870,14 +888,32 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) listKind := reflect.TypeOf(list).Kind() subsetKind := reflect.TypeOf(subset).Kind() - if listKind != reflect.Array && listKind != reflect.Slice { + if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) } - if subsetKind != reflect.Array && subsetKind != reflect.Slice { + if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } + subsetValue := reflect.ValueOf(subset) + if subsetKind == reflect.Map && listKind == reflect.Map { + listValue := reflect.ValueOf(list) + subsetKeys := subsetValue.MapKeys() + + for i := 0; i < len(subsetKeys); i++ { + subsetKey := subsetKeys[i] + subsetElement := subsetValue.MapIndex(subsetKey).Interface() + listElement := listValue.MapIndex(subsetKey).Interface() + + if !ObjectsAreEqual(subsetElement, listElement) { + return true + } + } + + return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) + } + for i := 0; i < subsetValue.Len(); i++ { element := subsetValue.Index(i).Interface() ok, found := containsElement(list, element) @@ -1110,6 +1146,27 @@ func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, return true } +// WithinRange asserts that a time is within a time range (inclusive). +// +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func WithinRange(t TestingT, actual, start, end time.Time, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if end.Before(start) { + return Fail(t, "Start should be before end", msgAndArgs...) + } + + if actual.Before(start) { + return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is before the range", actual, start, end), msgAndArgs...) + } else if actual.After(end) { + return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is after the range", actual, start, end), msgAndArgs...) + } + + return true +} + func toFloat(x interface{}) (float64, bool) { var xf float64 xok := true diff --git a/cli/vendor/github.com/stretchr/testify/require/require.go b/cli/vendor/github.com/stretchr/testify/require/require.go index 59c48277a..880853f5a 100644 --- a/cli/vendor/github.com/stretchr/testify/require/require.go +++ b/cli/vendor/github.com/stretchr/testify/require/require.go @@ -1864,6 +1864,32 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim t.FailNow() } +// WithinRange asserts that a time is within a time range (inclusive). +// +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.WithinRange(t, actual, start, end, msgAndArgs...) { + return + } + t.FailNow() +} + +// WithinRangef asserts that a time is within a time range (inclusive). +// +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.WithinRangef(t, actual, start, end, msg, args...) { + return + } + t.FailNow() +} + // YAMLEq asserts that two YAML strings are equivalent. func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { diff --git a/cli/vendor/github.com/stretchr/testify/require/require_forward.go b/cli/vendor/github.com/stretchr/testify/require/require_forward.go index 5bb07c89c..960bf6f2c 100644 --- a/cli/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/cli/vendor/github.com/stretchr/testify/require/require_forward.go @@ -1462,6 +1462,26 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta WithinDurationf(a.t, expected, actual, delta, msg, args...) } +// WithinRange asserts that a time is within a time range (inclusive). +// +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + WithinRange(a.t, actual, start, end, msgAndArgs...) +} + +// WithinRangef asserts that a time is within a time range (inclusive). +// +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + WithinRangef(a.t, actual, start, end, msg, args...) +} + // YAMLEq asserts that two YAML strings are equivalent. func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { diff --git a/cli/vendor/k8s.io/api/core/v1/generated.proto b/cli/vendor/k8s.io/api/core/v1/generated.proto index 256f5da32..e4e562e32 100644 --- a/cli/vendor/k8s.io/api/core/v1/generated.proto +++ b/cli/vendor/k8s.io/api/core/v1/generated.proto @@ -1761,7 +1761,8 @@ message HTTPGetAction { // HTTPHeader describes a custom header to be used in HTTP probes message HTTPHeader { - // The header field name + // The header field name. + // This will be canonicalized upon output, so case-variant names will be understood as the same header. optional string name = 1; // The header field value diff --git a/cli/vendor/k8s.io/api/core/v1/types.go b/cli/vendor/k8s.io/api/core/v1/types.go index 754a23613..e60e180c2 100644 --- a/cli/vendor/k8s.io/api/core/v1/types.go +++ b/cli/vendor/k8s.io/api/core/v1/types.go @@ -2112,7 +2112,8 @@ type SecretEnvSource struct { // HTTPHeader describes a custom header to be used in HTTP probes type HTTPHeader struct { - // The header field name + // The header field name. + // This will be canonicalized upon output, so case-variant names will be understood as the same header. Name string `json:"name" protobuf:"bytes,1,opt,name=name"` // The header field value Value string `json:"value" protobuf:"bytes,2,opt,name=value"` @@ -4266,6 +4267,9 @@ const ( // LoadBalancerPortsError represents the condition of the requested ports // on the cloud load balancer instance. LoadBalancerPortsError = "LoadBalancerPortsError" + // LoadBalancerPortsErrorReason reason in ServiceStatus condition LoadBalancerPortsError + // means the LoadBalancer was not able to be configured correctly. + LoadBalancerPortsErrorReason = "LoadBalancerMixedProtocolNotSupported" ) // ServiceStatus represents the current status of a service. @@ -6624,6 +6628,13 @@ const ( PortForwardRequestIDHeader = "requestID" ) +const ( + // MixedProtocolNotSupported error in PortStatus means that the cloud provider + // can't publish the port on the load balancer because mixed values of protocols + // on the same LoadBalancer type of Service are not supported by the cloud provider. + MixedProtocolNotSupported = "MixedProtocolNotSupported" +) + // PortStatus represents the error condition of a service port type PortStatus struct { diff --git a/cli/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go b/cli/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go index 6bae4bb76..77707706a 100644 --- a/cli/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go +++ b/cli/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go @@ -808,7 +808,7 @@ func (HTTPGetAction) SwaggerDoc() map[string]string { var map_HTTPHeader = map[string]string{ "": "HTTPHeader describes a custom header to be used in HTTP probes", - "name": "The header field name", + "name": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.", "value": "The header field value", } diff --git a/cli/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/micro_time_proto.go b/cli/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/micro_time_proto.go index 6dd6d8999..ab68181e9 100644 --- a/cli/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/micro_time_proto.go +++ b/cli/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/micro_time_proto.go @@ -27,9 +27,12 @@ func (m *MicroTime) ProtoMicroTime() *Timestamp { if m == nil { return &Timestamp{} } + + // truncate precision to microseconds to match JSON marshaling/unmarshaling + truncatedNanoseconds := time.Duration(m.Time.Nanosecond()).Truncate(time.Microsecond) return &Timestamp{ Seconds: m.Time.Unix(), - Nanos: int32(m.Time.Nanosecond()), + Nanos: int32(truncatedNanoseconds), } } @@ -51,7 +54,10 @@ func (m *MicroTime) Unmarshal(data []byte) error { if err := p.Unmarshal(data); err != nil { return err } - m.Time = time.Unix(p.Seconds, int64(p.Nanos)).Local() + + // truncate precision to microseconds to match JSON marshaling/unmarshaling + truncatedNanoseconds := time.Duration(p.Nanos).Truncate(time.Microsecond) + m.Time = time.Unix(p.Seconds, int64(truncatedNanoseconds)).Local() return nil } diff --git a/cli/vendor/k8s.io/apimachinery/pkg/runtime/converter.go b/cli/vendor/k8s.io/apimachinery/pkg/runtime/converter.go index 90bf487e3..62eb27afc 100644 --- a/cli/vendor/k8s.io/apimachinery/pkg/runtime/converter.go +++ b/cli/vendor/k8s.io/apimachinery/pkg/runtime/converter.go @@ -231,7 +231,7 @@ func (c *fromUnstructuredContext) pushKey(key string) { } -// FromUnstructuredWIthValidation converts an object from map[string]interface{} representation into a concrete type. +// FromUnstructuredWithValidation converts an object from map[string]interface{} representation into a concrete type. // It uses encoding/json/Unmarshaler if object implements it or reflection if not. // It takes a validationDirective that indicates how to behave when it encounters unknown fields. func (c *unstructuredConverter) FromUnstructuredWithValidation(u map[string]interface{}, obj interface{}, returnUnknownFields bool) error { @@ -465,7 +465,7 @@ func sliceFromUnstructured(sv, dv reflect.Value, ctx *fromUnstructuredContext) e } dv.SetBytes(data) } else { - dv.Set(reflect.Zero(dt)) + dv.Set(reflect.MakeSlice(dt, 0, 0)) } return nil } diff --git a/cli/vendor/k8s.io/client-go/plugin/pkg/client/auth/exec/exec.go b/cli/vendor/k8s.io/client-go/plugin/pkg/client/auth/exec/exec.go index d37dfbf73..73876f688 100644 --- a/cli/vendor/k8s.io/client-go/plugin/pkg/client/auth/exec/exec.go +++ b/cli/vendor/k8s.io/client-go/plugin/pkg/client/auth/exec/exec.go @@ -199,14 +199,18 @@ func newAuthenticator(c *cache, isTerminalFunc func(int) bool, config *api.ExecC now: time.Now, environ: os.Environ, - defaultDialer: defaultDialer, - connTracker: connTracker, + connTracker: connTracker, } for _, env := range config.Env { a.env = append(a.env, env.Name+"="+env.Value) } + // these functions are made comparable and stored in the cache so that repeated clientset + // construction with the same rest.Config results in a single TLS cache and Authenticator + a.getCert = &transport.GetCertHolder{GetCert: a.cert} + a.dial = &transport.DialHolder{Dial: defaultDialer.DialContext} + return c.put(key, a), nil } @@ -261,8 +265,6 @@ type Authenticator struct { now func() time.Time environ func() []string - // defaultDialer is used for clients which don't specify a custom dialer - defaultDialer *connrotation.Dialer // connTracker tracks all connections opened that we need to close when rotating a client certificate connTracker *connrotation.ConnectionTracker @@ -273,6 +275,12 @@ type Authenticator struct { mu sync.Mutex cachedCreds *credentials exp time.Time + + // getCert makes Authenticator.cert comparable to support TLS config caching + getCert *transport.GetCertHolder + // dial is used for clients which do not specify a custom dialer + // it is comparable to support TLS config caching + dial *transport.DialHolder } type credentials struct { @@ -300,18 +308,20 @@ func (a *Authenticator) UpdateTransportConfig(c *transport.Config) error { if c.HasCertCallback() { return errors.New("can't add TLS certificate callback: transport.Config.TLS.GetCert already set") } - c.TLS.GetCert = a.cert + c.TLS.GetCert = a.getCert.GetCert + c.TLS.GetCertHolder = a.getCert // comparable for TLS config caching - var d *connrotation.Dialer if c.Dial != nil { // if c has a custom dialer, we have to wrap it - d = connrotation.NewDialerWithTracker(c.Dial, a.connTracker) + // TLS config caching is not supported for this config + d := connrotation.NewDialerWithTracker(c.Dial, a.connTracker) + c.Dial = d.DialContext + c.DialHolder = nil } else { - d = a.defaultDialer + c.Dial = a.dial.Dial + c.DialHolder = a.dial // comparable for TLS config caching } - c.Dial = d.DialContext - return nil } diff --git a/cli/vendor/k8s.io/client-go/rest/request.go b/cli/vendor/k8s.io/client-go/rest/request.go index dba933f7d..a1f0a591d 100644 --- a/cli/vendor/k8s.io/client-go/rest/request.go +++ b/cli/vendor/k8s.io/client-go/rest/request.go @@ -34,6 +34,7 @@ import ( "time" "golang.org/x/net/http2" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -116,8 +117,11 @@ type Request struct { subresource string // output - err error - body io.Reader + err error + + // only one of body / bodyBytes may be set. requests using body are not retriable. + body io.Reader + bodyBytes []byte retryFn requestRetryFunc } @@ -443,12 +447,15 @@ func (r *Request) Body(obj interface{}) *Request { return r } glogBody("Request Body", data) - r.body = bytes.NewReader(data) + r.body = nil + r.bodyBytes = data case []byte: glogBody("Request Body", t) - r.body = bytes.NewReader(t) + r.body = nil + r.bodyBytes = t case io.Reader: r.body = t + r.bodyBytes = nil case runtime.Object: // callers may pass typed interface pointers, therefore we must check nil with reflection if reflect.ValueOf(t).IsNil() { @@ -465,7 +472,8 @@ func (r *Request) Body(obj interface{}) *Request { return r } glogBody("Request Body", data) - r.body = bytes.NewReader(data) + r.body = nil + r.bodyBytes = data r.SetHeader("Content-Type", r.c.content.ContentType) default: r.err = fmt.Errorf("unknown type used for body: %+v", obj) @@ -508,6 +516,87 @@ func (r *Request) URL() *url.URL { return finalURL } +// finalURLTemplate is similar to URL(), but will make all specific parameter values equal +// - instead of name or namespace, "{name}" and "{namespace}" will be used, and all query +// parameters will be reset. This creates a copy of the url so as not to change the +// underlying object. +func (r Request) finalURLTemplate() url.URL { + newParams := url.Values{} + v := []string{"{value}"} + for k := range r.params { + newParams[k] = v + } + r.params = newParams + u := r.URL() + if u == nil { + return url.URL{} + } + + segments := strings.Split(u.Path, "/") + groupIndex := 0 + index := 0 + trimmedBasePath := "" + if r.c.base != nil && strings.Contains(u.Path, r.c.base.Path) { + p := strings.TrimPrefix(u.Path, r.c.base.Path) + if !strings.HasPrefix(p, "/") { + p = "/" + p + } + // store the base path that we have trimmed so we can append it + // before returning the URL + trimmedBasePath = r.c.base.Path + segments = strings.Split(p, "/") + groupIndex = 1 + } + if len(segments) <= 2 { + return *u + } + + const CoreGroupPrefix = "api" + const NamedGroupPrefix = "apis" + isCoreGroup := segments[groupIndex] == CoreGroupPrefix + isNamedGroup := segments[groupIndex] == NamedGroupPrefix + if isCoreGroup { + // checking the case of core group with /api/v1/... format + index = groupIndex + 2 + } else if isNamedGroup { + // checking the case of named group with /apis/apps/v1/... format + index = groupIndex + 3 + } else { + // this should not happen that the only two possibilities are /api... and /apis..., just want to put an + // outlet here in case more API groups are added in future if ever possible: + // https://kubernetes.io/docs/concepts/overview/kubernetes-api/#api-groups + // if a wrong API groups name is encountered, return the {prefix} for url.Path + u.Path = "/{prefix}" + u.RawQuery = "" + return *u + } + // switch segLength := len(segments) - index; segLength { + switch { + // case len(segments) - index == 1: + // resource (with no name) do nothing + case len(segments)-index == 2: + // /$RESOURCE/$NAME: replace $NAME with {name} + segments[index+1] = "{name}" + case len(segments)-index == 3: + if segments[index+2] == "finalize" || segments[index+2] == "status" { + // /$RESOURCE/$NAME/$SUBRESOURCE: replace $NAME with {name} + segments[index+1] = "{name}" + } else { + // /namespace/$NAMESPACE/$RESOURCE: replace $NAMESPACE with {namespace} + segments[index+1] = "{namespace}" + } + case len(segments)-index >= 4: + segments[index+1] = "{namespace}" + // /namespace/$NAMESPACE/$RESOURCE/$NAME: replace $NAMESPACE with {namespace}, $NAME with {name} + if segments[index+3] != "finalize" && segments[index+3] != "status" { + // /$RESOURCE/$NAME/$SUBRESOURCE: replace $NAME with {name} + segments[index+3] = "{name}" + } + } + u.Path = path.Join(trimmedBasePath, path.Join(segments...)) + return *u +} + func (r *Request) tryThrottleWithInfo(ctx context.Context, retryInfo string) error { if r.rateLimiter == nil { return nil @@ -537,7 +626,7 @@ func (r *Request) tryThrottleWithInfo(ctx context.Context, retryInfo string) err // but we use a throttled logger to prevent spamming. globalThrottledLogger.Infof("%s", message) } - metrics.RateLimiterLatency.Observe(ctx, r.verb, *r.URL(), latency) + metrics.RateLimiterLatency.Observe(ctx, r.verb, r.finalURLTemplate(), latency) return err } @@ -744,9 +833,7 @@ func (r *Request) Stream(ctx context.Context) (io.ReadCloser, error) { if err != nil { return nil, err } - if r.body != nil { - req.Body = ioutil.NopCloser(r.body) - } + resp, err := client.Do(req) updateURLMetrics(ctx, r, resp, err) retry.After(ctx, r, resp, err) @@ -808,8 +895,20 @@ func (r *Request) requestPreflightCheck() error { } func (r *Request) newHTTPRequest(ctx context.Context) (*http.Request, error) { + var body io.Reader + switch { + case r.body != nil && r.bodyBytes != nil: + return nil, fmt.Errorf("cannot set both body and bodyBytes") + case r.body != nil: + body = r.body + case r.bodyBytes != nil: + // Create a new reader specifically for this request. + // Giving each request a dedicated reader allows retries to avoid races resetting the request body. + body = bytes.NewReader(r.bodyBytes) + } + url := r.URL().String() - req, err := http.NewRequest(r.verb, url, r.body) + req, err := http.NewRequest(r.verb, url, body) if err != nil { return nil, err } @@ -826,7 +925,7 @@ func (r *Request) request(ctx context.Context, fn func(*http.Request, *http.Resp // Metrics for total request latency start := time.Now() defer func() { - metrics.RequestLatency.Observe(ctx, r.verb, *r.URL(), time.Since(start)) + metrics.RequestLatency.Observe(ctx, r.verb, r.finalURLTemplate(), time.Since(start)) }() if r.err != nil { diff --git a/cli/vendor/k8s.io/client-go/rest/with_retry.go b/cli/vendor/k8s.io/client-go/rest/with_retry.go index bdcc6f3a9..ab2694754 100644 --- a/cli/vendor/k8s.io/client-go/rest/with_retry.go +++ b/cli/vendor/k8s.io/client-go/rest/with_retry.go @@ -154,6 +154,11 @@ func (r *withRetry) IsNextRetry(ctx context.Context, restReq *Request, httpReq * return false } + if restReq.body != nil { + // we have an opaque reader, we can't safely reset it + return false + } + r.attempts++ r.retryAfter = &RetryAfter{Attempt: r.attempts} if r.attempts > r.maxRetries { @@ -210,18 +215,6 @@ func (r *withRetry) Before(ctx context.Context, request *Request) error { return nil } - // At this point we've made atleast one attempt, post which the response - // body should have been fully read and closed in order for it to be safe - // to reset the request body before we reconnect, in order for us to reuse - // the same TCP connection. - if seeker, ok := request.body.(io.Seeker); ok && request.body != nil { - if _, err := seeker.Seek(0, io.SeekStart); err != nil { - err = fmt.Errorf("failed to reset the request body while retrying a request: %v", err) - r.trackPreviousError(err) - return err - } - } - // if we are here, we have made attempt(s) at least once before. if request.backoff != nil { delay := request.backoff.CalculateBackoff(url) diff --git a/cli/vendor/k8s.io/client-go/tools/cache/controller.go b/cli/vendor/k8s.io/client-go/tools/cache/controller.go index 0762da3be..96005ff58 100644 --- a/cli/vendor/k8s.io/client-go/tools/cache/controller.go +++ b/cli/vendor/k8s.io/client-go/tools/cache/controller.go @@ -353,17 +353,6 @@ func NewIndexerInformer( return clientState, newInformer(lw, objType, resyncPeriod, h, clientState, nil) } -// TransformFunc allows for transforming an object before it will be processed -// and put into the controller cache and before the corresponding handlers will -// be called on it. -// TransformFunc (similarly to ResourceEventHandler functions) should be able -// to correctly handle the tombstone of type cache.DeletedFinalStateUnknown -// -// The most common usage pattern is to clean-up some parts of the object to -// reduce component memory usage if a given component doesn't care about them. -// given controller doesn't care for them -type TransformFunc func(interface{}) (interface{}, error) - // NewTransformingInformer returns a Store and a controller for populating // the store while also providing event notifications. You should only used // the returned Store for Get/List operations; Add/Modify/Deletes will cause @@ -411,19 +400,11 @@ func processDeltas( // Object which receives event notifications from the given deltas handler ResourceEventHandler, clientState Store, - transformer TransformFunc, deltas Deltas, ) error { // from oldest to newest for _, d := range deltas { obj := d.Object - if transformer != nil { - var err error - obj, err = transformer(obj) - if err != nil { - return err - } - } switch d.Type { case Sync, Replaced, Added, Updated: @@ -475,6 +456,7 @@ func newInformer( fifo := NewDeltaFIFOWithOptions(DeltaFIFOOptions{ KnownObjects: clientState, EmitDeltaTypeReplaced: true, + Transformer: transformer, }) cfg := &Config{ @@ -486,7 +468,7 @@ func newInformer( Process: func(obj interface{}) error { if deltas, ok := obj.(Deltas); ok { - return processDeltas(h, clientState, transformer, deltas) + return processDeltas(h, clientState, deltas) } return errors.New("object given as Process argument is not Deltas") }, diff --git a/cli/vendor/k8s.io/client-go/tools/cache/delta_fifo.go b/cli/vendor/k8s.io/client-go/tools/cache/delta_fifo.go index 0c13a41f0..84f3ab9ca 100644 --- a/cli/vendor/k8s.io/client-go/tools/cache/delta_fifo.go +++ b/cli/vendor/k8s.io/client-go/tools/cache/delta_fifo.go @@ -51,6 +51,10 @@ type DeltaFIFOOptions struct { // When true, `Replaced` events will be sent for items passed to a Replace() call. // When false, `Sync` events will be sent instead. EmitDeltaTypeReplaced bool + + // If set, will be called for objects before enqueueing them. Please + // see the comment on TransformFunc for details. + Transformer TransformFunc } // DeltaFIFO is like FIFO, but differs in two ways. One is that the @@ -129,8 +133,32 @@ type DeltaFIFO struct { // emitDeltaTypeReplaced is whether to emit the Replaced or Sync // DeltaType when Replace() is called (to preserve backwards compat). emitDeltaTypeReplaced bool + + // Called with every object if non-nil. + transformer TransformFunc } +// TransformFunc allows for transforming an object before it will be processed. +// TransformFunc (similarly to ResourceEventHandler functions) should be able +// to correctly handle the tombstone of type cache.DeletedFinalStateUnknown. +// +// New in v1.27: In such cases, the contained object will already have gone +// through the transform object separately (when it was added / updated prior +// to the delete), so the TransformFunc can likely safely ignore such objects +// (i.e., just return the input object). +// +// The most common usage pattern is to clean-up some parts of the object to +// reduce component memory usage if a given component doesn't care about them. +// +// New in v1.27: unless the object is a DeletedFinalStateUnknown, TransformFunc +// sees the object before any other actor, and it is now safe to mutate the +// object in place instead of making a copy. +// +// Note that TransformFunc is called while inserting objects into the +// notification queue and is therefore extremely performance sensitive; please +// do not do anything that will take a long time. +type TransformFunc func(interface{}) (interface{}, error) + // DeltaType is the type of a change (addition, deletion, etc) type DeltaType string @@ -227,6 +255,7 @@ func NewDeltaFIFOWithOptions(opts DeltaFIFOOptions) *DeltaFIFO { knownObjects: opts.KnownObjects, emitDeltaTypeReplaced: opts.EmitDeltaTypeReplaced, + transformer: opts.Transformer, } f.cond.L = &f.lock return f @@ -411,6 +440,21 @@ func (f *DeltaFIFO) queueActionLocked(actionType DeltaType, obj interface{}) err if err != nil { return KeyError{obj, err} } + + // Every object comes through this code path once, so this is a good + // place to call the transform func. If obj is a + // DeletedFinalStateUnknown tombstone, then the containted inner object + // will already have gone through the transformer, but we document that + // this can happen. In cases involving Replace(), such an object can + // come through multiple times. + if f.transformer != nil { + var err error + obj, err = f.transformer(obj) + if err != nil { + return err + } + } + oldDeltas := f.items[id] newDeltas := append(oldDeltas, Delta{actionType, obj}) newDeltas = dedupDeltas(newDeltas) @@ -566,12 +610,11 @@ func (f *DeltaFIFO) Pop(process PopProcessFunc) (interface{}, error) { // using the Sync or Replace DeltaType and then (2) it does some deletions. // In particular: for every pre-existing key K that is not the key of // an object in `list` there is the effect of -// `Delete(DeletedFinalStateUnknown{K, O})` where O is current object -// of K. If `f.knownObjects == nil` then the pre-existing keys are -// those in `f.items` and the current object of K is the `.Newest()` -// of the Deltas associated with K. Otherwise the pre-existing keys -// are those listed by `f.knownObjects` and the current object of K is -// what `f.knownObjects.GetByKey(K)` returns. +// `Delete(DeletedFinalStateUnknown{K, O})` where O is the latest known +// object of K. The pre-existing keys are those in the union set of the keys in +// `f.items` and `f.knownObjects` (if not nil). The last known object for key K is +// the one present in the last delta in `f.items`. If there is no delta for K +// in `f.items`, it is the object in `f.knownObjects` func (f *DeltaFIFO) Replace(list []interface{}, _ string) error { f.lock.Lock() defer f.lock.Unlock() @@ -595,51 +638,23 @@ func (f *DeltaFIFO) Replace(list []interface{}, _ string) error { } } - if f.knownObjects == nil { - // Do deletion detection against our own list. - queuedDeletions := 0 - for k, oldItem := range f.items { - if keys.Has(k) { - continue - } - // Delete pre-existing items not in the new list. - // This could happen if watch deletion event was missed while - // disconnected from apiserver. - var deletedObj interface{} - if n := oldItem.Newest(); n != nil { - deletedObj = n.Object - } - queuedDeletions++ - if err := f.queueActionLocked(Deleted, DeletedFinalStateUnknown{k, deletedObj}); err != nil { - return err - } - } - - if !f.populated { - f.populated = true - // While there shouldn't be any queued deletions in the initial - // population of the queue, it's better to be on the safe side. - f.initialPopulationCount = keys.Len() + queuedDeletions - } - - return nil - } - - // Detect deletions not already in the queue. - knownKeys := f.knownObjects.ListKeys() + // Do deletion detection against objects in the queue queuedDeletions := 0 - for _, k := range knownKeys { + for k, oldItem := range f.items { if keys.Has(k) { continue } - - deletedObj, exists, err := f.knownObjects.GetByKey(k) - if err != nil { - deletedObj = nil - klog.Errorf("Unexpected error %v during lookup of key %v, placing DeleteFinalStateUnknown marker without object", err, k) - } else if !exists { - deletedObj = nil - klog.Infof("Key %v does not exist in known objects store, placing DeleteFinalStateUnknown marker without object", k) + // Delete pre-existing items not in the new list. + // This could happen if watch deletion event was missed while + // disconnected from apiserver. + var deletedObj interface{} + if n := oldItem.Newest(); n != nil { + deletedObj = n.Object + + // if the previous object is a DeletedFinalStateUnknown, we have to extract the actual Object + if d, ok := deletedObj.(DeletedFinalStateUnknown); ok { + deletedObj = d.Obj + } } queuedDeletions++ if err := f.queueActionLocked(Deleted, DeletedFinalStateUnknown{k, deletedObj}); err != nil { @@ -647,6 +662,32 @@ func (f *DeltaFIFO) Replace(list []interface{}, _ string) error { } } + if f.knownObjects != nil { + // Detect deletions for objects not present in the queue, but present in KnownObjects + knownKeys := f.knownObjects.ListKeys() + for _, k := range knownKeys { + if keys.Has(k) { + continue + } + if len(f.items[k]) > 0 { + continue + } + + deletedObj, exists, err := f.knownObjects.GetByKey(k) + if err != nil { + deletedObj = nil + klog.Errorf("Unexpected error %v during lookup of key %v, placing DeleteFinalStateUnknown marker without object", err, k) + } else if !exists { + deletedObj = nil + klog.Infof("Key %v does not exist in known objects store, placing DeleteFinalStateUnknown marker without object", k) + } + queuedDeletions++ + if err := f.queueActionLocked(Deleted, DeletedFinalStateUnknown{k, deletedObj}); err != nil { + return err + } + } + } + if !f.populated { f.populated = true f.initialPopulationCount = keys.Len() + queuedDeletions diff --git a/cli/vendor/k8s.io/client-go/tools/cache/shared_informer.go b/cli/vendor/k8s.io/client-go/tools/cache/shared_informer.go index 9f42782d1..35ebd396c 100644 --- a/cli/vendor/k8s.io/client-go/tools/cache/shared_informer.go +++ b/cli/vendor/k8s.io/client-go/tools/cache/shared_informer.go @@ -190,10 +190,7 @@ type SharedInformer interface { // // Must be set before starting the informer. // - // Note: Since the object given to the handler may be already shared with - // other goroutines, it is advisable to copy the object being - // transform before mutating it at all and returning the copy to prevent - // data races. + // Please see the comment on TransformFunc for more details. SetTransform(handler TransformFunc) error } @@ -404,6 +401,7 @@ func (s *sharedIndexInformer) Run(stopCh <-chan struct{}) { fifo := NewDeltaFIFOWithOptions(DeltaFIFOOptions{ KnownObjects: s.indexer, EmitDeltaTypeReplaced: true, + Transformer: s.transform, }) cfg := &Config{ @@ -568,7 +566,7 @@ func (s *sharedIndexInformer) HandleDeltas(obj interface{}) error { defer s.blockDeltas.Unlock() if deltas, ok := obj.(Deltas); ok { - return processDeltas(s, s.indexer, s.transform, deltas) + return processDeltas(s, s.indexer, deltas) } return errors.New("object given as Process argument is not Deltas") } diff --git a/cli/vendor/k8s.io/client-go/transport/cache.go b/cli/vendor/k8s.io/client-go/transport/cache.go index 214f0a79c..477c22cde 100644 --- a/cli/vendor/k8s.io/client-go/transport/cache.go +++ b/cli/vendor/k8s.io/client-go/transport/cache.go @@ -17,6 +17,7 @@ limitations under the License. package transport import ( + "context" "fmt" "net" "net/http" @@ -55,6 +56,9 @@ type tlsCacheKey struct { serverName string nextProtos string disableCompression bool + // these functions are wrapped to allow them to be used as map keys + getCert *GetCertHolder + dial *DialHolder } func (t tlsCacheKey) String() string { @@ -62,7 +66,8 @@ func (t tlsCacheKey) String() string { if len(t.keyData) > 0 { keyText = "" } - return fmt.Sprintf("insecure:%v, caData:%#v, certData:%#v, keyData:%s, serverName:%s, disableCompression:%t", t.insecure, t.caData, t.certData, keyText, t.serverName, t.disableCompression) + return fmt.Sprintf("insecure:%v, caData:%#v, certData:%#v, keyData:%s, serverName:%s, disableCompression:%t, getCert:%p, dial:%p", + t.insecure, t.caData, t.certData, keyText, t.serverName, t.disableCompression, t.getCert, t.dial) } func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) { @@ -92,8 +97,10 @@ func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) { return http.DefaultTransport, nil } - dial := config.Dial - if dial == nil { + var dial func(ctx context.Context, network, address string) (net.Conn, error) + if config.Dial != nil { + dial = config.Dial + } else { dial = (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, @@ -102,7 +109,7 @@ func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) { // If we use are reloading files, we need to handle certificate rotation properly // TODO(jackkleeman): We can also add rotation here when config.HasCertCallback() is true - if config.TLS.ReloadTLSFiles { + if config.TLS.ReloadTLSFiles && tlsConfig != nil && tlsConfig.GetClientCertificate != nil { dynamicCertDialer := certRotatingDialer(tlsConfig.GetClientCertificate, dial) tlsConfig.GetClientCertificate = dynamicCertDialer.GetClientCertificate dial = dynamicCertDialer.connDialer.DialContext @@ -138,10 +145,18 @@ func tlsConfigKey(c *Config) (tlsCacheKey, bool, error) { return tlsCacheKey{}, false, err } - if c.TLS.GetCert != nil || c.Dial != nil || c.Proxy != nil { + if c.Proxy != nil { // cannot determine equality for functions return tlsCacheKey{}, false, nil } + if c.Dial != nil && c.DialHolder == nil { + // cannot determine equality for dial function that doesn't have non-nil DialHolder set as well + return tlsCacheKey{}, false, nil + } + if c.TLS.GetCert != nil && c.TLS.GetCertHolder == nil { + // cannot determine equality for getCert function that doesn't have non-nil GetCertHolder set as well + return tlsCacheKey{}, false, nil + } k := tlsCacheKey{ insecure: c.TLS.Insecure, @@ -149,6 +164,8 @@ func tlsConfigKey(c *Config) (tlsCacheKey, bool, error) { serverName: c.TLS.ServerName, nextProtos: strings.Join(c.TLS.NextProtos, ","), disableCompression: c.DisableCompression, + getCert: c.TLS.GetCertHolder, + dial: c.DialHolder, } if c.TLS.ReloadTLSFiles { diff --git a/cli/vendor/k8s.io/client-go/transport/config.go b/cli/vendor/k8s.io/client-go/transport/config.go index 89de798f6..fd853c0b3 100644 --- a/cli/vendor/k8s.io/client-go/transport/config.go +++ b/cli/vendor/k8s.io/client-go/transport/config.go @@ -68,7 +68,11 @@ type Config struct { WrapTransport WrapperFunc // Dial specifies the dial function for creating unencrypted TCP connections. + // If specified, this transport will be non-cacheable unless DialHolder is also set. Dial func(ctx context.Context, network, address string) (net.Conn, error) + // DialHolder can be populated to make transport configs cacheable. + // If specified, DialHolder.Dial must be equal to Dial. + DialHolder *DialHolder // Proxy is the proxy func to be used for all requests made by this // transport. If Proxy is nil, http.ProxyFromEnvironment is used. If Proxy @@ -78,6 +82,11 @@ type Config struct { Proxy func(*http.Request) (*url.URL, error) } +// DialHolder is used to make the wrapped function comparable so that it can be used as a map key. +type DialHolder struct { + Dial func(ctx context.Context, network, address string) (net.Conn, error) +} + // ImpersonationConfig has all the available impersonation options type ImpersonationConfig struct { // UserName matches user.Info.GetName() @@ -143,5 +152,15 @@ type TLSConfig struct { // To use only http/1.1, set to ["http/1.1"]. NextProtos []string - GetCert func() (*tls.Certificate, error) // Callback that returns a TLS client certificate. CertData, CertFile, KeyData and KeyFile supercede this field. + // Callback that returns a TLS client certificate. CertData, CertFile, KeyData and KeyFile supercede this field. + // If specified, this transport is non-cacheable unless CertHolder is populated. + GetCert func() (*tls.Certificate, error) + // CertHolder can be populated to make transport configs that set GetCert cacheable. + // If set, CertHolder.GetCert must be equal to GetCert. + GetCertHolder *GetCertHolder +} + +// GetCertHolder is used to make the wrapped function comparable so that it can be used as a map key. +type GetCertHolder struct { + GetCert func() (*tls.Certificate, error) } diff --git a/cli/vendor/k8s.io/client-go/transport/transport.go b/cli/vendor/k8s.io/client-go/transport/transport.go index b4a7bfa67..eabfce72d 100644 --- a/cli/vendor/k8s.io/client-go/transport/transport.go +++ b/cli/vendor/k8s.io/client-go/transport/transport.go @@ -24,6 +24,7 @@ import ( "fmt" "io/ioutil" "net/http" + "reflect" "sync" "time" @@ -39,6 +40,10 @@ func New(config *Config) (http.RoundTripper, error) { return nil, fmt.Errorf("using a custom transport with TLS certificate options or the insecure flag is not allowed") } + if !isValidHolders(config) { + return nil, fmt.Errorf("misconfigured holder for dialer or cert callback") + } + var ( rt http.RoundTripper err error @@ -56,6 +61,26 @@ func New(config *Config) (http.RoundTripper, error) { return HTTPWrappersForConfig(config, rt) } +func isValidHolders(config *Config) bool { + if config.TLS.GetCertHolder != nil { + if config.TLS.GetCertHolder.GetCert == nil || + config.TLS.GetCert == nil || + reflect.ValueOf(config.TLS.GetCertHolder.GetCert).Pointer() != reflect.ValueOf(config.TLS.GetCert).Pointer() { + return false + } + } + + if config.DialHolder != nil { + if config.DialHolder.Dial == nil || + config.Dial == nil || + reflect.ValueOf(config.DialHolder.Dial).Pointer() != reflect.ValueOf(config.Dial).Pointer() { + return false + } + } + + return true +} + // TLSConfigFor returns a tls.Config that will provide the transport level security defined // by the provided Config. Will return nil if no transport level security is requested. func TLSConfigFor(c *Config) (*tls.Config, error) { diff --git a/cli/vendor/k8s.io/client-go/util/cert/cert.go b/cli/vendor/k8s.io/client-go/util/cert/cert.go index 75143ec07..447c3013c 100644 --- a/cli/vendor/k8s.io/client-go/util/cert/cert.go +++ b/cli/vendor/k8s.io/client-go/util/cert/cert.go @@ -26,6 +26,7 @@ import ( "encoding/pem" "fmt" "io/ioutil" + "math" "math/big" "net" "path/filepath" @@ -44,6 +45,7 @@ type Config struct { Organization []string AltNames AltNames Usages []x509.ExtKeyUsage + NotBefore time.Time } // AltNames contains the domain names and IP addresses that will be added @@ -57,14 +59,24 @@ type AltNames struct { // NewSelfSignedCACert creates a CA certificate func NewSelfSignedCACert(cfg Config, key crypto.Signer) (*x509.Certificate, error) { now := time.Now() + // returns a uniform random value in [0, max-1), then add 1 to serial to make it a uniform random value in [1, max). + serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64-1)) + if err != nil { + return nil, err + } + serial = new(big.Int).Add(serial, big.NewInt(1)) + notBefore := now.UTC() + if !cfg.NotBefore.IsZero() { + notBefore = cfg.NotBefore.UTC() + } tmpl := x509.Certificate{ - SerialNumber: new(big.Int).SetInt64(0), + SerialNumber: serial, Subject: pkix.Name{ CommonName: cfg.CommonName, Organization: cfg.Organization, }, DNSNames: []string{cfg.CommonName}, - NotBefore: now.UTC(), + NotBefore: notBefore, NotAfter: now.Add(duration365d * 10).UTC(), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, BasicConstraintsValid: true, @@ -116,9 +128,14 @@ func GenerateSelfSignedCertKeyWithFixtures(host string, alternateIPs []net.IP, a if err != nil { return nil, nil, err } - + // returns a uniform random value in [0, max-1), then add 1 to serial to make it a uniform random value in [1, max). + serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64-1)) + if err != nil { + return nil, nil, err + } + serial = new(big.Int).Add(serial, big.NewInt(1)) caTemplate := x509.Certificate{ - SerialNumber: big.NewInt(1), + SerialNumber: serial, Subject: pkix.Name{ CommonName: fmt.Sprintf("%s-ca@%d", host, time.Now().Unix()), }, @@ -144,9 +161,14 @@ func GenerateSelfSignedCertKeyWithFixtures(host string, alternateIPs []net.IP, a if err != nil { return nil, nil, err } - + // returns a uniform random value in [0, max-1), then add 1 to serial to make it a uniform random value in [1, max). + serial, err = cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64-1)) + if err != nil { + return nil, nil, err + } + serial = new(big.Int).Add(serial, big.NewInt(1)) template := x509.Certificate{ - SerialNumber: big.NewInt(2), + SerialNumber: serial, Subject: pkix.Name{ CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()), }, diff --git a/cli/vendor/modules.txt b/cli/vendor/modules.txt index caeae14ae..9aef3aaaa 100644 --- a/cli/vendor/modules.txt +++ b/cli/vendor/modules.txt @@ -122,7 +122,7 @@ github.com/google/go-containerregistry/pkg/name ## explicit; go 1.12 github.com/google/gofuzz github.com/google/gofuzz/bytesource -# github.com/google/uuid v1.2.0 +# github.com/google/uuid v1.3.0 ## explicit github.com/google/uuid # github.com/hashicorp/go-version v1.3.0 @@ -206,7 +206,7 @@ github.com/spf13/cobra # github.com/spf13/pflag v1.0.5 ## explicit; go 1.12 github.com/spf13/pflag -# github.com/stretchr/testify v1.7.5 +# github.com/stretchr/testify v1.8.0 ## explicit; go 1.13 github.com/stretchr/testify/assert github.com/stretchr/testify/require @@ -383,7 +383,7 @@ gopkg.in/yaml.v2 # gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 -# k8s.io/api v0.25.0 +# k8s.io/api v0.25.13 ## explicit; go 1.19 k8s.io/api/admission/v1 k8s.io/api/admission/v1beta1 @@ -433,12 +433,12 @@ k8s.io/api/scheduling/v1beta1 k8s.io/api/storage/v1 k8s.io/api/storage/v1alpha1 k8s.io/api/storage/v1beta1 -# k8s.io/apiextensions-apiserver v0.25.0 +# k8s.io/apiextensions-apiserver v0.25.13 ## explicit; go 1.19 k8s.io/apiextensions-apiserver/pkg/apis/apiextensions k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1 k8s.io/apiextensions-apiserver/pkg/apiserver/schema -# k8s.io/apimachinery v0.25.0 +# k8s.io/apimachinery v0.25.13 ## explicit; go 1.19 k8s.io/apimachinery/pkg/api/equality k8s.io/apimachinery/pkg/api/errors @@ -486,7 +486,7 @@ k8s.io/apimachinery/pkg/version k8s.io/apimachinery/pkg/watch k8s.io/apimachinery/third_party/forked/golang/json k8s.io/apimachinery/third_party/forked/golang/reflect -# k8s.io/client-go v0.25.0 +# k8s.io/client-go v0.25.13 ## explicit; go 1.19 k8s.io/client-go/applyconfigurations/admissionregistration/v1 k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1 @@ -622,7 +622,7 @@ k8s.io/client-go/util/homedir k8s.io/client-go/util/jsonpath k8s.io/client-go/util/keyutil k8s.io/client-go/util/workqueue -# k8s.io/component-base v0.25.0 +# k8s.io/component-base v0.25.13 ## explicit; go 1.19 k8s.io/component-base/config k8s.io/component-base/config/v1alpha1 diff --git a/go.mod b/go.mod index a7ff808e0..bcb7894b2 100644 --- a/go.mod +++ b/go.mod @@ -14,11 +14,11 @@ require ( golang.org/x/text v0.13.0 // indirect golang.org/x/tools v0.6.0 gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.25.6 - k8s.io/apimachinery v0.25.6 - k8s.io/apiserver v0.25.6 - k8s.io/client-go v0.25.6 - k8s.io/code-generator v0.25.6 + k8s.io/api v0.25.13 + k8s.io/apimachinery v0.25.13 + k8s.io/apiserver v0.25.13 + k8s.io/client-go v0.25.13 + k8s.io/code-generator v0.25.13 k8s.io/kube-aggregator v0.22.17 k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 sigs.k8s.io/controller-runtime v0.13.1 @@ -34,7 +34,7 @@ require ( github.com/spf13/cobra v1.6.1 golang.org/x/sync v0.1.0 gopkg.in/yaml.v2 v2.4.0 - k8s.io/component-base v0.25.6 + k8s.io/component-base v0.25.13 k8s.io/klog/v2 v2.70.1 k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed ) @@ -66,7 +66,7 @@ require ( github.com/golang/protobuf v1.5.2 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/gofuzz v1.1.0 // indirect - github.com/google/uuid v1.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/hashicorp/go-version v1.2.1 // indirect github.com/imdario/mergo v0.3.12 // indirect @@ -118,9 +118,9 @@ require ( gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect - k8s.io/apiextensions-apiserver v0.25.0 // indirect + k8s.io/apiextensions-apiserver v0.25.13 // indirect k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 // indirect - sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.35 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.37 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect ) diff --git a/go.sum b/go.sum index e628968b5..45996ec38 100644 --- a/go.sum +++ b/go.sum @@ -233,8 +233,8 @@ github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hf github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= @@ -912,31 +912,31 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8= k8s.io/api v0.22.17/go.mod h1:6qVojJ3y+qIq7JSMwTH0BcPHl3dch4HefIC+4nguZhs= -k8s.io/api v0.25.6 h1:LwDY2H6kD/3R8TekJYYaJWOdekNdXDO44eVpX6sNtJA= -k8s.io/api v0.25.6/go.mod h1:bVp01KUcl8VUHFBTJMOknWNo7XvR0cMbeTTuFg1zCUs= +k8s.io/api v0.25.13 h1:nOQWK5/ngLIG2CqmVV7uTFDsPCGkDk4kIGJ26t2AwIo= +k8s.io/api v0.25.13/go.mod h1:yGpHyrivZ0enqWqT5s1pN98a4Q834rZkIUEABpleEtw= k8s.io/apiextensions-apiserver v0.22.2/go.mod h1:2E0Ve/isxNl7tWLSUDgi6+cmwHi5fQRdwGVCxbC+KFA= -k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= -k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= +k8s.io/apiextensions-apiserver v0.25.13 h1:tdB/ULSvlbZIecU2bDOoCb46Ozk5VyLL6IPysC8gUpE= +k8s.io/apiextensions-apiserver v0.25.13/go.mod h1:uqYfnY2GOYXlfpnT6No9dxrxDbMjaYvh/Bpm0iNMYnA= k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apimachinery v0.22.17/go.mod h1:ZvVLP5iLhwVFg2Yx9Gh5W0um0DUauExbRhe+2Z8I1EU= -k8s.io/apimachinery v0.25.6 h1:r6KIF2AHwLqFfZ0LcOA3I11SF62YZK83dxj1fn14NOQ= -k8s.io/apimachinery v0.25.6/go.mod h1:1S2i1QHkmxc8+EZCIxe/fX5hpldVXk4gvnJInMEb8D4= +k8s.io/apimachinery v0.25.13 h1:byRHkSinOOVdo0pvjdblauFYfwAnx+JB8Pqi9w9weik= +k8s.io/apimachinery v0.25.13/go.mod h1:IFwbcNi3gKkfDhuy0VYu3+BwbxbiIov3p6FR8ge1Epc= k8s.io/apiserver v0.22.2/go.mod h1:vrpMmbyjWrgdyOvZTSpsusQq5iigKNWv9o9KlDAbBHI= k8s.io/apiserver v0.22.17/go.mod h1:zNXYCtXZ91AkmIUZgQ8lT9vdlDqgSkokJpds/F6DdGU= -k8s.io/apiserver v0.25.6 h1:32mn8HAlsEl1tpuiVmhAl0YCVkOugjybsJ6l6kf0c8k= -k8s.io/apiserver v0.25.6/go.mod h1:IEp2B2/FvQ8GmdspscUoUS0iFF/GGc6NVrJ/cTM4OaA= +k8s.io/apiserver v0.25.13 h1:vROP8uv4tqcASYUm6gsBbM04p5umfZI0vHd5WmNbxj4= +k8s.io/apiserver v0.25.13/go.mod h1:EdnRllTlbHg4sjuzFUxoJU8h0OWtJyJbB45dK4QCnrs= k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U= k8s.io/client-go v0.22.17/go.mod h1:SQPVpN+E/5Q/aSV7fYDT8VKVdaljhxI/t/84ADVJoC4= -k8s.io/client-go v0.25.6 h1:CHxACHi0DijmlYyUR7ooZoXnD5P8jYLgBHcxp775x/U= -k8s.io/client-go v0.25.6/go.mod h1:s9mMAGFYiH3Z66j7BESzu0GEradT9GQ2LjFf/YRrnyc= +k8s.io/client-go v0.25.13 h1:Wan/8RXVNxSgFI/wMfWwJjmLglRYuLItytMWNiGo9LY= +k8s.io/client-go v0.25.13/go.mod h1:b2on3RSCwHdmvnUQx4/bkgMAs19M7BlUDze3WJuK0TE= k8s.io/code-generator v0.22.2/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= k8s.io/code-generator v0.22.17/go.mod h1:iOZwYADSgFPNGWfqHFfg1V0TNJnl1t0WyZluQp4baqU= -k8s.io/code-generator v0.25.6 h1:md8jqhQiMqos8WaTY0ZxhDk8ttlmJl/ijCneJ8ahBNQ= -k8s.io/code-generator v0.25.6/go.mod h1:aDxzxJynLKQkaa117y0FFcgZ5jG8+GobxZ2JUntmvKk= +k8s.io/code-generator v0.25.13 h1:DCjLGhhBNjGWt54fOyMw4mKqgQJT4dCzcihKsLkVXNw= +k8s.io/code-generator v0.25.13/go.mod h1:FA5a4rk4tMTCgmiDeNdRjml+AGvm72SwZYwD5lBrezY= k8s.io/component-base v0.22.2/go.mod h1:5Br2QhI9OTe79p+TzPe9JKNQYvEKbq9rTJDWllunGug= k8s.io/component-base v0.22.17/go.mod h1:Mrcvmxs+Ctx/xCYGWoFAvfZO9DC4gDgLtUbPJ4PjjUE= -k8s.io/component-base v0.25.6 h1:v3ci6FbXFcxpjyQJaaLq0MgzT3vyFzwUDWtO+KRv9Bk= -k8s.io/component-base v0.25.6/go.mod h1:k7DfcfJ8cOI6A2xTCfU5LxsnXV+lWw1ME8cRCHzIh6o= +k8s.io/component-base v0.25.13 h1:Lxpq2nu7Q4SueubBlU8kEcTgdaNXsktYg9bLsqvAV+4= +k8s.io/component-base v0.25.13/go.mod h1:Fh9SqG+Uyumtc+7HNT2FBZCxksIIFaukoL2LjqbBtgY= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 h1:TT1WdmqqXareKxZ/oNXEUSwKlLiHzPMyB0t8BaFeBYI= @@ -960,8 +960,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.35 h1:+xBL5uTc+BkPBwmMi3vYfUJjq+N3K+H6PXeETwf5cPI= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.35/go.mod h1:WxjusMwXlKzfAs4p9km6XJRndVt2FROgMVCE4cdohFo= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.37 h1:fAPTNEpzQMOLMGwOHNbUkR2xXTQwMJOZYNx+/mLlOh0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.37/go.mod h1:vfnxT4FXNT8eGvO+xi/DsyC/qHmdujqwrUa1WSspCsk= sigs.k8s.io/controller-runtime v0.13.1 h1:tUsRCSJVM1QQOOeViGeX3GMT3dQF1eePPw6sEE3xSlg= sigs.k8s.io/controller-runtime v0.13.1/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= sigs.k8s.io/controller-tools v0.7.0 h1:iZIz1vEcavyEfxjcTLs1WH/MPf4vhPCtTKhoHqV8/G0= diff --git a/hack/dependencies.yml b/hack/dependencies.yml index d80849b97..8145f4248 100644 --- a/hack/dependencies.yml +++ b/hack/dependencies.yml @@ -1,51 +1,51 @@ - checksums: darwin: - amd64: bb45a5a192f409d11221db930d4420603b632ff09bf1621202c755b104f73f88 - arm64: b704d22ae50785618bce00afaad3c4b63c24e211fbca17d02361747cf047d7de + amd64: 2f0aa3127e9aa3e588b98169358b488dbe82d04c2a24b63f6faa09ed797e312b + arm64: 3f05260d6ca6ee9268921c6b58b828c4e58ec47ee7014bfa8c342a40dcb8041d linux: - amd64: d8114c573a04c67aab3d69c037adf87444f082a5872331ebb361c4a4a8cb6ab3 - arm64: efd7865f3cb3cf9e9b420cfc10fbe01cd9166103a6ec778dd66bc5f85b85621e + amd64: ba308a20b7711c2fd769baa3470f62788ec4356686557076ac7b857160c3cc69 + arm64: 506b3b9d0ab3ba0f9ed3fc47e5afae33b8624fd928075c62de8ac2517f52c069 dev: true name: ytt repo: vmware-tanzu/carvel-ytt urlTemplate: https://github.com/vmware-tanzu/carvel-{{.Name}}/releases/download/{{.Version}}/{{.Name}}-{{.OS}}-{{.Arch}} - version: v0.45.7 + version: v0.45.8 - checksums: darwin: - amd64: 5e6d1282d5a166ca03b11a459d05450281d9bed758241a078f9fcfb783449809 - arm64: 6b305274f08e9a1fa363f75d757bed72c2e355773d23c5f4c4ae9c4377813e96 + amd64: 422061b65ef05993f7279d9c4162981adcc42c2be2290b87ba75d51aa9f75d50 + arm64: 7eff04d3d904997c609fe7f51cb514042d5ecb6466ce9674d0775f8fcdd8553f linux: - amd64: b17b486d8f5d9b64200daeedf0f8c6f242faa8a9c6eecce768692565a863ebad - arm64: ec5df757ab5b894cd8c8e5508d6656ea88442f6fd0a5579ac1a65faf25bb0716 + amd64: 4b78bb6e5f29254ea6387a5a9e5498cc57dd96116bcf2a0ea5f6244d3453392c + arm64: ef034480a9ae23f80a8b109b0c3d07f082198f64b33868ab95ab5fa6464bb847 dev: true name: kbld repo: vmware-tanzu/carvel-kbld urlTemplate: https://github.com/vmware-tanzu/carvel-{{.Name}}/releases/download/{{.Version}}/{{.Name}}-{{.OS}}-{{.Arch}} - version: v0.37.9 + version: v0.37.10 - checksums: darwin: - amd64: 97715fa822696b66b3c7331c2c816c0e0f4c7da43a1253e1f4c87fa0a3fa1d00 - arm64: d4529812875681eb8945094f76bcaca0b49b51a9bdb9e8dc1a0ad536b466fbef + amd64: 064e8f9de7ac0af20845c7659cee9b8ff2afda945423cd0fe2542b86a11bbd5a + arm64: 196808e1d9a4ffee77fcc84e97a67e419302c6ca1868dac258450ee575ef7a46 linux: - amd64: 59ee85ae4beb297e0e260a5b3e063f2e52e772f11b79a67fc789ec0107666d56 - arm64: ff1bfd6f9f00b3ae836ca4cabfdcf1ad882ebc1a326d6099c5b8ea5c3cfcae11 + amd64: 5b67852a91af0a1074a6565c0ff893dda23749e2695f63cfe05a013332176d55 + arm64: 3ee4b8b44a79551d1062676c69f1d8e60fbd170c5f29fbc97a47a31d83ed6a5e dev: true name: kapp repo: vmware-tanzu/carvel-kapp urlTemplate: https://github.com/vmware-tanzu/carvel-{{.Name}}/releases/download/{{.Version}}/{{.Name}}-{{.OS}}-{{.Arch}} - version: v0.55.5 + version: v0.55.6 - checksums: darwin: - amd64: 12c8bc8f4a9fb5d850922d1f84ca779c5f8195c73a13e486425e3839bbf91e68 - arm64: a020e63ee149b0d3507c214ec2babaa3b690d54495411d846bdb24d2a0e8bd1b + amd64: 976a94c6f2bfc13268899e5846b9ba547422f7cfa1c714e91177ff990ffdd7bc + arm64: 06669410614070ff9114b5ed7f6da7851b0b7018274248c1543d8de97b5583ab linux: - amd64: c22adfd9ac85b2e9162d844fcb33b5f7a3f761f684f8e9ab00af5a1a316358fa - arm64: 45d1c3510898d61b790103552e8bee313361cdf097b9e647113f9a7b4587390c + amd64: 23df37ae5042d11c7547b9fed8513de4d174b9d0d2b56275cbd2fff9d4fafa05 + arm64: ece5b1976905ab8c2ec928829c0cbfbe607d3e9034332522fe2b56b58af87cf4 dev: true name: vendir repo: vmware-tanzu/carvel-vendir urlTemplate: https://github.com/vmware-tanzu/carvel-{{.Name}}/releases/download/{{.Version}}/{{.Name}}-{{.OS}}-{{.Arch}} - version: v0.33.6 + version: v0.33.7 - checksums: linux: amd64: 1b2313cd198d45eab00cc37c38f6b1ca0a948ba279c29e322bdf426d406129b5 diff --git a/pkg/apiserver/openapi/zz_generated.openapi.go b/pkg/apiserver/openapi/zz_generated.openapi.go index c524abf65..d2727730a 100644 --- a/pkg/apiserver/openapi/zz_generated.openapi.go +++ b/pkg/apiserver/openapi/zz_generated.openapi.go @@ -5729,7 +5729,7 @@ func schema_k8sio_api_core_v1_HTTPHeader(ref common.ReferenceCallback) common.Op Properties: map[string]spec.Schema{ "name": { SchemaProps: spec.SchemaProps{ - Description: "The header field name", + Description: "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.", Default: "", Type: []string{"string"}, Format: "", diff --git a/vendor/github.com/google/uuid/null.go b/vendor/github.com/google/uuid/null.go new file mode 100644 index 000000000..d7fcbf286 --- /dev/null +++ b/vendor/github.com/google/uuid/null.go @@ -0,0 +1,118 @@ +// Copyright 2021 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "database/sql/driver" + "encoding/json" + "fmt" +) + +var jsonNull = []byte("null") + +// NullUUID represents a UUID that may be null. +// NullUUID implements the SQL driver.Scanner interface so +// it can be used as a scan destination: +// +// var u uuid.NullUUID +// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u) +// ... +// if u.Valid { +// // use u.UUID +// } else { +// // NULL value +// } +// +type NullUUID struct { + UUID UUID + Valid bool // Valid is true if UUID is not NULL +} + +// Scan implements the SQL driver.Scanner interface. +func (nu *NullUUID) Scan(value interface{}) error { + if value == nil { + nu.UUID, nu.Valid = Nil, false + return nil + } + + err := nu.UUID.Scan(value) + if err != nil { + nu.Valid = false + return err + } + + nu.Valid = true + return nil +} + +// Value implements the driver Valuer interface. +func (nu NullUUID) Value() (driver.Value, error) { + if !nu.Valid { + return nil, nil + } + // Delegate to UUID Value function + return nu.UUID.Value() +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (nu NullUUID) MarshalBinary() ([]byte, error) { + if nu.Valid { + return nu.UUID[:], nil + } + + return []byte(nil), nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (nu *NullUUID) UnmarshalBinary(data []byte) error { + if len(data) != 16 { + return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) + } + copy(nu.UUID[:], data) + nu.Valid = true + return nil +} + +// MarshalText implements encoding.TextMarshaler. +func (nu NullUUID) MarshalText() ([]byte, error) { + if nu.Valid { + return nu.UUID.MarshalText() + } + + return jsonNull, nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (nu *NullUUID) UnmarshalText(data []byte) error { + id, err := ParseBytes(data) + if err != nil { + nu.Valid = false + return err + } + nu.UUID = id + nu.Valid = true + return nil +} + +// MarshalJSON implements json.Marshaler. +func (nu NullUUID) MarshalJSON() ([]byte, error) { + if nu.Valid { + return json.Marshal(nu.UUID) + } + + return jsonNull, nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (nu *NullUUID) UnmarshalJSON(data []byte) error { + if bytes.Equal(data, jsonNull) { + *nu = NullUUID{} + return nil // valid null UUID + } + err := json.Unmarshal(data, &nu.UUID) + nu.Valid = err == nil + return err +} diff --git a/vendor/github.com/google/uuid/uuid.go b/vendor/github.com/google/uuid/uuid.go index 60d26bb50..a57207aeb 100644 --- a/vendor/github.com/google/uuid/uuid.go +++ b/vendor/github.com/google/uuid/uuid.go @@ -12,6 +12,7 @@ import ( "fmt" "io" "strings" + "sync" ) // A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC @@ -33,7 +34,15 @@ const ( Future // Reserved for future definition. ) -var rander = rand.Reader // random function +const randPoolSize = 16 * 16 + +var ( + rander = rand.Reader // random function + poolEnabled = false + poolMu sync.Mutex + poolPos = randPoolSize // protected with poolMu + pool [randPoolSize]byte // protected with poolMu +) type invalidLengthError struct{ len int } @@ -41,6 +50,12 @@ func (err invalidLengthError) Error() string { return fmt.Sprintf("invalid UUID length: %d", err.len) } +// IsInvalidLengthError is matcher function for custom error invalidLengthError +func IsInvalidLengthError(err error) bool { + _, ok := err.(invalidLengthError) + return ok +} + // Parse decodes s into a UUID or returns an error. Both the standard UUID // forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the @@ -249,3 +264,31 @@ func SetRand(r io.Reader) { } rander = r } + +// EnableRandPool enables internal randomness pool used for Random +// (Version 4) UUID generation. The pool contains random bytes read from +// the random number generator on demand in batches. Enabling the pool +// may improve the UUID generation throughput significantly. +// +// Since the pool is stored on the Go heap, this feature may be a bad fit +// for security sensitive applications. +// +// Both EnableRandPool and DisableRandPool are not thread-safe and should +// only be called when there is no possibility that New or any other +// UUID Version 4 generation function will be called concurrently. +func EnableRandPool() { + poolEnabled = true +} + +// DisableRandPool disables the randomness pool if it was previously +// enabled with EnableRandPool. +// +// Both EnableRandPool and DisableRandPool are not thread-safe and should +// only be called when there is no possibility that New or any other +// UUID Version 4 generation function will be called concurrently. +func DisableRandPool() { + poolEnabled = false + defer poolMu.Unlock() + poolMu.Lock() + poolPos = randPoolSize +} diff --git a/vendor/github.com/google/uuid/version4.go b/vendor/github.com/google/uuid/version4.go index 86160fbd0..7697802e4 100644 --- a/vendor/github.com/google/uuid/version4.go +++ b/vendor/github.com/google/uuid/version4.go @@ -27,6 +27,8 @@ func NewString() string { // The strength of the UUIDs is based on the strength of the crypto/rand // package. // +// Uses the randomness pool if it was enabled with EnableRandPool. +// // A note about uniqueness derived from the UUID Wikipedia entry: // // Randomly generated UUIDs have 122 random bits. One's annual risk of being @@ -35,7 +37,10 @@ func NewString() string { // equivalent to the odds of creating a few tens of trillions of UUIDs in a // year and having one duplicate. func NewRandom() (UUID, error) { - return NewRandomFromReader(rander) + if !poolEnabled { + return NewRandomFromReader(rander) + } + return newRandomFromPool() } // NewRandomFromReader returns a UUID based on bytes read from a given io.Reader. @@ -49,3 +54,23 @@ func NewRandomFromReader(r io.Reader) (UUID, error) { uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 return uuid, nil } + +func newRandomFromPool() (UUID, error) { + var uuid UUID + poolMu.Lock() + if poolPos == randPoolSize { + _, err := io.ReadFull(rander, pool[:]) + if err != nil { + poolMu.Unlock() + return Nil, err + } + poolPos = 0 + } + copy(uuid[:], pool[poolPos:(poolPos+16)]) + poolPos += 16 + poolMu.Unlock() + + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 + return uuid, nil +} diff --git a/vendor/k8s.io/api/core/v1/generated.proto b/vendor/k8s.io/api/core/v1/generated.proto index 256f5da32..e4e562e32 100644 --- a/vendor/k8s.io/api/core/v1/generated.proto +++ b/vendor/k8s.io/api/core/v1/generated.proto @@ -1761,7 +1761,8 @@ message HTTPGetAction { // HTTPHeader describes a custom header to be used in HTTP probes message HTTPHeader { - // The header field name + // The header field name. + // This will be canonicalized upon output, so case-variant names will be understood as the same header. optional string name = 1; // The header field value diff --git a/vendor/k8s.io/api/core/v1/types.go b/vendor/k8s.io/api/core/v1/types.go index 754a23613..e60e180c2 100644 --- a/vendor/k8s.io/api/core/v1/types.go +++ b/vendor/k8s.io/api/core/v1/types.go @@ -2112,7 +2112,8 @@ type SecretEnvSource struct { // HTTPHeader describes a custom header to be used in HTTP probes type HTTPHeader struct { - // The header field name + // The header field name. + // This will be canonicalized upon output, so case-variant names will be understood as the same header. Name string `json:"name" protobuf:"bytes,1,opt,name=name"` // The header field value Value string `json:"value" protobuf:"bytes,2,opt,name=value"` @@ -4266,6 +4267,9 @@ const ( // LoadBalancerPortsError represents the condition of the requested ports // on the cloud load balancer instance. LoadBalancerPortsError = "LoadBalancerPortsError" + // LoadBalancerPortsErrorReason reason in ServiceStatus condition LoadBalancerPortsError + // means the LoadBalancer was not able to be configured correctly. + LoadBalancerPortsErrorReason = "LoadBalancerMixedProtocolNotSupported" ) // ServiceStatus represents the current status of a service. @@ -6624,6 +6628,13 @@ const ( PortForwardRequestIDHeader = "requestID" ) +const ( + // MixedProtocolNotSupported error in PortStatus means that the cloud provider + // can't publish the port on the load balancer because mixed values of protocols + // on the same LoadBalancer type of Service are not supported by the cloud provider. + MixedProtocolNotSupported = "MixedProtocolNotSupported" +) + // PortStatus represents the error condition of a service port type PortStatus struct { diff --git a/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go b/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go index 6bae4bb76..77707706a 100644 --- a/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go +++ b/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go @@ -808,7 +808,7 @@ func (HTTPGetAction) SwaggerDoc() map[string]string { var map_HTTPHeader = map[string]string{ "": "HTTPHeader describes a custom header to be used in HTTP probes", - "name": "The header field name", + "name": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.", "value": "The header field value", } diff --git a/vendor/k8s.io/apimachinery/pkg/runtime/converter.go b/vendor/k8s.io/apimachinery/pkg/runtime/converter.go index 90bf487e3..62eb27afc 100644 --- a/vendor/k8s.io/apimachinery/pkg/runtime/converter.go +++ b/vendor/k8s.io/apimachinery/pkg/runtime/converter.go @@ -231,7 +231,7 @@ func (c *fromUnstructuredContext) pushKey(key string) { } -// FromUnstructuredWIthValidation converts an object from map[string]interface{} representation into a concrete type. +// FromUnstructuredWithValidation converts an object from map[string]interface{} representation into a concrete type. // It uses encoding/json/Unmarshaler if object implements it or reflection if not. // It takes a validationDirective that indicates how to behave when it encounters unknown fields. func (c *unstructuredConverter) FromUnstructuredWithValidation(u map[string]interface{}, obj interface{}, returnUnknownFields bool) error { @@ -465,7 +465,7 @@ func sliceFromUnstructured(sv, dv reflect.Value, ctx *fromUnstructuredContext) e } dv.SetBytes(data) } else { - dv.Set(reflect.Zero(dt)) + dv.Set(reflect.MakeSlice(dt, 0, 0)) } return nil } diff --git a/vendor/k8s.io/apiserver/pkg/authentication/request/headerrequest/requestheader.go b/vendor/k8s.io/apiserver/pkg/authentication/request/headerrequest/requestheader.go index abf509a97..d02732739 100644 --- a/vendor/k8s.io/apiserver/pkg/authentication/request/headerrequest/requestheader.go +++ b/vendor/k8s.io/apiserver/pkg/authentication/request/headerrequest/requestheader.go @@ -163,17 +163,7 @@ func (a *requestHeaderAuthRequestHandler) AuthenticateRequest(req *http.Request) extra := newExtra(req.Header, a.extraHeaderPrefixes.Value()) // clear headers used for authentication - for _, headerName := range a.nameHeaders.Value() { - req.Header.Del(headerName) - } - for _, headerName := range a.groupHeaders.Value() { - req.Header.Del(headerName) - } - for k := range extra { - for _, prefix := range a.extraHeaderPrefixes.Value() { - req.Header.Del(prefix + k) - } - } + ClearAuthenticationHeaders(req.Header, a.nameHeaders, a.groupHeaders, a.extraHeaderPrefixes) return &authenticator.Response{ User: &user.DefaultInfo{ @@ -184,6 +174,26 @@ func (a *requestHeaderAuthRequestHandler) AuthenticateRequest(req *http.Request) }, true, nil } +func ClearAuthenticationHeaders(h http.Header, nameHeaders, groupHeaders, extraHeaderPrefixes StringSliceProvider) { + for _, headerName := range nameHeaders.Value() { + h.Del(headerName) + } + for _, headerName := range groupHeaders.Value() { + h.Del(headerName) + } + for _, prefix := range extraHeaderPrefixes.Value() { + for k := range h { + if hasPrefixIgnoreCase(k, prefix) { + delete(h, k) // we have the raw key so avoid relying on canonicalization + } + } + } +} + +func hasPrefixIgnoreCase(s, prefix string) bool { + return len(s) >= len(prefix) && strings.EqualFold(s[:len(prefix)], prefix) +} + func headerValue(h http.Header, headerNames []string) string { for _, headerName := range headerNames { headerValue := h.Get(headerName) @@ -226,7 +236,7 @@ func newExtra(h http.Header, headerPrefixes []string) map[string][]string { // we have to iterate over prefixes first in order to have proper ordering inside the value slices for _, prefix := range headerPrefixes { for headerName, vv := range h { - if !strings.HasPrefix(strings.ToLower(headerName), strings.ToLower(prefix)) { + if !hasPrefixIgnoreCase(headerName, prefix) { continue } diff --git a/vendor/k8s.io/apiserver/pkg/endpoints/filters/authentication.go b/vendor/k8s.io/apiserver/pkg/endpoints/filters/authentication.go index d69cfef32..d6741bf3a 100644 --- a/vendor/k8s.io/apiserver/pkg/endpoints/filters/authentication.go +++ b/vendor/k8s.io/apiserver/pkg/endpoints/filters/authentication.go @@ -27,6 +27,8 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/authentication/authenticator" + "k8s.io/apiserver/pkg/authentication/authenticatorfactory" + "k8s.io/apiserver/pkg/authentication/request/headerrequest" "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/klog/v2" @@ -38,15 +40,20 @@ type recordMetrics func(context.Context, *authenticator.Response, bool, error, a // stores any such user found onto the provided context for the request. If authentication fails or returns an error // the failed handler is used. On success, "Authorization" header is removed from the request and handler // is invoked to serve the request. -func WithAuthentication(handler http.Handler, auth authenticator.Request, failed http.Handler, apiAuds authenticator.Audiences) http.Handler { - return withAuthentication(handler, auth, failed, apiAuds, recordAuthMetrics) +func WithAuthentication(handler http.Handler, auth authenticator.Request, failed http.Handler, apiAuds authenticator.Audiences, requestHeaderConfig *authenticatorfactory.RequestHeaderConfig) http.Handler { + return withAuthentication(handler, auth, failed, apiAuds, requestHeaderConfig, recordAuthMetrics) } -func withAuthentication(handler http.Handler, auth authenticator.Request, failed http.Handler, apiAuds authenticator.Audiences, metrics recordMetrics) http.Handler { +func withAuthentication(handler http.Handler, auth authenticator.Request, failed http.Handler, apiAuds authenticator.Audiences, requestHeaderConfig *authenticatorfactory.RequestHeaderConfig, metrics recordMetrics) http.Handler { if auth == nil { klog.Warning("Authentication is disabled") return handler } + standardRequestHeaderConfig := &authenticatorfactory.RequestHeaderConfig{ + UsernameHeaders: headerrequest.StaticStringSlice{"X-Remote-User"}, + GroupHeaders: headerrequest.StaticStringSlice{"X-Remote-Group"}, + ExtraHeaderPrefixes: headerrequest.StaticStringSlice{"X-Remote-Extra-"}, + } return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { authenticationStart := time.Now() @@ -76,6 +83,24 @@ func withAuthentication(handler http.Handler, auth authenticator.Request, failed // authorization header is not required anymore in case of a successful authentication. req.Header.Del("Authorization") + // delete standard front proxy headers + headerrequest.ClearAuthenticationHeaders( + req.Header, + standardRequestHeaderConfig.UsernameHeaders, + standardRequestHeaderConfig.GroupHeaders, + standardRequestHeaderConfig.ExtraHeaderPrefixes, + ) + + // also delete any custom front proxy headers + if requestHeaderConfig != nil { + headerrequest.ClearAuthenticationHeaders( + req.Header, + requestHeaderConfig.UsernameHeaders, + requestHeaderConfig.GroupHeaders, + requestHeaderConfig.ExtraHeaderPrefixes, + ) + } + req = req.WithContext(genericapirequest.WithUser(req.Context(), resp.User)) handler.ServeHTTP(w, req) }) diff --git a/vendor/k8s.io/apiserver/pkg/endpoints/handlers/create.go b/vendor/k8s.io/apiserver/pkg/endpoints/handlers/create.go index 1f3ce8094..0e88cdbb7 100644 --- a/vendor/k8s.io/apiserver/pkg/endpoints/handlers/create.go +++ b/vendor/k8s.io/apiserver/pkg/endpoints/handlers/create.go @@ -164,8 +164,13 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int userInfo, _ := request.UserFrom(ctx) if objectMeta, err := meta.Accessor(obj); err == nil { - // Wipe fields which cannot take user-provided values - rest.WipeObjectMetaSystemFields(objectMeta) + preserveObjectMetaSystemFields := false + if c, ok := r.(rest.SubresourceObjectMetaPreserver); ok && len(scope.Subresource) > 0 { + preserveObjectMetaSystemFields = c.PreserveRequestObjectMetaSystemFieldsOnSubresourceCreate() + } + if !preserveObjectMetaSystemFields { + rest.WipeObjectMetaSystemFields(objectMeta) + } // ensure namespace on the object is correct, or error if a conflicting namespace was set in the object if err := rest.EnsureObjectNamespaceMatchesRequestNamespace(rest.ExpectedNamespaceForResource(namespace, scope.Resource), objectMeta); err != nil { diff --git a/vendor/k8s.io/apiserver/pkg/endpoints/handlers/delete.go b/vendor/k8s.io/apiserver/pkg/endpoints/handlers/delete.go index 542400a00..2a7a5da4a 100644 --- a/vendor/k8s.io/apiserver/pkg/endpoints/handlers/delete.go +++ b/vendor/k8s.io/apiserver/pkg/endpoints/handlers/delete.go @@ -183,12 +183,10 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope *RequestSc return } - // enforce a timeout of at most requestTimeoutUpperBound (34s) or less if the user-provided - // timeout inside the parent context is lower than requestTimeoutUpperBound. - ctx, cancel := context.WithTimeout(req.Context(), requestTimeoutUpperBound) - defer cancel() - - ctx = request.WithNamespace(ctx, namespace) + // DELETECOLLECTION can be a lengthy operation, + // we should not impose any 34s timeout here. + // NOTE: This is similar to LIST which does not enforce a 34s timeout. + ctx := request.WithNamespace(req.Context(), namespace) outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, scope) if err != nil { diff --git a/vendor/k8s.io/apiserver/pkg/registry/rest/rest.go b/vendor/k8s.io/apiserver/pkg/registry/rest/rest.go index 6330ea8f5..7b8d90e60 100644 --- a/vendor/k8s.io/apiserver/pkg/registry/rest/rest.go +++ b/vendor/k8s.io/apiserver/pkg/registry/rest/rest.go @@ -203,6 +203,13 @@ type NamedCreater interface { Create(ctx context.Context, name string, obj runtime.Object, createValidation ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) } +// SubresourceObjectMetaPreserver adds configuration options to a Creater for subresources. +type SubresourceObjectMetaPreserver interface { + // PreserveRequestObjectMetaSystemFieldsOnSubresourceCreate indicates that a + // handler should preserve fields of ObjectMeta that are managed by the system. + PreserveRequestObjectMetaSystemFieldsOnSubresourceCreate() bool +} + // UpdatedObjectInfo provides information about an updated object to an Updater. // It requires access to the old object in order to return the newly updated object. type UpdatedObjectInfo interface { diff --git a/vendor/k8s.io/apiserver/pkg/server/config.go b/vendor/k8s.io/apiserver/pkg/server/config.go index d21ea2ef0..fd650f6d3 100644 --- a/vendor/k8s.io/apiserver/pkg/server/config.go +++ b/vendor/k8s.io/apiserver/pkg/server/config.go @@ -309,6 +309,8 @@ type AuthenticationInfo struct { APIAudiences authenticator.Audiences // Authenticator determines which subject is making the request Authenticator authenticator.Request + + RequestHeaderConfig *authenticatorfactory.RequestHeaderConfig } type AuthorizationInfo struct { @@ -813,7 +815,7 @@ func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler { if c.FlowControl != nil { workEstimatorCfg := flowcontrolrequest.DefaultWorkEstimatorConfig() requestWorkEstimator := flowcontrolrequest.NewWorkEstimator( - c.StorageObjectCountTracker.Get, c.FlowControl.GetInterestedWatchCount, workEstimatorCfg) + c.StorageObjectCountTracker.Get, c.FlowControl.GetInterestedWatchCount, workEstimatorCfg, c.FlowControl.GetMaxSeats) handler = filterlatency.TrackCompleted(handler) handler = genericfilters.WithPriorityAndFairness(handler, c.LongRunningFunc, c.FlowControl, requestWorkEstimator) handler = filterlatency.TrackStarted(handler, "priorityandfairness") @@ -834,7 +836,7 @@ func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler { failedHandler = filterlatency.TrackCompleted(failedHandler) handler = filterlatency.TrackCompleted(handler) - handler = genericapifilters.WithAuthentication(handler, c.Authentication.Authenticator, failedHandler, c.Authentication.APIAudiences) + handler = genericapifilters.WithAuthentication(handler, c.Authentication.Authenticator, failedHandler, c.Authentication.APIAudiences, c.Authentication.RequestHeaderConfig) handler = filterlatency.TrackStarted(handler, "authentication") handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true") diff --git a/vendor/k8s.io/apiserver/pkg/server/options/authentication.go b/vendor/k8s.io/apiserver/pkg/server/options/authentication.go index 8ff771af0..283ecc450 100644 --- a/vendor/k8s.io/apiserver/pkg/server/options/authentication.go +++ b/vendor/k8s.io/apiserver/pkg/server/options/authentication.go @@ -76,6 +76,16 @@ func (s *RequestHeaderAuthenticationOptions) Validate() []error { allErrors = append(allErrors, err) } + if len(s.UsernameHeaders) > 0 && !caseInsensitiveHas(s.UsernameHeaders, "X-Remote-User") { + klog.Warningf("--requestheader-username-headers is set without specifying the standard X-Remote-User header - API aggregation will not work") + } + if len(s.GroupHeaders) > 0 && !caseInsensitiveHas(s.GroupHeaders, "X-Remote-Group") { + klog.Warningf("--requestheader-group-headers is set without specifying the standard X-Remote-Group header - API aggregation will not work") + } + if len(s.ExtraHeaderPrefixes) > 0 && !caseInsensitiveHas(s.ExtraHeaderPrefixes, "X-Remote-Extra-") { + klog.Warningf("--requestheader-extra-headers-prefix is set without specifying the standard X-Remote-Extra- header prefix - API aggregation will not work") + } + return allErrors } @@ -89,6 +99,15 @@ func checkForWhiteSpaceOnly(flag string, headerNames ...string) error { return nil } +func caseInsensitiveHas(headers []string, header string) bool { + for _, h := range headers { + if strings.EqualFold(h, header) { + return true + } + } + return false +} + func (s *RequestHeaderAuthenticationOptions) AddFlags(fs *pflag.FlagSet) { if s == nil { return @@ -354,6 +373,7 @@ func (s *DelegatingAuthenticationOptions) ApplyTo(authenticationInfo *server.Aut } if requestHeaderConfig != nil { cfg.RequestHeaderConfig = requestHeaderConfig + authenticationInfo.RequestHeaderConfig = requestHeaderConfig if err = authenticationInfo.ApplyClientCert(cfg.RequestHeaderConfig.CAContentProvider, servingInfo); err != nil { return fmt.Errorf("unable to load request-header-client-ca-file: %v", err) } diff --git a/vendor/k8s.io/apiserver/pkg/server/storage/storage_factory.go b/vendor/k8s.io/apiserver/pkg/server/storage/storage_factory.go index d8de4cd84..3aebab7de 100644 --- a/vendor/k8s.io/apiserver/pkg/server/storage/storage_factory.go +++ b/vendor/k8s.io/apiserver/pkg/server/storage/storage_factory.go @@ -22,8 +22,6 @@ import ( "io/ioutil" "strings" - "k8s.io/klog/v2" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/sets" @@ -31,6 +29,7 @@ import ( "k8s.io/apiserver/pkg/storage/storagebackend" "k8s.io/apiserver/pkg/storage/value" utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/klog/v2" ) // Backend describes the storage servers, the information here should be enough @@ -53,8 +52,12 @@ type StorageFactory interface { // centralized control over the shape of etcd directories ResourcePrefix(groupResource schema.GroupResource) string + // Configs gets configurations for all of registered storage destinations. + Configs() []storagebackend.Config + // Backends gets all backends for all registered storage destinations. // Used for getting all instances for health validations. + // Deprecated: Use Configs instead Backends() []Backend } @@ -288,28 +291,76 @@ func (s *DefaultStorageFactory) NewConfig(groupResource schema.GroupResource) (* return storageConfig.ForResource(groupResource), nil } +// Configs implements StorageFactory. +func (s *DefaultStorageFactory) Configs() []storagebackend.Config { + return configs(s.StorageConfig, s.Overrides) +} + +// Configs gets configurations for all of registered storage destinations. +func Configs(storageConfig storagebackend.Config) []storagebackend.Config { + return configs(storageConfig, nil) +} + +// Returns all storage configurations including those for group resource overrides +func configs(storageConfig storagebackend.Config, grOverrides map[schema.GroupResource]groupResourceOverrides) []storagebackend.Config { + locations := sets.NewString() + configs := []storagebackend.Config{} + for _, loc := range storageConfig.Transport.ServerList { + // copy + newConfig := storageConfig + newConfig.Transport.ServerList = []string{loc} + configs = append(configs, newConfig) + locations.Insert(loc) + } + + for _, override := range grOverrides { + for _, loc := range override.etcdLocation { + if locations.Has(loc) { + continue + } + // copy + newConfig := storageConfig + override.Apply(&newConfig, &StorageCodecConfig{}) + newConfig.Transport.ServerList = []string{loc} + configs = append(configs, newConfig) + locations.Insert(loc) + } + } + return configs +} + +// Backends implements StorageFactory. +func (s *DefaultStorageFactory) Backends() []Backend { + return backends(s.StorageConfig, s.Overrides) +} + // Backends returns all backends for all registered storage destinations. // Used for getting all instances for health validations. -func (s *DefaultStorageFactory) Backends() []Backend { - servers := sets.NewString(s.StorageConfig.Transport.ServerList...) +// Deprecated: Validate health by passing storagebackend.Config directly to storagefactory.CreateProber. +func Backends(storageConfig storagebackend.Config) []Backend { + return backends(storageConfig, nil) +} + +func backends(storageConfig storagebackend.Config, grOverrides map[schema.GroupResource]groupResourceOverrides) []Backend { + servers := sets.NewString(storageConfig.Transport.ServerList...) - for _, overrides := range s.Overrides { + for _, overrides := range grOverrides { servers.Insert(overrides.etcdLocation...) } tlsConfig := &tls.Config{ InsecureSkipVerify: true, } - if len(s.StorageConfig.Transport.CertFile) > 0 && len(s.StorageConfig.Transport.KeyFile) > 0 { - cert, err := tls.LoadX509KeyPair(s.StorageConfig.Transport.CertFile, s.StorageConfig.Transport.KeyFile) + if len(storageConfig.Transport.CertFile) > 0 && len(storageConfig.Transport.KeyFile) > 0 { + cert, err := tls.LoadX509KeyPair(storageConfig.Transport.CertFile, storageConfig.Transport.KeyFile) if err != nil { klog.Errorf("failed to load key pair while getting backends: %s", err) } else { tlsConfig.Certificates = []tls.Certificate{cert} } } - if len(s.StorageConfig.Transport.TrustedCAFile) > 0 { - if caCert, err := ioutil.ReadFile(s.StorageConfig.Transport.TrustedCAFile); err != nil { + if len(storageConfig.Transport.TrustedCAFile) > 0 { + if caCert, err := ioutil.ReadFile(storageConfig.Transport.TrustedCAFile); err != nil { klog.Errorf("failed to read ca file while getting backends: %s", err) } else { caPool := x509.NewCertPool() diff --git a/vendor/k8s.io/apiserver/pkg/storage/cacher/cacher.go b/vendor/k8s.io/apiserver/pkg/storage/cacher/cacher.go index 24dd64d9e..0d70f2d65 100644 --- a/vendor/k8s.io/apiserver/pkg/storage/cacher/cacher.go +++ b/vendor/k8s.io/apiserver/pkg/storage/cacher/cacher.go @@ -477,7 +477,7 @@ func (c *Cacher) Watch(ctx context.Context, key string, opts storage.ListOptions return nil, err } - if err := c.ready.wait(); err != nil { + if err := c.ready.wait(ctx); err != nil { return nil, errors.NewServiceUnavailable(err.Error()) } @@ -567,7 +567,7 @@ func (c *Cacher) Get(ctx context.Context, key string, opts storage.GetOptions, o // Do not create a trace - it's not for free and there are tons // of Get requests. We can add it if it will be really needed. - if err := c.ready.wait(); err != nil { + if err := c.ready.wait(ctx); err != nil { return errors.NewServiceUnavailable(err.Error()) } @@ -657,7 +657,7 @@ func (c *Cacher) GetList(ctx context.Context, key string, opts storage.ListOptio utiltrace.Field{Key: "type", Value: c.objectType.String()}) defer trace.LogIfLong(500 * time.Millisecond) - if err := c.ready.wait(); err != nil { + if err := c.ready.wait(ctx); err != nil { return errors.NewServiceUnavailable(err.Error()) } trace.Step("Ready") @@ -1066,7 +1066,7 @@ func filterWithAttrsFunction(key string, p storage.SelectionPredicate) filterWit // LastSyncResourceVersion returns resource version to which the underlying cache is synced. func (c *Cacher) LastSyncResourceVersion() (uint64, error) { - if err := c.ready.wait(); err != nil { + if err := c.ready.wait(context.Background()); err != nil { return 0, errors.NewServiceUnavailable(err.Error()) } diff --git a/vendor/k8s.io/apiserver/pkg/storage/cacher/ready.go b/vendor/k8s.io/apiserver/pkg/storage/cacher/ready.go index 8278dd2b2..47e03fe9e 100644 --- a/vendor/k8s.io/apiserver/pkg/storage/cacher/ready.go +++ b/vendor/k8s.io/apiserver/pkg/storage/cacher/ready.go @@ -17,6 +17,7 @@ limitations under the License. package cacher import ( + "context" "fmt" "sync" ) @@ -30,67 +31,111 @@ const ( ) // ready is a three state condition variable that blocks until is Ready if is not Stopped. -// Its initial state is Pending. +// Its initial state is Pending and its state machine diagram is as follow. +// +// Pending <------> Ready -----> Stopped +// +// | ^ +// └---------------------------┘ type ready struct { - state status - c *sync.Cond + state status // represent the state of the variable + lock sync.RWMutex // protect the state variable + restartLock sync.Mutex // protect the transition from ready to pending where the channel is recreated + waitCh chan struct{} // blocks until is ready or stopped } func newReady() *ready { return &ready{ - c: sync.NewCond(&sync.RWMutex{}), - state: Pending, + waitCh: make(chan struct{}), + state: Pending, } } +// done close the channel once the state is Ready or Stopped +func (r *ready) done() chan struct{} { + r.restartLock.Lock() + defer r.restartLock.Unlock() + return r.waitCh +} + // wait blocks until it is Ready or Stopped, it returns an error if is Stopped. -func (r *ready) wait() error { - r.c.L.Lock() - defer r.c.L.Unlock() - for r.state == Pending { - r.c.Wait() - } - switch r.state { - case Ready: - return nil - case Stopped: - return fmt.Errorf("apiserver cacher is stopped") - default: - return fmt.Errorf("unexpected apiserver cache state: %v", r.state) +func (r *ready) wait(ctx context.Context) error { + for { + // r.done() only blocks if state is Pending + select { + case <-ctx.Done(): + return ctx.Err() + case <-r.done(): + } + + r.lock.RLock() + switch r.state { + case Pending: + // since we allow to switch between the states Pending and Ready + // if there is a quick transition from Pending -> Ready -> Pending + // a process that was waiting can get unblocked and see a Pending + // state again. If the state is Pending we have to wait again to + // avoid an inconsistent state on the system, with some processes not + // waiting despite the state moved back to Pending. + r.lock.RUnlock() + case Ready: + r.lock.RUnlock() + return nil + case Stopped: + r.lock.RUnlock() + return fmt.Errorf("apiserver cacher is stopped") + default: + r.lock.RUnlock() + return fmt.Errorf("unexpected apiserver cache state: %v", r.state) + } } } // check returns true only if it is Ready. func (r *ready) check() bool { - // TODO: Make check() function more sophisticated, in particular - // allow it to behave as "waitWithTimeout". - rwMutex := r.c.L.(*sync.RWMutex) - rwMutex.RLock() - defer rwMutex.RUnlock() + r.lock.RLock() + defer r.lock.RUnlock() return r.state == Ready } // set the state to Pending (false) or Ready (true), it does not have effect if the state is Stopped. func (r *ready) set(ok bool) { - r.c.L.Lock() - defer r.c.L.Unlock() + r.lock.Lock() + defer r.lock.Unlock() if r.state == Stopped { return } - if ok { + if ok && r.state == Pending { r.state = Ready - } else { + select { + case <-r.waitCh: + default: + close(r.waitCh) + } + } else if !ok && r.state == Ready { + // creating the waitCh can be racy if + // something enter the wait() method + select { + case <-r.waitCh: + r.restartLock.Lock() + r.waitCh = make(chan struct{}) + r.restartLock.Unlock() + default: + } r.state = Pending } - r.c.Broadcast() } // stop the condition variable and set it as Stopped. This state is irreversible. func (r *ready) stop() { - r.c.L.Lock() - defer r.c.L.Unlock() + r.lock.Lock() + defer r.lock.Unlock() if r.state != Stopped { r.state = Stopped - r.c.Broadcast() + } + select { + case <-r.waitCh: + default: + close(r.waitCh) } } diff --git a/vendor/k8s.io/apiserver/pkg/storage/etcd3/healthcheck.go b/vendor/k8s.io/apiserver/pkg/storage/etcd3/healthcheck.go index ad051d2d6..3d4898103 100644 --- a/vendor/k8s.io/apiserver/pkg/storage/etcd3/healthcheck.go +++ b/vendor/k8s.io/apiserver/pkg/storage/etcd3/healthcheck.go @@ -28,6 +28,7 @@ type etcdHealth struct { } // EtcdHealthCheck decodes data returned from etcd /healthz handler. +// Deprecated: Validate health by passing storagebackend.Config directly to storagefactory.CreateProber. func EtcdHealthCheck(data []byte) error { obj := etcdHealth{} if err := json.Unmarshal(data, &obj); err != nil { diff --git a/vendor/k8s.io/apiserver/pkg/storage/storagebackend/factory/etcd3.go b/vendor/k8s.io/apiserver/pkg/storage/storagebackend/factory/etcd3.go index 55e2cc0b4..2f9908bd3 100644 --- a/vendor/k8s.io/apiserver/pkg/storage/storagebackend/factory/etcd3.go +++ b/vendor/k8s.io/apiserver/pkg/storage/storagebackend/factory/etcd3.go @@ -127,13 +127,12 @@ func newETCD3Check(c storagebackend.Config, timeout time.Duration, stopCh <-chan // constructing the etcd v3 client blocks and times out if etcd is not available. // retry in a loop in the background until we successfully create the client, storing the client or error encountered - lock := sync.Mutex{} - var client *clientv3.Client + lock := sync.RWMutex{} + var prober *etcd3Prober clientErr := fmt.Errorf("etcd client connection not yet established") go wait.PollUntil(time.Second, func() (bool, error) { - newClient, err := newETCD3Client(c.Transport) - + newProber, err := newETCD3Prober(c) lock.Lock() defer lock.Unlock() @@ -141,7 +140,7 @@ func newETCD3Check(c storagebackend.Config, timeout time.Duration, stopCh <-chan select { case <-stopCh: if err == nil { - newClient.Close() + newProber.Close() } return true, nil default: @@ -151,7 +150,7 @@ func newETCD3Check(c storagebackend.Config, timeout time.Duration, stopCh <-chan clientErr = err return false, nil } - client = newClient + prober = newProber clientErr = nil return true, nil }, stopCh) @@ -163,8 +162,8 @@ func newETCD3Check(c storagebackend.Config, timeout time.Duration, stopCh <-chan lock.Lock() defer lock.Unlock() - if client != nil { - client.Close() + if prober != nil { + prober.Close() clientErr = fmt.Errorf("server is shutting down") } }() @@ -183,7 +182,7 @@ func newETCD3Check(c storagebackend.Config, timeout time.Duration, stopCh <-chan ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() // See https://github.com/etcd-io/etcd/blob/c57f8b3af865d1b531b979889c602ba14377420e/etcdctl/ctlv3/command/ep_command.go#L118 - _, err := client.Get(ctx, path.Join("/", c.Prefix, "health")) + err := prober.Probe(ctx) if err == nil { return nil } @@ -191,6 +190,49 @@ func newETCD3Check(c storagebackend.Config, timeout time.Duration, stopCh <-chan }, nil } +func newETCD3Prober(c storagebackend.Config) (*etcd3Prober, error) { + client, err := newETCD3Client(c.Transport) + if err != nil { + return nil, err + } + return &etcd3Prober{ + client: client, + prefix: c.Prefix, + }, nil +} + +type etcd3Prober struct { + prefix string + + mux sync.RWMutex + client *clientv3.Client + closed bool +} + +func (p *etcd3Prober) Close() error { + p.mux.Lock() + defer p.mux.Unlock() + if !p.closed { + p.closed = true + return p.client.Close() + } + return fmt.Errorf("prober was closed") +} + +func (p *etcd3Prober) Probe(ctx context.Context) error { + p.mux.RLock() + defer p.mux.RUnlock() + if p.closed { + return fmt.Errorf("prober was closed") + } + // See https://github.com/etcd-io/etcd/blob/c57f8b3af865d1b531b979889c602ba14377420e/etcdctl/ctlv3/command/ep_command.go#L118 + _, err := p.client.Get(ctx, path.Join("/", p.prefix, "health")) + if err != nil { + return fmt.Errorf("error getting data from etcd: %w", err) + } + return nil +} + var newETCD3Client = func(c storagebackend.TransportConfig) (*clientv3.Client, error) { tlsInfo := transport.TLSInfo{ CertFile: c.CertFile, diff --git a/vendor/k8s.io/apiserver/pkg/storage/storagebackend/factory/factory.go b/vendor/k8s.io/apiserver/pkg/storage/storagebackend/factory/factory.go index 4c8a409d6..c8cdd19b9 100644 --- a/vendor/k8s.io/apiserver/pkg/storage/storagebackend/factory/factory.go +++ b/vendor/k8s.io/apiserver/pkg/storage/storagebackend/factory/factory.go @@ -17,6 +17,7 @@ limitations under the License. package factory import ( + "context" "fmt" "k8s.io/apimachinery/pkg/runtime" @@ -61,3 +62,20 @@ func CreateReadyCheck(c storagebackend.Config, stopCh <-chan struct{}) (func() e return nil, fmt.Errorf("unknown storage type: %s", c.Type) } } + +func CreateProber(c storagebackend.Config) (Prober, error) { + switch c.Type { + case storagebackend.StorageTypeETCD2: + return nil, fmt.Errorf("%s is no longer a supported storage backend", c.Type) + case storagebackend.StorageTypeUnset, storagebackend.StorageTypeETCD3: + return newETCD3Prober(c) + default: + return nil, fmt.Errorf("unknown storage type: %s", c.Type) + } +} + +// Prober is an interface that defines the Probe function for doing etcd readiness/liveness checks. +type Prober interface { + Probe(ctx context.Context) error + Close() error +} diff --git a/vendor/k8s.io/apiserver/pkg/util/flowcontrol/apf_controller.go b/vendor/k8s.io/apiserver/pkg/util/flowcontrol/apf_controller.go index cd4eff7be..9468a18cb 100644 --- a/vendor/k8s.io/apiserver/pkg/util/flowcontrol/apf_controller.go +++ b/vendor/k8s.io/apiserver/pkg/util/flowcontrol/apf_controller.go @@ -59,6 +59,11 @@ import ( const timeFmt = "2006-01-02T15:04:05.999" +const ( + // priorityLevelMaxSeatsPercent is the percentage of the nominalCL used as max seats allocatable from work estimator + priorityLevelMaxSeatsPercent = float64(0.15) +) + // This file contains a simple local (to the apiserver) controller // that digests API Priority and Fairness config objects (FlowSchema // and PriorityLevelConfiguration) into the data structure that the @@ -136,6 +141,12 @@ type configController struct { // watchTracker implements the necessary WatchTracker interface. WatchTracker + // MaxSeatsTracker tracks the maximum seats that should be allocatable from the + // work estimator for a given priority level. This controller does not enforce + // any limits on max seats stored in this tracker, it is up to the work estimator + // to set lower/upper limits on max seats (currently min=1, max=10). + MaxSeatsTracker + // the most recent update attempts, ordered by increasing age. // Consumer trims to keep only the last minute's worth of entries. // The controller uses this to limit itself to at most six updates @@ -214,6 +225,7 @@ func newTestableController(config TestableConfig) *configController { flowcontrolClient: config.FlowcontrolClient, priorityLevelStates: make(map[string]*priorityLevelState), WatchTracker: NewWatchTracker(), + MaxSeatsTracker: NewMaxSeatsTracker(), } klog.V(2).Infof("NewTestableController %q with serverConcurrencyLimit=%d, requestWaitLimit=%s, name=%s, asFieldManager=%q", cfgCtlr.name, cfgCtlr.serverConcurrencyLimit, cfgCtlr.requestWaitLimit, cfgCtlr.name, cfgCtlr.asFieldManager) // Start with longish delay because conflicts will be between @@ -628,6 +640,7 @@ func (meal *cfgMeal) processOldPLsLocked() { // draining and no use is coming from another // goroutine klog.V(3).Infof("Removing undesired priority level %q (nilQueues=%v), Type=%v", plName, plState.queues == nil, plState.pl.Spec.Type) + meal.cfgCtlr.MaxSeatsTracker.ForgetPriorityLevel(plName) continue } if !plState.quiescing { @@ -675,6 +688,17 @@ func (meal *cfgMeal) finishQueueSetReconfigsLocked() { var waitLimit int if qCfg := plState.pl.Spec.Limited.LimitResponse.Queuing; qCfg != nil { waitLimit = int(qCfg.Queues * qCfg.QueueLengthLimit) + + // Max seats allocatable from work estimator is calculated as MAX(1, MIN(0.15 * nominalCL, nominalCL/handSize)). + // This is to keep max seats relative to total available concurrency with a minimum value of 1. + // 15% of nominal concurrency was chosen since it preserved the previous max seats of 10 for default priority levels + // when using apiserver's default total server concurrency of 600 (--max-requests-inflight=400, --max-mutating-requests-inflight=200). + // This ensures that clusters with relatively high inflight requests will continue to use a max seats of 10 + // while clusters with lower inflight requests will use max seats no greater than nominalCL/handSize. + // Calculated max seats can return arbitrarily high values but work estimator currently limits max seats at 10. + handSize := plState.pl.Spec.Limited.LimitResponse.Queuing.HandSize + maxSeats := uint64(math.Max(1, math.Min(math.Ceil(float64(concurrencyLimit)*priorityLevelMaxSeatsPercent), float64(int32(concurrencyLimit)/handSize)))) + meal.cfgCtlr.MaxSeatsTracker.SetMaxSeats(plName, maxSeats) } meal.maxWaitingRequests += waitLimit diff --git a/vendor/k8s.io/apiserver/pkg/util/flowcontrol/apf_filter.go b/vendor/k8s.io/apiserver/pkg/util/flowcontrol/apf_filter.go index 71f147867..38b88bec6 100644 --- a/vendor/k8s.io/apiserver/pkg/util/flowcontrol/apf_filter.go +++ b/vendor/k8s.io/apiserver/pkg/util/flowcontrol/apf_filter.go @@ -76,6 +76,10 @@ type Interface interface { // WatchTracker provides the WatchTracker interface. WatchTracker + + // MaxSeatsTracker is invoked from the work estimator to track max seats + // that can be occupied by a request for a priority level. + MaxSeatsTracker } // This request filter implements https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/1040-priority-and-fairness/README.md diff --git a/vendor/k8s.io/apiserver/pkg/util/flowcontrol/max_seats.go b/vendor/k8s.io/apiserver/pkg/util/flowcontrol/max_seats.go new file mode 100644 index 000000000..18f88ab3b --- /dev/null +++ b/vendor/k8s.io/apiserver/pkg/util/flowcontrol/max_seats.go @@ -0,0 +1,66 @@ +/* +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 flowcontrol + +import ( + "sync" +) + +// MaxSeatsTracker is used to track max seats allocatable per priority level from the work estimator +type MaxSeatsTracker interface { + // GetMaxSeats returns the maximum seats a request should occupy for a given priority level. + GetMaxSeats(priorityLevelName string) uint64 + + // SetMaxSeats configures max seats for a priority level. + SetMaxSeats(priorityLevelName string, maxSeats uint64) + + // ForgetPriorityLevel removes max seats tracking for a priority level. + ForgetPriorityLevel(priorityLevelName string) +} + +type maxSeatsTracker struct { + sync.RWMutex + + maxSeats map[string]uint64 +} + +func NewMaxSeatsTracker() MaxSeatsTracker { + return &maxSeatsTracker{ + maxSeats: make(map[string]uint64), + } +} + +func (m *maxSeatsTracker) GetMaxSeats(plName string) uint64 { + m.RLock() + defer m.RUnlock() + + return m.maxSeats[plName] +} + +func (m *maxSeatsTracker) SetMaxSeats(plName string, maxSeats uint64) { + m.Lock() + defer m.Unlock() + + m.maxSeats[plName] = maxSeats +} + +func (m *maxSeatsTracker) ForgetPriorityLevel(plName string) { + m.Lock() + defer m.Unlock() + + delete(m.maxSeats, plName) +} diff --git a/vendor/k8s.io/apiserver/pkg/util/flowcontrol/request/config.go b/vendor/k8s.io/apiserver/pkg/util/flowcontrol/request/config.go index b6db19209..c51435b15 100644 --- a/vendor/k8s.io/apiserver/pkg/util/flowcontrol/request/config.go +++ b/vendor/k8s.io/apiserver/pkg/util/flowcontrol/request/config.go @@ -24,7 +24,7 @@ import ( const ( minimumSeats = 1 - maximumSeats = 10 + maximumSeatsLimit = 10 objectsPerSeat = 100.0 watchesPerSeat = 10.0 enableMutatingWorkEstimator = true @@ -39,12 +39,13 @@ type WorkEstimatorConfig struct { // MinimumSeats is the minimum number of seats a request must occupy. MinimumSeats uint64 `json:"minimumSeats,omitempty"` - // MaximumSeats is the maximum number of seats a request can occupy + + // MaximumSeatsLimit is an upper limit on the max seats a request can occupy. // // NOTE: work_estimate_seats_samples metric uses the value of maximumSeats // as the upper bound, so when we change maximumSeats we should also // update the buckets of the metric. - MaximumSeats uint64 `json:"maximumSeats,omitempty"` + MaximumSeatsLimit uint64 `json:"maximumSeatsLimit,omitempty"` } // ListWorkEstimatorConfig holds work estimator parameters related to list requests. @@ -66,7 +67,7 @@ type MutatingWorkEstimatorConfig struct { func DefaultWorkEstimatorConfig() *WorkEstimatorConfig { return &WorkEstimatorConfig{ MinimumSeats: minimumSeats, - MaximumSeats: maximumSeats, + MaximumSeatsLimit: maximumSeatsLimit, ListWorkEstimatorConfig: defaultListWorkEstimatorConfig(), MutatingWorkEstimatorConfig: defaultMutatingWorkEstimatorConfig(), } diff --git a/vendor/k8s.io/apiserver/pkg/util/flowcontrol/request/list_work_estimator.go b/vendor/k8s.io/apiserver/pkg/util/flowcontrol/request/list_work_estimator.go index 75d70a0ad..e49e08109 100644 --- a/vendor/k8s.io/apiserver/pkg/util/flowcontrol/request/list_work_estimator.go +++ b/vendor/k8s.io/apiserver/pkg/util/flowcontrol/request/list_work_estimator.go @@ -29,10 +29,11 @@ import ( "k8s.io/klog/v2" ) -func newListWorkEstimator(countFn objectCountGetterFunc, config *WorkEstimatorConfig) WorkEstimatorFunc { +func newListWorkEstimator(countFn objectCountGetterFunc, config *WorkEstimatorConfig, maxSeatsFn maxSeatsFunc) WorkEstimatorFunc { estimator := &listWorkEstimator{ config: config, countGetterFn: countFn, + maxSeatsFn: maxSeatsFn, } return estimator.estimate } @@ -40,14 +41,21 @@ func newListWorkEstimator(countFn objectCountGetterFunc, config *WorkEstimatorCo type listWorkEstimator struct { config *WorkEstimatorConfig countGetterFn objectCountGetterFunc + maxSeatsFn maxSeatsFunc } func (e *listWorkEstimator) estimate(r *http.Request, flowSchemaName, priorityLevelName string) WorkEstimate { + minSeats := e.config.MinimumSeats + maxSeats := e.maxSeatsFn(priorityLevelName) + if maxSeats == 0 || maxSeats > e.config.MaximumSeatsLimit { + maxSeats = e.config.MaximumSeatsLimit + } + requestInfo, ok := apirequest.RequestInfoFrom(r.Context()) if !ok { // no RequestInfo should never happen, but to be on the safe side // let's return maximumSeats - return WorkEstimate{InitialSeats: e.config.MaximumSeats} + return WorkEstimate{InitialSeats: maxSeats} } if requestInfo.Name != "" { @@ -56,7 +64,7 @@ func (e *listWorkEstimator) estimate(r *http.Request, flowSchemaName, priorityLe // Example of such list requests: // /apis/certificates.k8s.io/v1/certificatesigningrequests?fieldSelector=metadata.name%3Dcsr-xxs4m // /api/v1/namespaces/test/configmaps?fieldSelector=metadata.name%3Dbig-deployment-1&limit=500&resourceVersion=0 - return WorkEstimate{InitialSeats: e.config.MinimumSeats} + return WorkEstimate{InitialSeats: minSeats} } query := r.URL.Query() @@ -66,7 +74,7 @@ func (e *listWorkEstimator) estimate(r *http.Request, flowSchemaName, priorityLe // This request is destined to fail in the validation layer, // return maximumSeats for this request to be consistent. - return WorkEstimate{InitialSeats: e.config.MaximumSeats} + return WorkEstimate{InitialSeats: maxSeats} } isListFromCache := !shouldListFromStorage(query, &listOptions) @@ -77,7 +85,7 @@ func (e *listWorkEstimator) estimate(r *http.Request, flowSchemaName, priorityLe // be conservative here and allocate maximum seats to this list request. // NOTE: if a CRD is removed, its count will go stale first and then the // pruner will eventually remove the CRD from the cache. - return WorkEstimate{InitialSeats: e.config.MaximumSeats} + return WorkEstimate{InitialSeats: maxSeats} case err == ObjectCountNotFoundErr: // there are multiple scenarios in which we can see this error: // a. the type is truly unknown, a typo on the caller's part. @@ -91,12 +99,12 @@ func (e *listWorkEstimator) estimate(r *http.Request, flowSchemaName, priorityLe // when aggregated API calls are overestimated, we allocate the minimum // possible seats (see #109106 as an example when being more conservative // led to problems). - return WorkEstimate{InitialSeats: e.config.MinimumSeats} + return WorkEstimate{InitialSeats: minSeats} case err != nil: // we should never be here since Get returns either ObjectCountStaleErr or // ObjectCountNotFoundErr, return maximumSeats to be on the safe side. klog.ErrorS(err, "Unexpected error from object count tracker") - return WorkEstimate{InitialSeats: e.config.MaximumSeats} + return WorkEstimate{InitialSeats: maxSeats} } limit := numStored @@ -125,11 +133,11 @@ func (e *listWorkEstimator) estimate(r *http.Request, flowSchemaName, priorityLe seats := uint64(math.Ceil(float64(estimatedObjectsToBeProcessed) / e.config.ObjectsPerSeat)) // make sure we never return a seat of zero - if seats < e.config.MinimumSeats { - seats = e.config.MinimumSeats + if seats < minSeats { + seats = minSeats } - if seats > e.config.MaximumSeats { - seats = e.config.MaximumSeats + if seats > maxSeats { + seats = maxSeats } return WorkEstimate{InitialSeats: seats} } diff --git a/vendor/k8s.io/apiserver/pkg/util/flowcontrol/request/mutating_work_estimator.go b/vendor/k8s.io/apiserver/pkg/util/flowcontrol/request/mutating_work_estimator.go index 990aa6324..aa2807fd1 100644 --- a/vendor/k8s.io/apiserver/pkg/util/flowcontrol/request/mutating_work_estimator.go +++ b/vendor/k8s.io/apiserver/pkg/util/flowcontrol/request/mutating_work_estimator.go @@ -25,25 +25,33 @@ import ( "k8s.io/apiserver/pkg/util/flowcontrol/metrics" ) -func newMutatingWorkEstimator(countFn watchCountGetterFunc, config *WorkEstimatorConfig) WorkEstimatorFunc { +func newMutatingWorkEstimator(countFn watchCountGetterFunc, config *WorkEstimatorConfig, maxSeatsFn maxSeatsFunc) WorkEstimatorFunc { estimator := &mutatingWorkEstimator{ - config: config, - countFn: countFn, + config: config, + countFn: countFn, + maxSeatsFn: maxSeatsFn, } return estimator.estimate } type mutatingWorkEstimator struct { - config *WorkEstimatorConfig - countFn watchCountGetterFunc + config *WorkEstimatorConfig + countFn watchCountGetterFunc + maxSeatsFn maxSeatsFunc } func (e *mutatingWorkEstimator) estimate(r *http.Request, flowSchemaName, priorityLevelName string) WorkEstimate { + minSeats := e.config.MinimumSeats + maxSeats := e.maxSeatsFn(priorityLevelName) + if maxSeats == 0 || maxSeats > e.config.MaximumSeatsLimit { + maxSeats = e.config.MaximumSeatsLimit + } + // TODO(wojtekt): Remove once we tune the algorithm to not fail // scalability tests. if !e.config.Enabled { return WorkEstimate{ - InitialSeats: 1, + InitialSeats: minSeats, } } @@ -52,11 +60,12 @@ func (e *mutatingWorkEstimator) estimate(r *http.Request, flowSchemaName, priori // no RequestInfo should never happen, but to be on the safe side // let's return a large value. return WorkEstimate{ - InitialSeats: 1, - FinalSeats: e.config.MaximumSeats, + InitialSeats: minSeats, + FinalSeats: maxSeats, AdditionalLatency: e.config.eventAdditionalDuration(), } } + watchCount := e.countFn(requestInfo) metrics.ObserveWatchCount(r.Context(), priorityLevelName, flowSchemaName, watchCount) @@ -117,8 +126,8 @@ func (e *mutatingWorkEstimator) estimate(r *http.Request, flowSchemaName, priori // // TODO: Confirm that the current cap of maximumSeats allow us to // achieve the above. - if finalSeats > e.config.MaximumSeats { - finalSeats = e.config.MaximumSeats + if finalSeats > maxSeats { + finalSeats = maxSeats } additionalLatency = finalWork.DurationPerSeat(float64(finalSeats)) } diff --git a/vendor/k8s.io/apiserver/pkg/util/flowcontrol/request/width.go b/vendor/k8s.io/apiserver/pkg/util/flowcontrol/request/width.go index 86f042584..e55cc714b 100644 --- a/vendor/k8s.io/apiserver/pkg/util/flowcontrol/request/width.go +++ b/vendor/k8s.io/apiserver/pkg/util/flowcontrol/request/width.go @@ -61,15 +61,19 @@ type objectCountGetterFunc func(string) (int64, error) // number of watchers potentially interested in a given request. type watchCountGetterFunc func(*apirequest.RequestInfo) int +// MaxSeatsFunc represents a function that returns the maximum seats +// allowed for the work estimator for a given priority level. +type maxSeatsFunc func(priorityLevelName string) uint64 + // NewWorkEstimator estimates the work that will be done by a given request, // if no WorkEstimatorFunc matches the given request then the default // work estimate of 1 seat is allocated to the request. -func NewWorkEstimator(objectCountFn objectCountGetterFunc, watchCountFn watchCountGetterFunc, config *WorkEstimatorConfig) WorkEstimatorFunc { +func NewWorkEstimator(objectCountFn objectCountGetterFunc, watchCountFn watchCountGetterFunc, config *WorkEstimatorConfig, maxSeatsFn maxSeatsFunc) WorkEstimatorFunc { estimator := &workEstimator{ minimumSeats: config.MinimumSeats, - maximumSeats: config.MaximumSeats, - listWorkEstimator: newListWorkEstimator(objectCountFn, config), - mutatingWorkEstimator: newMutatingWorkEstimator(watchCountFn, config), + maximumSeatsLimit: config.MaximumSeatsLimit, + listWorkEstimator: newListWorkEstimator(objectCountFn, config, maxSeatsFn), + mutatingWorkEstimator: newMutatingWorkEstimator(watchCountFn, config, maxSeatsFn), } return estimator.estimate } @@ -86,8 +90,8 @@ func (e WorkEstimatorFunc) EstimateWork(r *http.Request, flowSchemaName, priorit type workEstimator struct { // the minimum number of seats a request must occupy minimumSeats uint64 - // the maximum number of seats a request can occupy - maximumSeats uint64 + // the default maximum number of seats a request can occupy + maximumSeatsLimit uint64 // listWorkEstimator estimates work for list request(s) listWorkEstimator WorkEstimatorFunc // mutatingWorkEstimator calculates the width of mutating request(s) @@ -99,7 +103,7 @@ func (e *workEstimator) estimate(r *http.Request, flowSchemaName, priorityLevelN if !ok { klog.ErrorS(fmt.Errorf("no RequestInfo found in context"), "Failed to estimate work for the request", "URI", r.RequestURI) // no RequestInfo should never happen, but to be on the safe side let's return maximumSeats - return WorkEstimate{InitialSeats: e.maximumSeats} + return WorkEstimate{InitialSeats: e.maximumSeatsLimit} } switch requestInfo.Verb { diff --git a/vendor/k8s.io/client-go/tools/cache/controller.go b/vendor/k8s.io/client-go/tools/cache/controller.go index 0762da3be..96005ff58 100644 --- a/vendor/k8s.io/client-go/tools/cache/controller.go +++ b/vendor/k8s.io/client-go/tools/cache/controller.go @@ -353,17 +353,6 @@ func NewIndexerInformer( return clientState, newInformer(lw, objType, resyncPeriod, h, clientState, nil) } -// TransformFunc allows for transforming an object before it will be processed -// and put into the controller cache and before the corresponding handlers will -// be called on it. -// TransformFunc (similarly to ResourceEventHandler functions) should be able -// to correctly handle the tombstone of type cache.DeletedFinalStateUnknown -// -// The most common usage pattern is to clean-up some parts of the object to -// reduce component memory usage if a given component doesn't care about them. -// given controller doesn't care for them -type TransformFunc func(interface{}) (interface{}, error) - // NewTransformingInformer returns a Store and a controller for populating // the store while also providing event notifications. You should only used // the returned Store for Get/List operations; Add/Modify/Deletes will cause @@ -411,19 +400,11 @@ func processDeltas( // Object which receives event notifications from the given deltas handler ResourceEventHandler, clientState Store, - transformer TransformFunc, deltas Deltas, ) error { // from oldest to newest for _, d := range deltas { obj := d.Object - if transformer != nil { - var err error - obj, err = transformer(obj) - if err != nil { - return err - } - } switch d.Type { case Sync, Replaced, Added, Updated: @@ -475,6 +456,7 @@ func newInformer( fifo := NewDeltaFIFOWithOptions(DeltaFIFOOptions{ KnownObjects: clientState, EmitDeltaTypeReplaced: true, + Transformer: transformer, }) cfg := &Config{ @@ -486,7 +468,7 @@ func newInformer( Process: func(obj interface{}) error { if deltas, ok := obj.(Deltas); ok { - return processDeltas(h, clientState, transformer, deltas) + return processDeltas(h, clientState, deltas) } return errors.New("object given as Process argument is not Deltas") }, diff --git a/vendor/k8s.io/client-go/tools/cache/delta_fifo.go b/vendor/k8s.io/client-go/tools/cache/delta_fifo.go index 0c13a41f0..84f3ab9ca 100644 --- a/vendor/k8s.io/client-go/tools/cache/delta_fifo.go +++ b/vendor/k8s.io/client-go/tools/cache/delta_fifo.go @@ -51,6 +51,10 @@ type DeltaFIFOOptions struct { // When true, `Replaced` events will be sent for items passed to a Replace() call. // When false, `Sync` events will be sent instead. EmitDeltaTypeReplaced bool + + // If set, will be called for objects before enqueueing them. Please + // see the comment on TransformFunc for details. + Transformer TransformFunc } // DeltaFIFO is like FIFO, but differs in two ways. One is that the @@ -129,8 +133,32 @@ type DeltaFIFO struct { // emitDeltaTypeReplaced is whether to emit the Replaced or Sync // DeltaType when Replace() is called (to preserve backwards compat). emitDeltaTypeReplaced bool + + // Called with every object if non-nil. + transformer TransformFunc } +// TransformFunc allows for transforming an object before it will be processed. +// TransformFunc (similarly to ResourceEventHandler functions) should be able +// to correctly handle the tombstone of type cache.DeletedFinalStateUnknown. +// +// New in v1.27: In such cases, the contained object will already have gone +// through the transform object separately (when it was added / updated prior +// to the delete), so the TransformFunc can likely safely ignore such objects +// (i.e., just return the input object). +// +// The most common usage pattern is to clean-up some parts of the object to +// reduce component memory usage if a given component doesn't care about them. +// +// New in v1.27: unless the object is a DeletedFinalStateUnknown, TransformFunc +// sees the object before any other actor, and it is now safe to mutate the +// object in place instead of making a copy. +// +// Note that TransformFunc is called while inserting objects into the +// notification queue and is therefore extremely performance sensitive; please +// do not do anything that will take a long time. +type TransformFunc func(interface{}) (interface{}, error) + // DeltaType is the type of a change (addition, deletion, etc) type DeltaType string @@ -227,6 +255,7 @@ func NewDeltaFIFOWithOptions(opts DeltaFIFOOptions) *DeltaFIFO { knownObjects: opts.KnownObjects, emitDeltaTypeReplaced: opts.EmitDeltaTypeReplaced, + transformer: opts.Transformer, } f.cond.L = &f.lock return f @@ -411,6 +440,21 @@ func (f *DeltaFIFO) queueActionLocked(actionType DeltaType, obj interface{}) err if err != nil { return KeyError{obj, err} } + + // Every object comes through this code path once, so this is a good + // place to call the transform func. If obj is a + // DeletedFinalStateUnknown tombstone, then the containted inner object + // will already have gone through the transformer, but we document that + // this can happen. In cases involving Replace(), such an object can + // come through multiple times. + if f.transformer != nil { + var err error + obj, err = f.transformer(obj) + if err != nil { + return err + } + } + oldDeltas := f.items[id] newDeltas := append(oldDeltas, Delta{actionType, obj}) newDeltas = dedupDeltas(newDeltas) @@ -566,12 +610,11 @@ func (f *DeltaFIFO) Pop(process PopProcessFunc) (interface{}, error) { // using the Sync or Replace DeltaType and then (2) it does some deletions. // In particular: for every pre-existing key K that is not the key of // an object in `list` there is the effect of -// `Delete(DeletedFinalStateUnknown{K, O})` where O is current object -// of K. If `f.knownObjects == nil` then the pre-existing keys are -// those in `f.items` and the current object of K is the `.Newest()` -// of the Deltas associated with K. Otherwise the pre-existing keys -// are those listed by `f.knownObjects` and the current object of K is -// what `f.knownObjects.GetByKey(K)` returns. +// `Delete(DeletedFinalStateUnknown{K, O})` where O is the latest known +// object of K. The pre-existing keys are those in the union set of the keys in +// `f.items` and `f.knownObjects` (if not nil). The last known object for key K is +// the one present in the last delta in `f.items`. If there is no delta for K +// in `f.items`, it is the object in `f.knownObjects` func (f *DeltaFIFO) Replace(list []interface{}, _ string) error { f.lock.Lock() defer f.lock.Unlock() @@ -595,51 +638,23 @@ func (f *DeltaFIFO) Replace(list []interface{}, _ string) error { } } - if f.knownObjects == nil { - // Do deletion detection against our own list. - queuedDeletions := 0 - for k, oldItem := range f.items { - if keys.Has(k) { - continue - } - // Delete pre-existing items not in the new list. - // This could happen if watch deletion event was missed while - // disconnected from apiserver. - var deletedObj interface{} - if n := oldItem.Newest(); n != nil { - deletedObj = n.Object - } - queuedDeletions++ - if err := f.queueActionLocked(Deleted, DeletedFinalStateUnknown{k, deletedObj}); err != nil { - return err - } - } - - if !f.populated { - f.populated = true - // While there shouldn't be any queued deletions in the initial - // population of the queue, it's better to be on the safe side. - f.initialPopulationCount = keys.Len() + queuedDeletions - } - - return nil - } - - // Detect deletions not already in the queue. - knownKeys := f.knownObjects.ListKeys() + // Do deletion detection against objects in the queue queuedDeletions := 0 - for _, k := range knownKeys { + for k, oldItem := range f.items { if keys.Has(k) { continue } - - deletedObj, exists, err := f.knownObjects.GetByKey(k) - if err != nil { - deletedObj = nil - klog.Errorf("Unexpected error %v during lookup of key %v, placing DeleteFinalStateUnknown marker without object", err, k) - } else if !exists { - deletedObj = nil - klog.Infof("Key %v does not exist in known objects store, placing DeleteFinalStateUnknown marker without object", k) + // Delete pre-existing items not in the new list. + // This could happen if watch deletion event was missed while + // disconnected from apiserver. + var deletedObj interface{} + if n := oldItem.Newest(); n != nil { + deletedObj = n.Object + + // if the previous object is a DeletedFinalStateUnknown, we have to extract the actual Object + if d, ok := deletedObj.(DeletedFinalStateUnknown); ok { + deletedObj = d.Obj + } } queuedDeletions++ if err := f.queueActionLocked(Deleted, DeletedFinalStateUnknown{k, deletedObj}); err != nil { @@ -647,6 +662,32 @@ func (f *DeltaFIFO) Replace(list []interface{}, _ string) error { } } + if f.knownObjects != nil { + // Detect deletions for objects not present in the queue, but present in KnownObjects + knownKeys := f.knownObjects.ListKeys() + for _, k := range knownKeys { + if keys.Has(k) { + continue + } + if len(f.items[k]) > 0 { + continue + } + + deletedObj, exists, err := f.knownObjects.GetByKey(k) + if err != nil { + deletedObj = nil + klog.Errorf("Unexpected error %v during lookup of key %v, placing DeleteFinalStateUnknown marker without object", err, k) + } else if !exists { + deletedObj = nil + klog.Infof("Key %v does not exist in known objects store, placing DeleteFinalStateUnknown marker without object", k) + } + queuedDeletions++ + if err := f.queueActionLocked(Deleted, DeletedFinalStateUnknown{k, deletedObj}); err != nil { + return err + } + } + } + if !f.populated { f.populated = true f.initialPopulationCount = keys.Len() + queuedDeletions diff --git a/vendor/k8s.io/client-go/tools/cache/shared_informer.go b/vendor/k8s.io/client-go/tools/cache/shared_informer.go index 9f42782d1..35ebd396c 100644 --- a/vendor/k8s.io/client-go/tools/cache/shared_informer.go +++ b/vendor/k8s.io/client-go/tools/cache/shared_informer.go @@ -190,10 +190,7 @@ type SharedInformer interface { // // Must be set before starting the informer. // - // Note: Since the object given to the handler may be already shared with - // other goroutines, it is advisable to copy the object being - // transform before mutating it at all and returning the copy to prevent - // data races. + // Please see the comment on TransformFunc for more details. SetTransform(handler TransformFunc) error } @@ -404,6 +401,7 @@ func (s *sharedIndexInformer) Run(stopCh <-chan struct{}) { fifo := NewDeltaFIFOWithOptions(DeltaFIFOOptions{ KnownObjects: s.indexer, EmitDeltaTypeReplaced: true, + Transformer: s.transform, }) cfg := &Config{ @@ -568,7 +566,7 @@ func (s *sharedIndexInformer) HandleDeltas(obj interface{}) error { defer s.blockDeltas.Unlock() if deltas, ok := obj.(Deltas); ok { - return processDeltas(s, s.indexer, s.transform, deltas) + return processDeltas(s, s.indexer, deltas) } return errors.New("object given as Process argument is not Deltas") } diff --git a/vendor/k8s.io/client-go/tools/events/event_broadcaster.go b/vendor/k8s.io/client-go/tools/events/event_broadcaster.go index dd7e0aa12..7a9435637 100644 --- a/vendor/k8s.io/client-go/tools/events/event_broadcaster.go +++ b/vendor/k8s.io/client-go/tools/events/event_broadcaster.go @@ -181,22 +181,24 @@ func (e *eventBroadcasterImpl) recordToSink(event *eventsv1.Event, clock clock.C return nil } isomorphicEvent.Series = &eventsv1.EventSeries{ - Count: 1, + Count: 2, LastObservedTime: metav1.MicroTime{Time: clock.Now()}, } - return isomorphicEvent + // Make a copy of the Event to make sure that recording it + // doesn't mess with the object stored in cache. + return isomorphicEvent.DeepCopy() } e.eventCache[eventKey] = eventCopy - return eventCopy + // Make a copy of the Event to make sure that recording it doesn't + // mess with the object stored in cache. + return eventCopy.DeepCopy() }() if evToRecord != nil { - recordedEvent := e.attemptRecording(evToRecord) - if recordedEvent != nil { - recordedEventKey := getKey(recordedEvent) - e.mu.Lock() - defer e.mu.Unlock() - e.eventCache[recordedEventKey] = recordedEvent - } + // TODO: Add a metric counting the number of recording attempts + e.attemptRecording(evToRecord) + // We don't want the new recorded Event to be reflected in the + // client's cache because server-side mutations could mess with the + // aggregation mechanism used by the client. } }() } @@ -248,6 +250,14 @@ func recordEvent(sink EventSink, event *eventsv1.Event) (*eventsv1.Event, bool) return nil, false case *errors.StatusError: if errors.IsAlreadyExists(err) { + // If we tried to create an Event from an EventSerie, it means that + // the original Patch request failed because the Event we were + // trying to patch didn't exist. If the creation failed because the + // Event now exists, it is safe to retry. This occurs when a new + // Event is emitted twice in a very short period of time. + if isEventSeries { + return nil, true + } klog.V(5).Infof("Server rejected event '%#v': '%v' (will not retry!)", event, err) } else { klog.Errorf("Server rejected event '%#v': '%v' (will not retry!)", event, err) diff --git a/vendor/k8s.io/client-go/transport/cache.go b/vendor/k8s.io/client-go/transport/cache.go index b4f8dab0c..477c22cde 100644 --- a/vendor/k8s.io/client-go/transport/cache.go +++ b/vendor/k8s.io/client-go/transport/cache.go @@ -109,7 +109,7 @@ func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) { // If we use are reloading files, we need to handle certificate rotation properly // TODO(jackkleeman): We can also add rotation here when config.HasCertCallback() is true - if config.TLS.ReloadTLSFiles { + if config.TLS.ReloadTLSFiles && tlsConfig != nil && tlsConfig.GetClientCertificate != nil { dynamicCertDialer := certRotatingDialer(tlsConfig.GetClientCertificate, dial) tlsConfig.GetClientCertificate = dynamicCertDialer.GetClientCertificate dial = dynamicCertDialer.connDialer.DialContext diff --git a/vendor/k8s.io/client-go/util/cert/cert.go b/vendor/k8s.io/client-go/util/cert/cert.go index 75143ec07..447c3013c 100644 --- a/vendor/k8s.io/client-go/util/cert/cert.go +++ b/vendor/k8s.io/client-go/util/cert/cert.go @@ -26,6 +26,7 @@ import ( "encoding/pem" "fmt" "io/ioutil" + "math" "math/big" "net" "path/filepath" @@ -44,6 +45,7 @@ type Config struct { Organization []string AltNames AltNames Usages []x509.ExtKeyUsage + NotBefore time.Time } // AltNames contains the domain names and IP addresses that will be added @@ -57,14 +59,24 @@ type AltNames struct { // NewSelfSignedCACert creates a CA certificate func NewSelfSignedCACert(cfg Config, key crypto.Signer) (*x509.Certificate, error) { now := time.Now() + // returns a uniform random value in [0, max-1), then add 1 to serial to make it a uniform random value in [1, max). + serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64-1)) + if err != nil { + return nil, err + } + serial = new(big.Int).Add(serial, big.NewInt(1)) + notBefore := now.UTC() + if !cfg.NotBefore.IsZero() { + notBefore = cfg.NotBefore.UTC() + } tmpl := x509.Certificate{ - SerialNumber: new(big.Int).SetInt64(0), + SerialNumber: serial, Subject: pkix.Name{ CommonName: cfg.CommonName, Organization: cfg.Organization, }, DNSNames: []string{cfg.CommonName}, - NotBefore: now.UTC(), + NotBefore: notBefore, NotAfter: now.Add(duration365d * 10).UTC(), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, BasicConstraintsValid: true, @@ -116,9 +128,14 @@ func GenerateSelfSignedCertKeyWithFixtures(host string, alternateIPs []net.IP, a if err != nil { return nil, nil, err } - + // returns a uniform random value in [0, max-1), then add 1 to serial to make it a uniform random value in [1, max). + serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64-1)) + if err != nil { + return nil, nil, err + } + serial = new(big.Int).Add(serial, big.NewInt(1)) caTemplate := x509.Certificate{ - SerialNumber: big.NewInt(1), + SerialNumber: serial, Subject: pkix.Name{ CommonName: fmt.Sprintf("%s-ca@%d", host, time.Now().Unix()), }, @@ -144,9 +161,14 @@ func GenerateSelfSignedCertKeyWithFixtures(host string, alternateIPs []net.IP, a if err != nil { return nil, nil, err } - + // returns a uniform random value in [0, max-1), then add 1 to serial to make it a uniform random value in [1, max). + serial, err = cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64-1)) + if err != nil { + return nil, nil, err + } + serial = new(big.Int).Add(serial, big.NewInt(1)) template := x509.Certificate{ - SerialNumber: big.NewInt(2), + SerialNumber: serial, Subject: pkix.Name{ CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()), }, diff --git a/vendor/k8s.io/component-base/metrics/metric.go b/vendor/k8s.io/component-base/metrics/metric.go index 2980a9723..e0a58b9b8 100644 --- a/vendor/k8s.io/component-base/metrics/metric.go +++ b/vendor/k8s.io/component-base/metrics/metric.go @@ -207,7 +207,6 @@ var noopCounterVec = &prometheus.CounterVec{} var noopHistogramVec = &prometheus.HistogramVec{} var noopTimingHistogramVec = &promext.TimingHistogramVec{} var noopGaugeVec = &prometheus.GaugeVec{} -var noopObserverVec = &noopObserverVector{} // just use a convenience struct for all the no-ops var noop = &noopMetric{} @@ -226,22 +225,3 @@ func (noopMetric) Desc() *prometheus.Desc { return nil } func (noopMetric) Write(*dto.Metric) error { return nil } func (noopMetric) Describe(chan<- *prometheus.Desc) {} func (noopMetric) Collect(chan<- prometheus.Metric) {} - -type noopObserverVector struct{} - -func (noopObserverVector) GetMetricWith(prometheus.Labels) (prometheus.Observer, error) { - return noop, nil -} -func (noopObserverVector) GetMetricWithLabelValues(...string) (prometheus.Observer, error) { - return noop, nil -} -func (noopObserverVector) With(prometheus.Labels) prometheus.Observer { return noop } -func (noopObserverVector) WithLabelValues(...string) prometheus.Observer { return noop } -func (noopObserverVector) CurryWith(prometheus.Labels) (prometheus.ObserverVec, error) { - return noopObserverVec, nil -} -func (noopObserverVector) MustCurryWith(prometheus.Labels) prometheus.ObserverVec { - return noopObserverVec -} -func (noopObserverVector) Describe(chan<- *prometheus.Desc) {} -func (noopObserverVector) Collect(chan<- prometheus.Metric) {} diff --git a/vendor/modules.txt b/vendor/modules.txt index 5295dd40c..c277d9bcd 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -143,7 +143,7 @@ github.com/google/go-cmp/cmp/internal/value # github.com/google/gofuzz v1.1.0 ## explicit; go 1.12 github.com/google/gofuzz -# github.com/google/uuid v1.2.0 +# github.com/google/uuid v1.3.0 ## explicit github.com/google/uuid # github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 @@ -556,7 +556,7 @@ gopkg.in/yaml.v2 # gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 -# k8s.io/api v0.25.6 +# k8s.io/api v0.25.13 ## explicit; go 1.19 k8s.io/api/admission/v1 k8s.io/api/admission/v1beta1 @@ -606,12 +606,12 @@ k8s.io/api/scheduling/v1beta1 k8s.io/api/storage/v1 k8s.io/api/storage/v1alpha1 k8s.io/api/storage/v1beta1 -# k8s.io/apiextensions-apiserver v0.25.0 +# k8s.io/apiextensions-apiserver v0.25.13 ## explicit; go 1.19 k8s.io/apiextensions-apiserver/pkg/apis/apiextensions k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1 k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1 -# k8s.io/apimachinery v0.25.6 +# k8s.io/apimachinery v0.25.13 ## explicit; go 1.19 k8s.io/apimachinery/pkg/api/equality k8s.io/apimachinery/pkg/api/errors @@ -667,7 +667,7 @@ k8s.io/apimachinery/pkg/version k8s.io/apimachinery/pkg/watch k8s.io/apimachinery/third_party/forked/golang/json k8s.io/apimachinery/third_party/forked/golang/reflect -# k8s.io/apiserver v0.25.6 +# k8s.io/apiserver v0.25.13 ## explicit; go 1.19 k8s.io/apiserver/pkg/admission k8s.io/apiserver/pkg/admission/configuration @@ -799,7 +799,7 @@ k8s.io/apiserver/plugin/pkg/audit/truncate k8s.io/apiserver/plugin/pkg/audit/webhook k8s.io/apiserver/plugin/pkg/authenticator/token/webhook k8s.io/apiserver/plugin/pkg/authorizer/webhook -# k8s.io/client-go v0.25.6 +# k8s.io/client-go v0.25.13 ## explicit; go 1.19 k8s.io/client-go/applyconfigurations/admissionregistration/v1 k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1 @@ -1084,7 +1084,7 @@ k8s.io/client-go/util/homedir k8s.io/client-go/util/jsonpath k8s.io/client-go/util/keyutil k8s.io/client-go/util/workqueue -# k8s.io/code-generator v0.25.6 +# k8s.io/code-generator v0.25.13 ## explicit; go 1.19 k8s.io/code-generator k8s.io/code-generator/cmd/client-gen @@ -1120,7 +1120,7 @@ k8s.io/code-generator/cmd/set-gen k8s.io/code-generator/pkg/namer k8s.io/code-generator/pkg/util k8s.io/code-generator/third_party/forked/golang/reflect -# k8s.io/component-base v0.25.6 +# k8s.io/component-base v0.25.13 ## explicit; go 1.19 k8s.io/component-base/cli/flag k8s.io/component-base/config @@ -1200,7 +1200,7 @@ k8s.io/utils/path k8s.io/utils/pointer k8s.io/utils/strings/slices k8s.io/utils/trace -# sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.35 +# sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.37 ## explicit; go 1.17 sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client/metrics diff --git a/vendor/sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client/client.go b/vendor/sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client/client.go index cb186cefc..d9c151e98 100644 --- a/vendor/sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client/client.go +++ b/vendor/sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client/client.go @@ -118,6 +118,8 @@ func (cm *connectionManager) closeAll() { // grpcTunnel implements Tunnel type grpcTunnel struct { stream client.ProxyService_ProxyClient + sendLock sync.Mutex + recvLock sync.Mutex clientConn clientConn pendingDial pendingDialManager conns connectionManager @@ -243,20 +245,17 @@ func (t *grpcTunnel) serve(tunnelCtx context.Context) { }() for { - pkt, err := t.stream.Recv() + pkt, err := t.Recv() if err == io.EOF { return } - const segment = commonmetrics.SegmentToClient isClosing := t.isClosing() if err != nil || pkt == nil { if !isClosing { klog.ErrorS(err, "stream read failure") } - metrics.Metrics.ObserveStreamErrorNoPacket(segment, err) return } - metrics.Metrics.ObservePacket(segment, pkt.Type) if isClosing { return } @@ -335,11 +334,23 @@ func (t *grpcTunnel) serve(tunnelCtx context.Context) { case client.PacketType_DATA: resp := pkt.GetData() + if resp.ConnectID == 0 { + klog.ErrorS(nil, "Received packet missing ConnectID", "packetType", "DATA") + continue + } // TODO: flow control conn, ok := t.conns.get(resp.ConnectID) if !ok { - klog.V(1).InfoS("Connection not recognized", "connectionID", resp.ConnectID) + klog.ErrorS(nil, "Connection not recognized", "connectionID", resp.ConnectID, "packetType", "DATA") + t.Send(&client.Packet{ + Type: client.PacketType_CLOSE_REQ, + Payload: &client.Packet_CloseRequest{ + CloseRequest: &client.CloseRequest{ + ConnectID: resp.ConnectID, + }, + }, + }) continue } timer := time.NewTimer((time.Duration)(t.readTimeoutSeconds) * time.Second) @@ -358,7 +369,7 @@ func (t *grpcTunnel) serve(tunnelCtx context.Context) { conn, ok := t.conns.get(resp.ConnectID) if !ok { - klog.V(1).InfoS("Connection not recognized", "connectionID", resp.ConnectID) + klog.V(1).InfoS("Connection not recognized", "connectionID", resp.ConnectID, "packetType", "CLOSE_RSP") continue } close(conn.readCh) @@ -418,18 +429,15 @@ func (t *grpcTunnel) dialContext(requestCtx context.Context, protocol, address s } klog.V(5).InfoS("[tracing] send packet", "type", req.Type) - const segment = commonmetrics.SegmentFromClient - metrics.Metrics.ObservePacket(segment, req.Type) - err := t.stream.Send(req) + err := t.Send(req) if err != nil { - metrics.Metrics.ObserveStreamError(segment, err, req.Type) return nil, err } klog.V(5).Infoln("DIAL_REQ sent to proxy server") c := &conn{ - stream: t.stream, + tunnel: t, random: random, closeTunnel: t.closeTunnel, } @@ -473,10 +481,7 @@ func (t *grpcTunnel) closeDial(dialID int64) { }, }, } - const segment = commonmetrics.SegmentFromClient - metrics.Metrics.ObservePacket(segment, req.Type) - if err := t.stream.Send(req); err != nil { - metrics.Metrics.ObserveStreamError(segment, err, req.Type) + if err := t.Send(req); err != nil { klog.V(5).InfoS("Failed to send DIAL_CLS", "err", err, "dialID", dialID) } t.closeTunnel() @@ -491,6 +496,35 @@ func (t *grpcTunnel) isClosing() bool { return atomic.LoadUint32(&t.closing) != 0 } +func (t *grpcTunnel) Send(pkt *client.Packet) error { + t.sendLock.Lock() + defer t.sendLock.Unlock() + + const segment = commonmetrics.SegmentFromClient + metrics.Metrics.ObservePacket(segment, pkt.Type) + err := t.stream.Send(pkt) + if err != nil && err != io.EOF { + metrics.Metrics.ObserveStreamError(segment, err, pkt.Type) + } + return err +} + +func (t *grpcTunnel) Recv() (*client.Packet, error) { + t.recvLock.Lock() + defer t.recvLock.Unlock() + + const segment = commonmetrics.SegmentToClient + pkt, err := t.stream.Recv() + if err != nil { + if err != io.EOF { + metrics.Metrics.ObserveStreamErrorNoPacket(segment, err) + } + return nil, err + } + metrics.Metrics.ObservePacket(segment, pkt.Type) + return pkt, nil +} + func GetDialFailureReason(err error) (isDialFailure bool, reason metrics.DialFailureReason) { var df *dialFailure if errors.As(err, &df) { diff --git a/vendor/sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client/conn.go b/vendor/sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client/conn.go index 14384a62c..f4d3f7886 100644 --- a/vendor/sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client/conn.go +++ b/vendor/sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client/conn.go @@ -24,8 +24,6 @@ import ( "k8s.io/klog/v2" - "sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client/metrics" - commonmetrics "sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/common/metrics" "sigs.k8s.io/apiserver-network-proxy/konnectivity-client/proto/client" ) @@ -38,7 +36,7 @@ var errConnCloseTimeout = errors.New("close timeout") // conn is an implementation of net.Conn, where the data is transported // over an established tunnel defined by a gRPC service ProxyService. type conn struct { - stream client.ProxyService_ProxyClient + tunnel *grpcTunnel connID int64 random int64 readCh chan []byte @@ -65,11 +63,8 @@ func (c *conn) Write(data []byte) (n int, err error) { klog.V(5).InfoS("[tracing] send req", "type", req.Type) - const segment = commonmetrics.SegmentFromClient - metrics.Metrics.ObservePacket(segment, req.Type) - err = c.stream.Send(req) + err = c.tunnel.Send(req) if err != nil { - metrics.Metrics.ObserveStreamError(segment, err, req.Type) return 0, err } return len(data), err @@ -153,10 +148,7 @@ func (c *conn) Close() error { klog.V(5).InfoS("[tracing] send req", "type", req.Type) - const segment = commonmetrics.SegmentFromClient - metrics.Metrics.ObservePacket(segment, req.Type) - if err := c.stream.Send(req); err != nil { - metrics.Metrics.ObserveStreamError(segment, err, req.Type) + if err := c.tunnel.Send(req); err != nil { return err }