update dependencies

master
elijahomolo 2022-10-13 11:00:38 -04:00
parent 60d060978a
commit 97dc16097f
515 changed files with 24190 additions and 7009 deletions

View File

@ -2,32 +2,25 @@ version: 2
jobs:
license:
docker:
- image: golang:1.17
- image: golang:1.19
steps:
- checkout
- run: ./script/check-licenses.sh
vet:
docker:
- image: golang:1.17
- image: golang:1.19
steps:
- checkout
- run: go vet ./...
lint:
docker:
- image: golang:1.17
steps:
- checkout
- run: go get golang.org/x/lint/golint
- run: sh -c golint -set_exit_status $(go list ./...)
test:
docker:
- image: golang:1.17
- image: golang:1.19
steps:
- checkout
- run: go test -race -cover ./...
smoke-test:
docker:
- image: golang:1.17
- image: golang:1.19
steps:
- checkout
- run: go run ./cmd/clusterlint --version
@ -37,6 +30,5 @@ workflows:
jobs:
- license
- vet
- lint
- test
- smoke-test

View File

@ -17,7 +17,7 @@ jobs:
- name: Install go
uses: actions/setup-go@0caeaed6fd66a828038c2da3c0f662a42862658f
with:
go-version: ^1.17
go-version: ^1.19
- name: Check out code into the Go module directory
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
- name: Login to dockerhub to push the image

View File

@ -1,5 +1,5 @@
# Build the clusterlint binary
FROM golang:1.17 as builder
FROM golang:1.19 as builder
WORKDIR /workspace
# Copy the Go Modules manifests
COPY go.mod go.mod

View File

@ -82,7 +82,7 @@ We do not recommend having a `concurrencyPolicy` of `Allow` for CronJob resource
```yaml
# Not recommended: Having a concurrency policy of Allow
apiVersion: batch/v1beta1
apiVersion: batch/v1
kind: CronJob
metadata:
name: mycron
@ -94,7 +94,7 @@ spec:
```yaml
# Recommended: Having a concurrency policy of Forbid or Replace
apiVersion: batch/v1beta1
apiVersion: batch/v1
kind: CronJob
metadata:
name: mycron

View File

@ -18,6 +18,7 @@ package basic
import (
"fmt"
batchv1 "k8s.io/api/batch/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
@ -54,7 +55,7 @@ func (c *cronJobConcurrencyCheck) Run(objects *kube.Objects) ([]checks.Diagnosti
var diagnostics []checks.Diagnostic
for _, cronjob := range objects.CronJobs.Items {
if batchv1beta1.AllowConcurrent == cronjob.Spec.ConcurrencyPolicy {
if batchv1.AllowConcurrent == cronjob.Spec.ConcurrencyPolicy {
d := checks.Diagnostic{
Severity: checks.Warning,
Message: fmt.Sprintf("CronJob has a concurrency policy of `%s`. Prefer to use `%s` or `%s`", cronjob.Spec.ConcurrencyPolicy, batchv1beta1.ForbidConcurrent, batchv1beta1.ReplaceConcurrent),

View File

@ -17,10 +17,10 @@ limitations under the License.
package basic
import (
batchv1 "k8s.io/api/batch/v1"
"testing"
"github.com/stretchr/testify/assert"
batchv1beta1 "k8s.io/api/batch/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/digitalocean/clusterlint/checks"
@ -50,17 +50,17 @@ func TestCronJobConcurrency(t *testing.T) {
}{
{
name: "cronjob with 'Forbid' policy",
objs: policy(batchv1beta1.ForbidConcurrent),
objs: policy(batchv1.ForbidConcurrent),
expected: nil,
},
{
name: "cronjob with 'Replace' policy",
objs: policy(batchv1beta1.ReplaceConcurrent),
objs: policy(batchv1.ReplaceConcurrent),
expected: nil,
},
{
name: "cronjob with 'Allow' policy",
objs: policy(batchv1beta1.AllowConcurrent),
objs: policy(batchv1.AllowConcurrent),
expected: []checks.Diagnostic{
{
Severity: checks.Warning,
@ -82,9 +82,9 @@ func TestCronJobConcurrency(t *testing.T) {
}
}
func policy(policy batchv1beta1.ConcurrencyPolicy) *kube.Objects {
func policy(policy batchv1.ConcurrencyPolicy) *kube.Objects {
objs := initCronJob()
objs.CronJobs.Items[0].Spec = batchv1beta1.CronJobSpec{
objs.CronJobs.Items[0].Spec = batchv1.CronJobSpec{
ConcurrencyPolicy: policy,
}
return objs
@ -92,10 +92,10 @@ func policy(policy batchv1beta1.ConcurrencyPolicy) *kube.Objects {
func initCronJob() *kube.Objects {
objs := &kube.Objects{
CronJobs: &batchv1beta1.CronJobList{
Items: []batchv1beta1.CronJob{
CronJobs: &batchv1.CronJobList{
Items: []batchv1.CronJob{
{
TypeMeta: metav1.TypeMeta{Kind: "CronJob", APIVersion: "batch/v1beta1"},
TypeMeta: metav1.TypeMeta{Kind: "CronJob", APIVersion: "batch/v1"},
ObjectMeta: metav1.ObjectMeta{Name: "cronjob_foo"},
},
},

40
go.mod
View File

@ -1,6 +1,6 @@
module github.com/digitalocean/clusterlint
go 1.18
go 1.19
require (
github.com/docker/distribution v2.7.1+incompatible
@ -9,30 +9,30 @@ require (
github.com/stretchr/testify v1.7.0
github.com/urfave/cli v1.19.1
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
k8s.io/api v0.24.3
k8s.io/apimachinery v0.24.3
k8s.io/client-go v0.24.3
k8s.io/api v0.25.2
k8s.io/apimachinery v0.25.2
k8s.io/client-go v0.25.2
)
require (
cloud.google.com/go v0.81.0 // indirect
cloud.google.com/go v0.97.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.18 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect
github.com/Azure/go-autorest/autorest v0.11.27 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful v2.9.5+incompatible // indirect
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
github.com/go-logr/logr v1.2.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.5 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
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
@ -49,22 +49,22 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.27.1 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
k8s.io/klog/v2 v2.60.1 // indirect
k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // 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
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.2.0 // indirect
)

189
go.sum
View File

@ -18,8 +18,15 @@ cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKP
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
cloud.google.com/go v0.97.0 h1:3DXvAyifywvq64LfkKaMOmkWPS1CikIQdMe2lY9vxU8=
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@ -42,12 +49,13 @@ github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
github.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM=
github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A=
github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg=
github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
@ -55,8 +63,9 @@ github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSY
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw=
github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
@ -66,15 +75,16 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@ -82,6 +92,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -93,26 +104,24 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw=
github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c=
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@ -120,8 +129,9 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@ -142,11 +152,13 @@ github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
@ -155,6 +167,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -173,9 +186,9 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54=
github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@ -188,14 +201,16 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
@ -207,15 +222,18 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
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/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=
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@ -255,8 +273,6 @@ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -270,19 +286,14 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU=
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 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
@ -291,7 +302,9 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@ -319,6 +332,7 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -326,9 +340,10 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -352,6 +367,7 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
@ -388,7 +404,6 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
@ -401,9 +416,10 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -415,6 +431,10 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -440,10 +460,8 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -458,7 +476,6 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -475,11 +492,16 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -536,7 +558,6 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
@ -552,11 +573,14 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@ -579,6 +603,13 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -610,6 +641,7 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
@ -627,6 +659,21 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@ -640,11 +687,19 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -657,8 +712,9 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -668,19 +724,17 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@ -689,38 +743,35 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw=
k8s.io/api v0.24.3 h1:tt55QEmKd6L2k5DP6G/ZzdMQKvG5ro4H4teClqm0sTY=
k8s.io/api v0.24.3/go.mod h1:elGR/XSZrS7z7cSZPzVWaycpJuGIw57j9b95/1PdJNI=
k8s.io/api v0.25.2 h1:v6G8RyFcwf0HR5jQGIAYlvtRNrxMJQG1xJzaSeVnIS8=
k8s.io/api v0.25.2/go.mod h1:qP1Rn4sCVFwx/xIhe+we2cwBLTXNcheRyYXwajonhy0=
k8s.io/apimachinery v0.19.0/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
k8s.io/apimachinery v0.24.3 h1:hrFiNSA2cBZqllakVYyH/VyEh4B581bQRmqATJSeQTg=
k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
k8s.io/apimachinery v0.25.2 h1:WbxfAjCx+AeN8Ilp9joWnyJ6xu9OMeS/fsfjK/5zaQs=
k8s.io/apimachinery v0.25.2/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHwA=
k8s.io/client-go v0.19.0/go.mod h1:H9E/VT95blcFQnlyShFgnFT9ZnJOAceiUHM3MlRC+mU=
k8s.io/client-go v0.24.3 h1:Nl1840+6p4JqkFWEW2LnMKU667BUxw03REfLAVhuKQY=
k8s.io/client-go v0.24.3/go.mod h1:AAovolf5Z9bY1wIg2FZ8LPQlEdKHjLI7ZD4rw920BJw=
k8s.io/client-go v0.25.2 h1:SUPp9p5CwM0yXGQrwYurw9LWz+YtMwhWd0GqOsSiefo=
k8s.io/client-go v0.25.2/go.mod h1:i7cNU7N+yGQmJkewcRD2+Vuj4iz7b30kI8OcL3horQ4=
k8s.io/code-generator v0.19.0/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc=
k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ=
k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 h1:Gii5eqf+GmIEwGNKQYQClCayuJCe2/4fZUvF7VG99sU=
k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk=
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA=
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU=
k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc=
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4=
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
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/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y=
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY=
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k=
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y=
sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@ -26,7 +26,7 @@ import (
csi "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned"
"golang.org/x/sync/errgroup"
arv1 "k8s.io/api/admissionregistration/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
st "k8s.io/api/storage/v1"
@ -70,7 +70,7 @@ type Objects struct {
MutatingWebhookConfigurations *arv1.MutatingWebhookConfigurationList
ValidatingWebhookConfigurations *arv1.ValidatingWebhookConfigurationList
Namespaces *corev1.NamespaceList
CronJobs *batchv1beta1.CronJobList
CronJobs *batchv1.CronJobList
}
// Client encapsulates a client for a Kubernetes cluster.
@ -91,7 +91,7 @@ func (c *Client) Close() {
func (c *Client) FetchObjects(ctx context.Context, filter ObjectFilter) (*Objects, error) {
client := c.KubeClient.CoreV1()
admissionControllerClient := c.KubeClient.AdmissionregistrationV1()
batchClient := c.KubeClient.BatchV1beta1()
batchClient := c.KubeClient.BatchV1()
storageClient := c.KubeClient.StorageV1()
csiClient := c.CSIClient.SnapshotV1()
csiBetaClient := c.CSIClient.SnapshotV1beta1()
@ -281,7 +281,7 @@ func objectsWithoutNils(objects *Objects) *Objects {
objects.Namespaces = &v1.NamespaceList{}
}
if objects.CronJobs == nil {
objects.CronJobs = &batchv1beta1.CronJobList{}
objects.CronJobs = &batchv1.CronJobList{}
}
if objects.VolumeSnapshotsV1 == nil {
objects.VolumeSnapshotsV1 = &csitypes.VolumeSnapshotList{}

View File

@ -282,6 +282,7 @@ func NewClient(c *http.Client) *Client {
// getETag returns a value from the metadata service as well as the associated ETag.
// This func is otherwise equivalent to Get.
func (c *Client) getETag(suffix string) (value, etag string, err error) {
ctx := context.TODO()
// Using a fixed IP makes it very difficult to spoof the metadata service in
// a container, which is an important use-case for local testing of cloud
// deployments. To enable spoofing of the metadata service, the environment
@ -304,10 +305,26 @@ func (c *Client) getETag(suffix string) (value, etag string, err error) {
}
req.Header.Set("Metadata-Flavor", "Google")
req.Header.Set("User-Agent", userAgent)
res, err := c.hc.Do(req)
if err != nil {
var res *http.Response
var reqErr error
retryer := newRetryer()
for {
res, reqErr = c.hc.Do(req)
var code int
if res != nil {
code = res.StatusCode
}
if delay, shouldRetry := retryer.Retry(code, reqErr); shouldRetry {
if err := sleep(ctx, delay); err != nil {
return "", "", err
}
continue
}
break
}
if reqErr != nil {
return "", "", nil
}
defer res.Body.Close()
if res.StatusCode == http.StatusNotFound {
return "", "", NotDefinedError(suffix)

114
vendor/cloud.google.com/go/compute/metadata/retry.go generated vendored Normal file
View File

@ -0,0 +1,114 @@
// Copyright 2021 Google LLC
//
// 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 metadata
import (
"context"
"io"
"math/rand"
"net/http"
"time"
)
const (
maxRetryAttempts = 5
)
var (
syscallRetryable = func(err error) bool { return false }
)
// defaultBackoff is basically equivalent to gax.Backoff without the need for
// the dependency.
type defaultBackoff struct {
max time.Duration
mul float64
cur time.Duration
}
func (b *defaultBackoff) Pause() time.Duration {
d := time.Duration(1 + rand.Int63n(int64(b.cur)))
b.cur = time.Duration(float64(b.cur) * b.mul)
if b.cur > b.max {
b.cur = b.max
}
return d
}
// sleep is the equivalent of gax.Sleep without the need for the dependency.
func sleep(ctx context.Context, d time.Duration) error {
t := time.NewTimer(d)
select {
case <-ctx.Done():
t.Stop()
return ctx.Err()
case <-t.C:
return nil
}
}
func newRetryer() *metadataRetryer {
return &metadataRetryer{bo: &defaultBackoff{
cur: 100 * time.Millisecond,
max: 30 * time.Second,
mul: 2,
}}
}
type backoff interface {
Pause() time.Duration
}
type metadataRetryer struct {
bo backoff
attempts int
}
func (r *metadataRetryer) Retry(status int, err error) (time.Duration, bool) {
if status == http.StatusOK {
return 0, false
}
retryOk := shouldRetry(status, err)
if !retryOk {
return 0, false
}
if r.attempts == maxRetryAttempts {
return 0, false
}
r.attempts++
return r.bo.Pause(), true
}
func shouldRetry(status int, err error) bool {
if 500 <= status && status <= 599 {
return true
}
if err == io.ErrUnexpectedEOF {
return true
}
// Transient network errors should be retried.
if syscallRetryable(err) {
return true
}
if err, ok := err.(interface{ Temporary() bool }); ok {
if err.Temporary() {
return true
}
}
if err, ok := err.(interface{ Unwrap() error }); ok {
return shouldRetry(status, err.Unwrap())
}
return false
}

View File

@ -0,0 +1,26 @@
// Copyright 2021 Google LLC
//
// 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.
//go:build linux
// +build linux
package metadata
import "syscall"
func init() {
// Initialize syscallRetryable to return true on transient socket-level
// errors. These errors are specific to Linux.
syscallRetryable = func(err error) bool { return err == syscall.ECONNRESET || err == syscall.ECONNREFUSED }
}

View File

@ -1,3 +1,5 @@
# NOTE: This module will go out of support by March 31, 2023. For authenticating with Azure AD, use module [azidentity](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity) instead. For help migrating from `adal` to `azidentiy` please consult the [migration guide](https://aka.ms/azsdk/go/identity/migration). General information about the retirement of this and other legacy modules can be found [here](https://azure.microsoft.com/updates/support-for-azure-sdk-libraries-that-do-not-conform-to-our-current-azure-sdk-guidelines-will-be-retired-as-of-31-march-2023/).
# Azure Active Directory authentication for Go
This is a standalone package for authenticating with Azure Active
@ -18,7 +20,7 @@ go get -u github.com/Azure/go-autorest/autorest/adal
## Usage
An Active Directory application is required in order to use this library. An application can be registered in the [Azure Portal](https://portal.azure.com/) by following these [guidelines](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-integrating-applications) or using the [Azure CLI](https://github.com/Azure/azure-cli).
An Active Directory application is required in order to use this library. An application can be registered in the [Azure Portal](https://portal.azure.com/) by following these [guidelines](https://docs.microsoft.com/azure/active-directory/develop/active-directory-integrating-applications) or using the [Azure CLI](https://github.com/Azure/azure-cli).
### Register an Azure AD Application with secret
@ -88,7 +90,7 @@ An Active Directory application is required in order to use this library. An app
### Grant the necessary permissions
Azure relies on a Role-Based Access Control (RBAC) model to manage the access to resources at a fine-grained
level. There is a set of [pre-defined roles](https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-built-in-roles)
level. There is a set of [pre-defined roles](https://docs.microsoft.com/azure/active-directory/role-based-access-built-in-roles)
which can be assigned to a service principal of an Azure AD application depending of your needs.
```
@ -104,7 +106,7 @@ It is also possible to define custom role definitions.
az role definition create --role-definition role-definition.json
```
* Check [custom roles](https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-control-custom-roles) for more details regarding the content of `role-definition.json` file.
* Check [custom roles](https://docs.microsoft.com/azure/active-directory/role-based-access-control-custom-roles) for more details regarding the content of `role-definition.json` file.
### Acquire Access Token

View File

@ -1,3 +1,4 @@
//go:build modhack
// +build modhack
package adal

View File

@ -16,9 +16,11 @@ package adal
import (
"crypto/tls"
"net"
"net/http"
"net/http/cookiejar"
"sync"
"time"
"github.com/Azure/go-autorest/tracing"
)
@ -72,15 +74,18 @@ func sender() Sender {
// note that we can't init defaultSender in init() since it will
// execute before calling code has had a chance to enable tracing
defaultSenderInit.Do(func() {
// Use behaviour compatible with DefaultTransport, but require TLS minimum version.
defaultTransport := http.DefaultTransport.(*http.Transport)
// copied from http.DefaultTransport with a TLS minimum version.
transport := &http.Transport{
Proxy: defaultTransport.Proxy,
DialContext: defaultTransport.DialContext,
MaxIdleConns: defaultTransport.MaxIdleConns,
IdleConnTimeout: defaultTransport.IdleConnTimeout,
TLSHandshakeTimeout: defaultTransport.TLSHandshakeTimeout,
ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout,
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
},

View File

@ -37,7 +37,7 @@ import (
"github.com/Azure/go-autorest/autorest/date"
"github.com/Azure/go-autorest/logger"
"github.com/form3tech-oss/jwt-go"
"github.com/golang-jwt/jwt/v4"
)
const (
@ -365,6 +365,25 @@ func (secret ServicePrincipalAuthorizationCodeSecret) MarshalJSON() ([]byte, err
})
}
// ServicePrincipalFederatedSecret implements ServicePrincipalSecret for Federated JWTs.
type ServicePrincipalFederatedSecret struct {
jwt string
}
// SetAuthenticationValues is a method of the interface ServicePrincipalSecret.
// It will populate the form submitted during OAuth Token Acquisition using a JWT signed by an OIDC issuer.
func (secret *ServicePrincipalFederatedSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error {
v.Set("client_assertion", secret.jwt)
v.Set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
return nil
}
// MarshalJSON implements the json.Marshaler interface.
func (secret ServicePrincipalFederatedSecret) MarshalJSON() ([]byte, error) {
return nil, errors.New("marshalling ServicePrincipalFederatedSecret is not supported")
}
// ServicePrincipalToken encapsulates a Token created for a Service Principal.
type ServicePrincipalToken struct {
inner servicePrincipalToken
@ -419,6 +438,8 @@ func (spt *ServicePrincipalToken) UnmarshalJSON(data []byte) error {
spt.inner.Secret = &ServicePrincipalUsernamePasswordSecret{}
case "ServicePrincipalAuthorizationCodeSecret":
spt.inner.Secret = &ServicePrincipalAuthorizationCodeSecret{}
case "ServicePrincipalFederatedSecret":
return errors.New("unmarshalling ServicePrincipalFederatedSecret is not supported")
default:
return fmt.Errorf("unrecognized token type '%s'", secret["type"])
}
@ -665,6 +686,31 @@ func NewServicePrincipalTokenFromAuthorizationCode(oauthConfig OAuthConfig, clie
)
}
// NewServicePrincipalTokenFromFederatedToken creates a ServicePrincipalToken from the supplied federated OIDC JWT.
func NewServicePrincipalTokenFromFederatedToken(oauthConfig OAuthConfig, clientID string, jwt string, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
if err := validateOAuthConfig(oauthConfig); err != nil {
return nil, err
}
if err := validateStringParam(clientID, "clientID"); err != nil {
return nil, err
}
if err := validateStringParam(resource, "resource"); err != nil {
return nil, err
}
if jwt == "" {
return nil, fmt.Errorf("parameter 'jwt' cannot be empty")
}
return NewServicePrincipalTokenWithSecret(
oauthConfig,
clientID,
resource,
&ServicePrincipalFederatedSecret{
jwt: jwt,
},
callbacks...,
)
}
type msiType int
const (
@ -676,8 +722,6 @@ const (
func (m msiType) String() string {
switch m {
case msiTypeUnavailable:
return "unavailable"
case msiTypeAppServiceV20170901:
return "AppServiceV20170901"
case msiTypeCloudShell:
@ -699,13 +743,9 @@ func getMSIType() (msiType, string, error) {
}
// if ONLY the env var MSI_ENDPOINT is set the msiType is CloudShell
return msiTypeCloudShell, endpointEnvVar, nil
} else if msiAvailableHook(context.Background(), sender()) {
// if MSI_ENDPOINT is NOT set AND the IMDS endpoint is available the msiType is IMDS. This will timeout after 500 milliseconds
return msiTypeIMDS, msiEndpoint, nil
} else {
// if MSI_ENDPOINT is NOT set and IMDS endpoint is not available Managed Identity is not available
return msiTypeUnavailable, "", errors.New("MSI not available")
}
// if MSI_ENDPOINT is NOT set assume the msiType is IMDS
return msiTypeIMDS, msiEndpoint, nil
}
// GetMSIVMEndpoint gets the MSI endpoint on Virtual Machines.
@ -800,13 +840,13 @@ func newServicePrincipalTokenFromMSI(msiEndpoint, resource, userAssignedID, iden
}
msiType, endpoint, err := getMSIType()
if err != nil {
logger.Instance.Writef(logger.LogError, "Error determining managed identity environment: %v", err)
logger.Instance.Writef(logger.LogError, "Error determining managed identity environment: %v\n", err)
return nil, err
}
logger.Instance.Writef(logger.LogInfo, "Managed identity environment is %s, endpoint is %s", msiType, endpoint)
logger.Instance.Writef(logger.LogInfo, "Managed identity environment is %s, endpoint is %s\n", msiType, endpoint)
if msiEndpoint != "" {
endpoint = msiEndpoint
logger.Instance.Writef(logger.LogInfo, "Managed identity custom endpoint is %s", endpoint)
logger.Instance.Writef(logger.LogInfo, "Managed identity custom endpoint is %s\n", endpoint)
}
msiEndpointURL, err := url.Parse(endpoint)
if err != nil {
@ -1064,8 +1104,8 @@ func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource
// AAD returns expires_in as a string, ADFS returns it as an int
ExpiresIn json.Number `json:"expires_in"`
// expires_on can be in two formats, a UTC time stamp or the number of seconds.
ExpiresOn string `json:"expires_on"`
// expires_on can be in three formats, a UTC time stamp, or the number of seconds as a string *or* int.
ExpiresOn interface{} `json:"expires_on"`
NotBefore json.Number `json:"not_before"`
Resource string `json:"resource"`
@ -1078,7 +1118,7 @@ func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource
}
expiresOn := json.Number("")
// ADFS doesn't include the expires_on field
if token.ExpiresOn != "" {
if token.ExpiresOn != nil {
if expiresOn, err = parseExpiresOn(token.ExpiresOn); err != nil {
return newTokenRefreshError(fmt.Sprintf("adal: failed to parse expires_on: %v value '%s'", err, token.ExpiresOn), resp)
}
@ -1095,18 +1135,27 @@ func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource
}
// converts expires_on to the number of seconds
func parseExpiresOn(s string) (json.Number, error) {
// convert the expiration date to the number of seconds from now
timeToDuration := func(t time.Time) json.Number {
dur := t.Sub(time.Now().UTC())
return json.Number(strconv.FormatInt(int64(dur.Round(time.Second).Seconds()), 10))
func parseExpiresOn(s interface{}) (json.Number, error) {
// the JSON unmarshaler treats JSON numbers unmarshaled into an interface{} as float64
asFloat64, ok := s.(float64)
if ok {
// this is the number of seconds as int case
return json.Number(strconv.FormatInt(int64(asFloat64), 10)), nil
}
if _, err := strconv.ParseInt(s, 10, 64); err == nil {
asStr, ok := s.(string)
if !ok {
return "", fmt.Errorf("unexpected expires_on type %T", s)
}
// convert the expiration date to the number of seconds from the unix epoch
timeToDuration := func(t time.Time) json.Number {
return json.Number(strconv.FormatInt(t.UTC().Unix(), 10))
}
if _, err := json.Number(asStr).Int64(); err == nil {
// this is the number of seconds case, no conversion required
return json.Number(s), nil
} else if eo, err := time.Parse(expiresOnDateFormatPM, s); err == nil {
return json.Number(asStr), nil
} else if eo, err := time.Parse(expiresOnDateFormatPM, asStr); err == nil {
return timeToDuration(eo), nil
} else if eo, err := time.Parse(expiresOnDateFormat, s); err == nil {
} else if eo, err := time.Parse(expiresOnDateFormat, asStr); err == nil {
return timeToDuration(eo), nil
} else {
// unknown format
@ -1322,15 +1371,26 @@ func NewMultiTenantServicePrincipalTokenFromCertificate(multiTenantCfg MultiTena
}
// MSIAvailable returns true if the MSI endpoint is available for authentication.
func MSIAvailable(ctx context.Context, sender Sender) bool {
resp, err := getMSIEndpoint(ctx, sender)
func MSIAvailable(ctx context.Context, s Sender) bool {
msiType, _, err := getMSIType()
if err != nil {
return false
}
if msiType != msiTypeIMDS {
return true
}
if s == nil {
s = sender()
}
resp, err := getMSIEndpoint(ctx, s)
if err == nil {
resp.Body.Close()
}
return err == nil
}
// used for testing purposes
var msiAvailableHook = func(ctx context.Context, sender Sender) bool {
return MSIAvailable(ctx, sender)
}

View File

@ -1,3 +1,4 @@
//go:build go1.13
// +build go1.13
// Copyright 2017 Microsoft Corporation
@ -24,7 +25,7 @@ import (
)
func getMSIEndpoint(ctx context.Context, sender Sender) (*http.Response, error) {
tempCtx, cancel := context.WithTimeout(ctx, 500*time.Millisecond)
tempCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
defer cancel()
// http.NewRequestWithContext() was added in Go 1.13
req, _ := http.NewRequestWithContext(tempCtx, http.MethodGet, msiEndpoint, nil)

View File

@ -1,3 +1,4 @@
//go:build !go1.13
// +build !go1.13
// Copyright 2017 Microsoft Corporation
@ -23,7 +24,7 @@ import (
)
func getMSIEndpoint(ctx context.Context, sender Sender) (*http.Response, error) {
tempCtx, cancel := context.WithTimeout(ctx, 500*time.Millisecond)
tempCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
defer cancel()
req, _ := http.NewRequest(http.MethodGet, msiEndpoint, nil)
req = req.WithContext(tempCtx)

View File

@ -26,6 +26,7 @@ import (
"time"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/logger"
"github.com/Azure/go-autorest/tracing"
)
@ -215,6 +216,7 @@ func (f *Future) WaitForCompletionRef(ctx context.Context, client autorest.Clien
}
// if the initial response has a Retry-After, sleep for the specified amount of time before starting to poll
if delay, ok := f.GetPollingDelay(); ok {
logger.Instance.Writeln(logger.LogInfo, "WaitForCompletionRef: initial polling delay")
if delayElapsed := autorest.DelayForBackoff(delay, 0, cancelCtx.Done()); !delayElapsed {
err = cancelCtx.Err()
return
@ -234,12 +236,14 @@ func (f *Future) WaitForCompletionRef(ctx context.Context, client autorest.Clien
var ok bool
delay, ok = f.GetPollingDelay()
if !ok {
logger.Instance.Writeln(logger.LogInfo, "WaitForCompletionRef: Using client polling delay")
delay = client.PollingDelay
}
} else {
// there was an error polling for status so perform exponential
// back-off based on the number of attempts using the client's retry
// duration. update attempts after delayAttempt to avoid off-by-one.
logger.Instance.Writef(logger.LogError, "WaitForCompletionRef: %s\n", err)
delayAttempt = attempts
delay = client.RetryDuration
attempts++

View File

@ -68,7 +68,7 @@ func (se ServiceError) Error() string {
if err != nil {
result += fmt.Sprintf(" Details=%v", se.Details)
}
result += fmt.Sprintf(" Details=%v", string(d))
result += fmt.Sprintf(" Details=%s", d)
}
if se.InnerError != nil {
@ -76,7 +76,7 @@ func (se ServiceError) Error() string {
if err != nil {
result += fmt.Sprintf(" InnerError=%v", se.InnerError)
}
result += fmt.Sprintf(" InnerError=%v", string(d))
result += fmt.Sprintf(" InnerError=%s", d)
}
if se.AdditionalInfo != nil {
@ -84,7 +84,7 @@ func (se ServiceError) Error() string {
if err != nil {
result += fmt.Sprintf(" AdditionalInfo=%v", se.AdditionalInfo)
}
result += fmt.Sprintf(" AdditionalInfo=%v", string(d))
result += fmt.Sprintf(" AdditionalInfo=%s", d)
}
return result
@ -211,7 +211,7 @@ func (r Resource) String() string {
}
// ParseResourceID parses a resource ID into a ResourceDetails struct.
// See https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-template-functions-resource#return-value-4.
// See https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/template-functions-resource?tabs=json#resourceid.
func ParseResourceID(resourceID string) (Resource, error) {
const resourceIDPatternText = `(?i)subscriptions/(.+)/resourceGroups/(.+)/providers/(.+?)/(.+?)/(.+)`
@ -335,13 +335,13 @@ func WithErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator {
b, decodeErr := autorest.CopyAndDecode(encodedAs, resp.Body, &e)
resp.Body = ioutil.NopCloser(&b)
if decodeErr != nil {
return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b.String(), decodeErr)
return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b, decodeErr)
}
if e.ServiceError == nil {
// Check if error is unwrapped ServiceError
decoder := autorest.NewDecoder(encodedAs, bytes.NewReader(b.Bytes()))
if err := decoder.Decode(&e.ServiceError); err != nil {
return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b.String(), err)
return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b, err)
}
// for example, should the API return the literal value `null` as the response
@ -364,7 +364,7 @@ func WithErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator {
rawBody := map[string]interface{}{}
decoder := autorest.NewDecoder(encodedAs, bytes.NewReader(b.Bytes()))
if err := decoder.Decode(&rawBody); err != nil {
return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b.String(), err)
return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b, err)
}
e.ServiceError = &ServiceError{

View File

@ -34,8 +34,10 @@ const (
var environments = map[string]Environment{
"AZURECHINACLOUD": ChinaCloud,
"AZUREGERMANCLOUD": GermanCloud,
"AZURECLOUD": PublicCloud,
"AZUREPUBLICCLOUD": PublicCloud,
"AZUREUSGOVERNMENTCLOUD": USGovernmentCloud,
"AZUREUSGOVERNMENT": USGovernmentCloud,
"AZUREUSGOVERNMENTCLOUD": USGovernmentCloud, //TODO: deprecate
}
// ResourceIdentifier contains a set of Azure resource IDs.
@ -45,9 +47,14 @@ type ResourceIdentifier struct {
Datalake string `json:"datalake"`
Batch string `json:"batch"`
OperationalInsights string `json:"operationalInsights"`
OSSRDBMS string `json:"ossRDBMS"`
Storage string `json:"storage"`
Synapse string `json:"synapse"`
ServiceBus string `json:"serviceBus"`
SQLDatabase string `json:"sqlDatabase"`
CosmosDB string `json:"cosmosDB"`
ManagedHSM string `json:"managedHSM"`
MicrosoftGraph string `json:"microsoftGraph"`
}
// Environment represents a set of endpoints for each of Azure's Clouds.
@ -60,21 +67,28 @@ type Environment struct {
ActiveDirectoryEndpoint string `json:"activeDirectoryEndpoint"`
GalleryEndpoint string `json:"galleryEndpoint"`
KeyVaultEndpoint string `json:"keyVaultEndpoint"`
ManagedHSMEndpoint string `json:"managedHSMEndpoint"`
GraphEndpoint string `json:"graphEndpoint"`
ServiceBusEndpoint string `json:"serviceBusEndpoint"`
BatchManagementEndpoint string `json:"batchManagementEndpoint"`
MicrosoftGraphEndpoint string `json:"microsoftGraphEndpoint"`
StorageEndpointSuffix string `json:"storageEndpointSuffix"`
CosmosDBDNSSuffix string `json:"cosmosDBDNSSuffix"`
MariaDBDNSSuffix string `json:"mariaDBDNSSuffix"`
MySQLDatabaseDNSSuffix string `json:"mySqlDatabaseDNSSuffix"`
PostgresqlDatabaseDNSSuffix string `json:"postgresqlDatabaseDNSSuffix"`
SQLDatabaseDNSSuffix string `json:"sqlDatabaseDNSSuffix"`
TrafficManagerDNSSuffix string `json:"trafficManagerDNSSuffix"`
KeyVaultDNSSuffix string `json:"keyVaultDNSSuffix"`
ManagedHSMDNSSuffix string `json:"managedHSMDNSSuffix"`
ServiceBusEndpointSuffix string `json:"serviceBusEndpointSuffix"`
ServiceManagementVMDNSSuffix string `json:"serviceManagementVMDNSSuffix"`
ResourceManagerVMDNSSuffix string `json:"resourceManagerVMDNSSuffix"`
ContainerRegistryDNSSuffix string `json:"containerRegistryDNSSuffix"`
CosmosDBDNSSuffix string `json:"cosmosDBDNSSuffix"`
TokenAudience string `json:"tokenAudience"`
APIManagementHostNameSuffix string `json:"apiManagementHostNameSuffix"`
SynapseEndpointSuffix string `json:"synapseEndpointSuffix"`
DatalakeSuffix string `json:"datalakeSuffix"`
ResourceIdentifiers ResourceIdentifier `json:"resourceIdentifiers"`
}
@ -89,30 +103,42 @@ var (
ActiveDirectoryEndpoint: "https://login.microsoftonline.com/",
GalleryEndpoint: "https://gallery.azure.com/",
KeyVaultEndpoint: "https://vault.azure.net/",
ManagedHSMEndpoint: "https://managedhsm.azure.net/",
GraphEndpoint: "https://graph.windows.net/",
ServiceBusEndpoint: "https://servicebus.windows.net/",
BatchManagementEndpoint: "https://batch.core.windows.net/",
MicrosoftGraphEndpoint: "https://graph.microsoft.com/",
StorageEndpointSuffix: "core.windows.net",
CosmosDBDNSSuffix: "documents.azure.com",
MariaDBDNSSuffix: "mariadb.database.azure.com",
MySQLDatabaseDNSSuffix: "mysql.database.azure.com",
PostgresqlDatabaseDNSSuffix: "postgres.database.azure.com",
SQLDatabaseDNSSuffix: "database.windows.net",
TrafficManagerDNSSuffix: "trafficmanager.net",
KeyVaultDNSSuffix: "vault.azure.net",
ManagedHSMDNSSuffix: "managedhsm.azure.net",
ServiceBusEndpointSuffix: "servicebus.windows.net",
ServiceManagementVMDNSSuffix: "cloudapp.net",
ResourceManagerVMDNSSuffix: "cloudapp.azure.com",
ContainerRegistryDNSSuffix: "azurecr.io",
CosmosDBDNSSuffix: "documents.azure.com",
TokenAudience: "https://management.azure.com/",
APIManagementHostNameSuffix: "azure-api.net",
SynapseEndpointSuffix: "dev.azuresynapse.net",
DatalakeSuffix: "azuredatalakestore.net",
ResourceIdentifiers: ResourceIdentifier{
Graph: "https://graph.windows.net/",
KeyVault: "https://vault.azure.net",
Datalake: "https://datalake.azure.net/",
Batch: "https://batch.core.windows.net/",
OperationalInsights: "https://api.loganalytics.io",
OSSRDBMS: "https://ossrdbms-aad.database.windows.net",
Storage: "https://storage.azure.com/",
Synapse: "https://dev.azuresynapse.net",
ServiceBus: "https://servicebus.azure.net/",
SQLDatabase: "https://database.windows.net/",
CosmosDB: "https://cosmos.azure.com",
ManagedHSM: "https://managedhsm.azure.net",
MicrosoftGraph: "https://graph.microsoft.com/",
},
}
@ -126,30 +152,42 @@ var (
ActiveDirectoryEndpoint: "https://login.microsoftonline.us/",
GalleryEndpoint: "https://gallery.usgovcloudapi.net/",
KeyVaultEndpoint: "https://vault.usgovcloudapi.net/",
ManagedHSMEndpoint: NotAvailable,
GraphEndpoint: "https://graph.windows.net/",
ServiceBusEndpoint: "https://servicebus.usgovcloudapi.net/",
BatchManagementEndpoint: "https://batch.core.usgovcloudapi.net/",
MicrosoftGraphEndpoint: "https://graph.microsoft.us/",
StorageEndpointSuffix: "core.usgovcloudapi.net",
CosmosDBDNSSuffix: "documents.azure.us",
MariaDBDNSSuffix: "mariadb.database.usgovcloudapi.net",
MySQLDatabaseDNSSuffix: "mysql.database.usgovcloudapi.net",
PostgresqlDatabaseDNSSuffix: "postgres.database.usgovcloudapi.net",
SQLDatabaseDNSSuffix: "database.usgovcloudapi.net",
TrafficManagerDNSSuffix: "usgovtrafficmanager.net",
KeyVaultDNSSuffix: "vault.usgovcloudapi.net",
ManagedHSMDNSSuffix: NotAvailable,
ServiceBusEndpointSuffix: "servicebus.usgovcloudapi.net",
ServiceManagementVMDNSSuffix: "usgovcloudapp.net",
ResourceManagerVMDNSSuffix: "cloudapp.usgovcloudapi.net",
ContainerRegistryDNSSuffix: "azurecr.us",
CosmosDBDNSSuffix: "documents.azure.us",
TokenAudience: "https://management.usgovcloudapi.net/",
APIManagementHostNameSuffix: "azure-api.us",
SynapseEndpointSuffix: NotAvailable,
SynapseEndpointSuffix: "dev.azuresynapse.usgovcloudapi.net",
DatalakeSuffix: NotAvailable,
ResourceIdentifiers: ResourceIdentifier{
Graph: "https://graph.windows.net/",
KeyVault: "https://vault.usgovcloudapi.net",
Datalake: NotAvailable,
Batch: "https://batch.core.usgovcloudapi.net/",
OperationalInsights: "https://api.loganalytics.us",
OSSRDBMS: "https://ossrdbms-aad.database.usgovcloudapi.net",
Storage: "https://storage.azure.com/",
Synapse: NotAvailable,
Synapse: "https://dev.azuresynapse.usgovcloudapi.net",
ServiceBus: "https://servicebus.azure.net/",
SQLDatabase: "https://database.usgovcloudapi.net/",
CosmosDB: "https://cosmos.azure.com",
ManagedHSM: NotAvailable,
MicrosoftGraph: "https://graph.microsoft.us/",
},
}
@ -163,30 +201,42 @@ var (
ActiveDirectoryEndpoint: "https://login.chinacloudapi.cn/",
GalleryEndpoint: "https://gallery.chinacloudapi.cn/",
KeyVaultEndpoint: "https://vault.azure.cn/",
ManagedHSMEndpoint: NotAvailable,
GraphEndpoint: "https://graph.chinacloudapi.cn/",
ServiceBusEndpoint: "https://servicebus.chinacloudapi.cn/",
BatchManagementEndpoint: "https://batch.chinacloudapi.cn/",
MicrosoftGraphEndpoint: "https://microsoftgraph.chinacloudapi.cn/",
StorageEndpointSuffix: "core.chinacloudapi.cn",
CosmosDBDNSSuffix: "documents.azure.cn",
MariaDBDNSSuffix: "mariadb.database.chinacloudapi.cn",
MySQLDatabaseDNSSuffix: "mysql.database.chinacloudapi.cn",
PostgresqlDatabaseDNSSuffix: "postgres.database.chinacloudapi.cn",
SQLDatabaseDNSSuffix: "database.chinacloudapi.cn",
TrafficManagerDNSSuffix: "trafficmanager.cn",
KeyVaultDNSSuffix: "vault.azure.cn",
ManagedHSMDNSSuffix: NotAvailable,
ServiceBusEndpointSuffix: "servicebus.chinacloudapi.cn",
ServiceManagementVMDNSSuffix: "chinacloudapp.cn",
ResourceManagerVMDNSSuffix: "cloudapp.chinacloudapi.cn",
ContainerRegistryDNSSuffix: "azurecr.cn",
CosmosDBDNSSuffix: "documents.azure.cn",
TokenAudience: "https://management.chinacloudapi.cn/",
APIManagementHostNameSuffix: "azure-api.cn",
SynapseEndpointSuffix: "dev.azuresynapse.azure.cn",
DatalakeSuffix: NotAvailable,
ResourceIdentifiers: ResourceIdentifier{
Graph: "https://graph.chinacloudapi.cn/",
KeyVault: "https://vault.azure.cn",
Datalake: NotAvailable,
Batch: "https://batch.chinacloudapi.cn/",
OperationalInsights: NotAvailable,
OSSRDBMS: "https://ossrdbms-aad.database.chinacloudapi.cn",
Storage: "https://storage.azure.com/",
Synapse: "https://dev.azuresynapse.net",
ServiceBus: "https://servicebus.azure.net/",
SQLDatabase: "https://database.chinacloudapi.cn/",
CosmosDB: "https://cosmos.azure.com",
ManagedHSM: NotAvailable,
MicrosoftGraph: "https://microsoftgraph.chinacloudapi.cn",
},
}
@ -200,30 +250,42 @@ var (
ActiveDirectoryEndpoint: "https://login.microsoftonline.de/",
GalleryEndpoint: "https://gallery.cloudapi.de/",
KeyVaultEndpoint: "https://vault.microsoftazure.de/",
ManagedHSMEndpoint: NotAvailable,
GraphEndpoint: "https://graph.cloudapi.de/",
ServiceBusEndpoint: "https://servicebus.cloudapi.de/",
BatchManagementEndpoint: "https://batch.cloudapi.de/",
MicrosoftGraphEndpoint: NotAvailable,
StorageEndpointSuffix: "core.cloudapi.de",
CosmosDBDNSSuffix: "documents.microsoftazure.de",
MariaDBDNSSuffix: "mariadb.database.cloudapi.de",
MySQLDatabaseDNSSuffix: "mysql.database.cloudapi.de",
PostgresqlDatabaseDNSSuffix: "postgres.database.cloudapi.de",
SQLDatabaseDNSSuffix: "database.cloudapi.de",
TrafficManagerDNSSuffix: "azuretrafficmanager.de",
KeyVaultDNSSuffix: "vault.microsoftazure.de",
ManagedHSMDNSSuffix: NotAvailable,
ServiceBusEndpointSuffix: "servicebus.cloudapi.de",
ServiceManagementVMDNSSuffix: "azurecloudapp.de",
ResourceManagerVMDNSSuffix: "cloudapp.microsoftazure.de",
ContainerRegistryDNSSuffix: NotAvailable,
CosmosDBDNSSuffix: "documents.microsoftazure.de",
TokenAudience: "https://management.microsoftazure.de/",
APIManagementHostNameSuffix: NotAvailable,
SynapseEndpointSuffix: NotAvailable,
DatalakeSuffix: NotAvailable,
ResourceIdentifiers: ResourceIdentifier{
Graph: "https://graph.cloudapi.de/",
KeyVault: "https://vault.microsoftazure.de",
Datalake: NotAvailable,
Batch: "https://batch.cloudapi.de/",
OperationalInsights: NotAvailable,
OSSRDBMS: "https://ossrdbms-aad.database.cloudapi.de",
Storage: "https://storage.azure.com/",
Synapse: NotAvailable,
ServiceBus: "https://servicebus.azure.net/",
SQLDatabase: "https://database.cloudapi.de/",
CosmosDB: "https://cosmos.azure.com",
ManagedHSM: NotAvailable,
MicrosoftGraph: NotAvailable,
},
}
)

View File

@ -64,7 +64,7 @@ func DoRetryWithRegistration(client autorest.Client) autorest.SendDecorator {
if re.ServiceError != nil && re.ServiceError.Code == "MissingSubscriptionRegistration" {
regErr := register(client, r, re)
if regErr != nil {
return resp, fmt.Errorf("failed auto registering Resource Provider: %s. Original error: %s", regErr, err)
return resp, fmt.Errorf("failed auto registering Resource Provider: %s. Original error: %w", regErr, err)
}
}
}

View File

@ -31,7 +31,7 @@ import (
const (
// DefaultPollingDelay is a reasonable delay between polling requests.
DefaultPollingDelay = 60 * time.Second
DefaultPollingDelay = 30 * time.Second
// DefaultPollingDuration is a reasonable total polling duration.
DefaultPollingDuration = 15 * time.Minute

View File

@ -1,3 +1,4 @@
//go:build modhack
// +build modhack
package autorest

View File

@ -241,6 +241,8 @@ func WithBaseURL(baseURL string) PrepareDecorator {
return r, fmt.Errorf("autorest: No scheme detected in URL %s", baseURL)
}
if u.RawQuery != "" {
// handle unencoded semicolons (ideally the server would send them already encoded)
u.RawQuery = strings.Replace(u.RawQuery, ";", "%3B", -1)
q, err := url.ParseQuery(u.RawQuery)
if err != nil {
return r, err

View File

@ -1,3 +1,4 @@
//go:build !go1.8
// +build !go1.8
// Copyright 2017 Microsoft Corporation

View File

@ -1,3 +1,4 @@
//go:build go1.8
// +build go1.8
// Copyright 2017 Microsoft Corporation

View File

@ -20,12 +20,14 @@ import (
"fmt"
"log"
"math"
"net"
"net/http"
"net/http/cookiejar"
"strconv"
"sync"
"time"
"github.com/Azure/go-autorest/logger"
"github.com/Azure/go-autorest/tracing"
)
@ -128,15 +130,18 @@ func sender(renengotiation tls.RenegotiationSupport) Sender {
// note that we can't init defaultSenders in init() since it will
// execute before calling code has had a chance to enable tracing
defaultSenders[renengotiation].init.Do(func() {
// Use behaviour compatible with DefaultTransport, but require TLS minimum version.
defaultTransport := http.DefaultTransport.(*http.Transport)
// copied from http.DefaultTransport with a TLS minimum version.
transport := &http.Transport{
Proxy: defaultTransport.Proxy,
DialContext: defaultTransport.DialContext,
MaxIdleConns: defaultTransport.MaxIdleConns,
IdleConnTimeout: defaultTransport.IdleConnTimeout,
TLSHandshakeTimeout: defaultTransport.TLSHandshakeTimeout,
ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout,
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
Renegotiation: renengotiation,
@ -271,6 +276,7 @@ func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator {
if err == nil {
return resp, err
}
logger.Instance.Writef(logger.LogError, "DoRetryForAttempts: received error for attempt %d: %v\n", attempt+1, err)
if !DelayForBackoff(backoff, attempt, r.Context().Done()) {
return nil, r.Context().Err()
}
@ -325,6 +331,9 @@ func doRetryForStatusCodesImpl(s Sender, r *http.Request, count429 bool, attempt
if err == nil && !ResponseHasStatusCode(resp, codes...) || IsTokenRefreshError(err) {
return resp, err
}
if err != nil {
logger.Instance.Writef(logger.LogError, "DoRetryForStatusCodes: received error for attempt %d: %v\n", attempt+1, err)
}
delayed := DelayWithRetryAfter(resp, r.Context().Done())
// if this was a 429 set the delay cap as specified.
// applicable only in the absence of a retry-after header.
@ -391,6 +400,7 @@ func DoRetryForDuration(d time.Duration, backoff time.Duration) SendDecorator {
if err == nil {
return resp, err
}
logger.Instance.Writef(logger.LogError, "DoRetryForDuration: received error for attempt %d: %v\n", attempt+1, err)
if !DelayForBackoff(backoff, attempt, r.Context().Done()) {
return nil, r.Context().Err()
}
@ -438,6 +448,7 @@ func DelayForBackoffWithCap(backoff, cap time.Duration, attempt int, cancel <-ch
if cap > 0 && d > cap {
d = cap
}
logger.Instance.Writef(logger.LogInfo, "DelayForBackoffWithCap: sleeping for %s\n", d)
select {
case <-time.After(d):
return true

View File

@ -1,3 +1,4 @@
//go:build go1.13
// +build go1.13
// Copyright 2017 Microsoft Corporation

View File

@ -1,3 +1,4 @@
//go:build !go1.13
// +build !go1.13
// Copyright 2017 Microsoft Corporation

View File

@ -1,6 +0,0 @@
language: go
go:
- 1.x
script: go test -v

View File

@ -1,7 +0,0 @@
all: test
test:
go test -v .
ex:
cd examples && ls *.go | xargs go build -o /tmp/ignore

View File

@ -68,3 +68,4 @@ examples/restful-html-template
s.html
restful-path-tail
.idea

1
vendor/github.com/emicklei/go-restful/v3/.goconvey generated vendored Normal file
View File

@ -0,0 +1 @@
ignore

13
vendor/github.com/emicklei/go-restful/v3/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,13 @@
language: go
go:
- 1.x
before_install:
- go test -v
script:
- go test -race -coverprofile=coverage.txt -covermode=atomic
after_success:
- bash <(curl -s https://codecov.io/bash)

View File

@ -1,7 +1,106 @@
## Change history of go-restful
# Change history of go-restful
## [v3.8.0] - 20221-06-06
- use exact matching of allowed domain entries, issue #489 (#493)
- this changes fixes [security] Authorization Bypass Through User-Controlled Key
by changing the behaviour of the AllowedDomains setting in the CORS filter.
To support the previous behaviour, the CORS filter type now has a AllowedDomainFunc
callback mechanism which is called when a simple domain match fails.
- add test and fix for POST without body and Content-type, issue #492 (#496)
- [Minor] Bad practice to have a mix of Receiver types. (#491)
## [v3.7.2] - 2021-11-24
- restored FilterChain (#482 by SVilgelm)
## [v3.7.1] - 2021-10-04
- fix problem with contentEncodingEnabled setting (#479)
## [v3.7.0] - 2021-09-24
- feat(parameter): adds additional openapi mappings (#478)
## [v3.6.0] - 2021-09-18
- add support for vendor extensions (#477 thx erraggy)
## [v3.5.2] - 2021-07-14
- fix removing absent route from webservice (#472)
## [v3.5.1] - 2021-04-12
- fix handling no match access selected path
- remove obsolete field
## [v3.5.0] - 2021-04-10
- add check for wildcard (#463) in CORS
- add access to Route from Request, issue #459 (#462)
## [v3.4.0] - 2020-11-10
- Added OPTIONS to WebService
## [v3.3.2] - 2020-01-23
- Fixed duplicate compression in dispatch. #449
## [v3.3.1] - 2020-08-31
- Added check on writer to prevent compression of response twice. #447
## [v3.3.0] - 2020-08-19
- Enable content encoding on Handle and ServeHTTP (#446)
- List available representations in 406 body (#437)
- Convert to string using rune() (#443)
## [v3.2.0] - 2020-06-21
- 405 Method Not Allowed must have Allow header (#436) (thx Bracken <abdawson@gmail.com>)
- add field allowedMethodsWithoutContentType (#424)
## [v3.1.0]
- support describing response headers (#426)
- fix openapi examples (#425)
v3.0.0
- fix: use request/response resulting from filter chain
- add Go module
Module consumer should use github.com/emicklei/go-restful/v3 as import path
v2.10.0
- support for Custom Verbs (thanks Vinci Xu <277040271@qq.com>)
- fixed static example (thanks Arthur <yang_yapo@126.com>)
- simplify code (thanks Christian Muehlhaeuser <muesli@gmail.com>)
- added JWT HMAC with SHA-512 authentication code example (thanks Amim Knabben <amim.knabben@gmail.com>)
v2.9.6
- small optimization in filter code
v2.11.1
- fix WriteError return value (#415)
v2.11.0
- allow prefix and suffix in path variable expression (#414)
v2.9.6
- support google custome verb (#413)
v2.9.5
- fix panic in Response.WriteError if err == nil
v2.9.4

8
vendor/github.com/emicklei/go-restful/v3/Makefile generated vendored Normal file
View File

@ -0,0 +1,8 @@
all: test
test:
go vet .
go test -cover -v .
ex:
find ./examples -type f -name "*.go" | xargs -I {} go build -o /tmp/ignore {}

View File

@ -4,9 +4,10 @@ package for building REST-style Web Services using Google Go
[![Build Status](https://travis-ci.org/emicklei/go-restful.png)](https://travis-ci.org/emicklei/go-restful)
[![Go Report Card](https://goreportcard.com/badge/github.com/emicklei/go-restful)](https://goreportcard.com/report/github.com/emicklei/go-restful)
[![GoDoc](https://godoc.org/github.com/emicklei/go-restful?status.svg)](https://godoc.org/github.com/emicklei/go-restful)
[![GoDoc](https://godoc.org/github.com/emicklei/go-restful?status.svg)](https://pkg.go.dev/github.com/emicklei/go-restful)
[![codecov](https://codecov.io/gh/emicklei/go-restful/branch/master/graph/badge.svg)](https://codecov.io/gh/emicklei/go-restful)
- [Code examples](https://github.com/emicklei/go-restful/tree/master/examples)
- [Code examples use v3](https://github.com/emicklei/go-restful/tree/v3/examples)
REST asks developers to use HTTP methods explicitly and in a way that's consistent with the protocol definition. This basic REST design principle establishes a one-to-one mapping between create, read, update, and delete (CRUD) operations and HTTP methods. According to this mapping:
@ -18,6 +19,28 @@ REST asks developers to use HTTP methods explicitly and in a way that's consiste
- PATCH = Update partial content of a resource
- OPTIONS = Get information about the communication options for the request URI
### Usage
#### Without Go Modules
All versions up to `v2.*.*` (on the master) are not supporting Go modules.
```
import (
restful "github.com/emicklei/go-restful"
)
```
#### Using Go Modules
As of version `v3.0.0` (on the v3 branch), this package supports Go modules.
```
import (
restful "github.com/emicklei/go-restful/v3"
)
```
### Example
```Go
@ -39,15 +62,15 @@ func (u UserResource) findUser(request *restful.Request, response *restful.Respo
}
```
[Full API of a UserResource](https://github.com/emicklei/go-restful/tree/master/examples/restful-user-resource.go)
[Full API of a UserResource](https://github.com/emicklei/go-restful/blob/v3/examples/user-resource/restful-user-resource.go)
### Features
- Routes for request &#8594; function mapping with path parameter (e.g. {id}) support
- Routes for request &#8594; function mapping with path parameter (e.g. {id} but also prefix_{var} and {var}_suffix) support
- Configurable router:
- (default) Fast routing algorithm that allows static elements, regular expressions and dynamic parameters in the URL path (e.g. /meetings/{id} or /static/{subpath:*}
- (default) Fast routing algorithm that allows static elements, [google custom method](https://cloud.google.com/apis/design/custom_methods), regular expressions and dynamic parameters in the URL path (e.g. /resource/name:customVerb, /meetings/{id} or /static/{subpath:*})
- Routing algorithm after [JSR311](http://jsr311.java.net/nonav/releases/1.1/spec/spec.html) that is implemented using (but does **not** accept) regular expressions
- Request API for reading structs from JSON/XML and accesing parameters (path,query,header)
- Request API for reading structs from JSON/XML and accessing parameters (path,query,header)
- Response API for writing structs to JSON/XML and setting headers
- Customizable encoding using EntityReaderWriter registration
- Filters for intercepting the request &#8594; response flow on Service or Route level
@ -73,10 +96,9 @@ There are several hooks to customize the behavior of the go-restful package.
- Encoders for other serializers
- Use [jsoniter](https://github.com/json-iterator/go) by build this package using a tag, e.g. `go build -tags=jsoniter .`
TODO: write examples of these.
## Resources
- [Example programs](./examples)
- [Example posted on blog](http://ernestmicklei.com/2012/11/go-restful-first-working-example/)
- [Design explained on blog](http://ernestmicklei.com/2012/11/go-restful-api-design/)
- [sourcegraph](https://sourcegraph.com/github.com/emicklei/go-restful)
@ -85,4 +107,4 @@ TODO: write examples of these.
Type ```git shortlog -s``` for a full list of contributors.
© 2012 - 2018, http://ernestmicklei.com. MIT License. Contributions are welcome.
© 2012 - 2022, http://ernestmicklei.com. MIT License. Contributions are welcome.

13
vendor/github.com/emicklei/go-restful/v3/SECURITY.md generated vendored Normal file
View File

@ -0,0 +1,13 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| v3.7.x | :white_check_mark: |
| < v3.0.1 | :x: |
## Reporting a Vulnerability
Create an Issue and put the label `[security]` in the title of the issue.
Valid reported security issues are expected to be solved within a week.

View File

@ -83,7 +83,11 @@ func (c *CompressingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error
}
// WantsCompressedResponse reads the Accept-Encoding header to see if and which encoding is requested.
func wantsCompressedResponse(httpRequest *http.Request) (bool, string) {
// It also inspects the httpWriter whether its content-encoding is already set (non-empty).
func wantsCompressedResponse(httpRequest *http.Request, httpWriter http.ResponseWriter) (bool, string) {
if contentEncoding := httpWriter.Header().Get(HEADER_ContentEncoding); contentEncoding != "" {
return false, ""
}
header := httpRequest.Header.Get(HEADER_AcceptEncoding)
gi := strings.Index(header, ENCODING_GZIP)
zi := strings.Index(header, ENCODING_DEFLATE)

View File

@ -14,7 +14,7 @@ import (
"strings"
"sync"
"github.com/emicklei/go-restful/log"
"github.com/emicklei/go-restful/v3/log"
)
// Container holds a collection of WebServices and a http.ServeMux to dispatch http requests.
@ -185,6 +185,11 @@ func logStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter)
// when a ServiceError is returned during route selection. Default implementation
// calls resp.WriteErrorString(err.Code, err.Message)
func writeServiceError(err ServiceError, req *Request, resp *Response) {
for header, values := range err.Header {
for _, value := range values {
resp.Header().Add(header, value)
}
}
resp.WriteErrorString(err.Code, err.Message)
}
@ -201,6 +206,7 @@ func (c *Container) Dispatch(httpWriter http.ResponseWriter, httpRequest *http.R
// Dispatch the incoming Http Request to a matching WebService.
func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.Request) {
// so we can assign a compressing one later
writer := httpWriter
// CompressingResponseWriter should be closed after all operations are done
@ -231,28 +237,8 @@ func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.R
c.webServices,
httpRequest)
}()
// Detect if compression is needed
// assume without compression, test for override
contentEncodingEnabled := c.contentEncodingEnabled
if route != nil && route.contentEncodingEnabled != nil {
contentEncodingEnabled = *route.contentEncodingEnabled
}
if contentEncodingEnabled {
doCompress, encoding := wantsCompressedResponse(httpRequest)
if doCompress {
var err error
writer, err = NewCompressingResponseWriter(httpWriter, encoding)
if err != nil {
log.Print("unable to install compressor: ", err)
httpWriter.WriteHeader(http.StatusInternalServerError)
return
}
}
}
if err != nil {
// a non-200 response has already been written
// a non-200 response (may be compressed) has already been written
// run container filters anyway ; they should not touch the response...
chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) {
switch err.(type) {
@ -265,6 +251,29 @@ func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.R
chain.ProcessFilter(NewRequest(httpRequest), NewResponse(writer))
return
}
// Unless httpWriter is already an CompressingResponseWriter see if we need to install one
if _, isCompressing := httpWriter.(*CompressingResponseWriter); !isCompressing {
// Detect if compression is needed
// assume without compression, test for override
contentEncodingEnabled := c.contentEncodingEnabled
if route != nil && route.contentEncodingEnabled != nil {
contentEncodingEnabled = *route.contentEncodingEnabled
}
if contentEncodingEnabled {
doCompress, encoding := wantsCompressedResponse(httpRequest, httpWriter)
if doCompress {
var err error
writer, err = NewCompressingResponseWriter(httpWriter, encoding)
if err != nil {
log.Print("unable to install compressor: ", err)
httpWriter.WriteHeader(http.StatusInternalServerError)
return
}
}
}
}
pathProcessor, routerProcessesPath := c.router.(PathProcessor)
if !routerProcessesPath {
pathProcessor = defaultPathProcessor{}
@ -272,16 +281,18 @@ func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.R
pathParams := pathProcessor.ExtractParameters(route, webService, httpRequest.URL.Path)
wrappedRequest, wrappedResponse := route.wrapRequestResponse(writer, httpRequest, pathParams)
// pass through filters (if any)
if len(c.containerFilters)+len(webService.filters)+len(route.Filters) > 0 {
if size := len(c.containerFilters) + len(webService.filters) + len(route.Filters); size > 0 {
// compose filter chain
allFilters := []FilterFunction{}
allFilters := make([]FilterFunction, 0, size)
allFilters = append(allFilters, c.containerFilters...)
allFilters = append(allFilters, webService.filters...)
allFilters = append(allFilters, route.Filters...)
chain := FilterChain{Filters: allFilters, Target: func(req *Request, resp *Response) {
// handle request by route after passing all filters
route.Function(wrappedRequest, wrappedResponse)
}}
chain := FilterChain{
Filters: allFilters,
Target: route.Function,
ParameterDocs: route.ParameterDocs,
Operation: route.Operation,
}
chain.ProcessFilter(wrappedRequest, wrappedResponse)
} else {
// no filters, handle request by route
@ -299,13 +310,75 @@ func fixedPrefixPath(pathspec string) string {
}
// ServeHTTP implements net/http.Handler therefore a Container can be a Handler in a http.Server
func (c *Container) ServeHTTP(httpwriter http.ResponseWriter, httpRequest *http.Request) {
c.ServeMux.ServeHTTP(httpwriter, httpRequest)
func (c *Container) ServeHTTP(httpWriter http.ResponseWriter, httpRequest *http.Request) {
// Skip, if content encoding is disabled
if !c.contentEncodingEnabled {
c.ServeMux.ServeHTTP(httpWriter, httpRequest)
return
}
// content encoding is enabled
// Skip, if httpWriter is already an CompressingResponseWriter
if _, ok := httpWriter.(*CompressingResponseWriter); ok {
c.ServeMux.ServeHTTP(httpWriter, httpRequest)
return
}
writer := httpWriter
// CompressingResponseWriter should be closed after all operations are done
defer func() {
if compressWriter, ok := writer.(*CompressingResponseWriter); ok {
compressWriter.Close()
}
}()
doCompress, encoding := wantsCompressedResponse(httpRequest, httpWriter)
if doCompress {
var err error
writer, err = NewCompressingResponseWriter(httpWriter, encoding)
if err != nil {
log.Print("unable to install compressor: ", err)
httpWriter.WriteHeader(http.StatusInternalServerError)
return
}
}
c.ServeMux.ServeHTTP(writer, httpRequest)
}
// Handle registers the handler for the given pattern. If a handler already exists for pattern, Handle panics.
func (c *Container) Handle(pattern string, handler http.Handler) {
c.ServeMux.Handle(pattern, handler)
c.ServeMux.Handle(pattern, http.HandlerFunc(func(httpWriter http.ResponseWriter, httpRequest *http.Request) {
// Skip, if httpWriter is already an CompressingResponseWriter
if _, ok := httpWriter.(*CompressingResponseWriter); ok {
handler.ServeHTTP(httpWriter, httpRequest)
return
}
writer := httpWriter
// CompressingResponseWriter should be closed after all operations are done
defer func() {
if compressWriter, ok := writer.(*CompressingResponseWriter); ok {
compressWriter.Close()
}
}()
if c.contentEncodingEnabled {
doCompress, encoding := wantsCompressedResponse(httpRequest, httpWriter)
if doCompress {
var err error
writer, err = NewCompressingResponseWriter(httpWriter, encoding)
if err != nil {
log.Print("unable to install compressor: ", err)
httpWriter.WriteHeader(http.StatusInternalServerError)
return
}
}
}
handler.ServeHTTP(writer, httpRequest)
}))
}
// HandleWithFilter registers the handler for the given pattern.
@ -319,7 +392,7 @@ func (c *Container) HandleWithFilter(pattern string, handler http.Handler) {
}
chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) {
handler.ServeHTTP(httpResponse, httpRequest)
handler.ServeHTTP(resp, req.Request)
}}
chain.ProcessFilter(NewRequest(httpRequest), NewResponse(httpResponse))
}

View File

@ -19,8 +19,21 @@ import (
// http://www.html5rocks.com/en/tutorials/cors/#toc-handling-a-not-so-simple-request
type CrossOriginResourceSharing struct {
ExposeHeaders []string // list of Header names
AllowedHeaders []string // list of Header names
AllowedDomains []string // list of allowed values for Http Origin. An allowed value can be a regular expression to support subdomain matching. If empty all are allowed.
// AllowedHeaders is alist of Header names. Checking is case-insensitive.
// The list may contain the special wildcard string ".*" ; all is allowed
AllowedHeaders []string
// AllowedDomains is a list of allowed values for Http Origin.
// The list may contain the special wildcard string ".*" ; all is allowed
// If empty all are allowed.
AllowedDomains []string
// AllowedDomainFunc is optional and is a function that will do the check
// when the origin is not part of the AllowedDomains and it does not contain the wildcard ".*".
AllowedDomainFunc func(origin string) bool
// AllowedMethods is either empty or has a list of http methods names. Checking is case-insensitive.
AllowedMethods []string
MaxAge int // number of seconds before requiring new Options request
CookiesAllowed bool
@ -119,37 +132,25 @@ func (c CrossOriginResourceSharing) isOriginAllowed(origin string) bool {
if len(origin) == 0 {
return false
}
lowerOrigin := strings.ToLower(origin)
if len(c.AllowedDomains) == 0 {
if c.AllowedDomainFunc != nil {
return c.AllowedDomainFunc(lowerOrigin)
}
return true
}
allowed := false
// exact match on each allowed domain
for _, domain := range c.AllowedDomains {
if domain == origin {
allowed = true
break
if domain == ".*" || strings.ToLower(domain) == lowerOrigin {
return true
}
}
if !allowed {
if len(c.allowedOriginPatterns) == 0 {
// compile allowed domains to allowed origin patterns
allowedOriginRegexps, err := compileRegexps(c.AllowedDomains)
if err != nil {
if c.AllowedDomainFunc != nil {
return c.AllowedDomainFunc(origin)
}
return false
}
c.allowedOriginPatterns = allowedOriginRegexps
}
for _, pattern := range c.allowedOriginPatterns {
if allowed = pattern.MatchString(origin); allowed {
break
}
}
}
return allowed
}
func (c CrossOriginResourceSharing) setAllowOriginHeader(req *Request, resp *Response) {
origin := req.Request.Header.Get(HEADER_Origin)
@ -184,19 +185,9 @@ func (c CrossOriginResourceSharing) isValidAccessControlRequestHeader(header str
if strings.ToLower(each) == strings.ToLower(header) {
return true
}
if each == "*" {
return true
}
}
return false
}
// Take a list of strings and compile them into a list of regular expressions.
func compileRegexps(regexpStrings []string) ([]*regexp.Regexp, error) {
regexps := []*regexp.Regexp{}
for _, regexpStr := range regexpStrings {
r, err := regexp.Compile(regexpStr)
if err != nil {
return regexps, err
}
regexps = append(regexps, r)
}
return regexps, nil
}

View File

@ -47,7 +47,7 @@ func (c CurlyRouter) SelectRoute(
func (c CurlyRouter) selectRoutes(ws *WebService, requestTokens []string) sortableCurlyRoutes {
candidates := make(sortableCurlyRoutes, 0, 8)
for _, each := range ws.routes {
matches, paramCount, staticCount := c.matchesRouteByPathTokens(each.pathParts, requestTokens)
matches, paramCount, staticCount := c.matchesRouteByPathTokens(each.pathParts, requestTokens, each.hasCustomVerb)
if matches {
candidates.add(curlyRoute{each, paramCount, staticCount}) // TODO make sure Routes() return pointers?
}
@ -57,7 +57,7 @@ func (c CurlyRouter) selectRoutes(ws *WebService, requestTokens []string) sortab
}
// matchesRouteByPathTokens computes whether it matches, howmany parameters do match and what the number of static path elements are.
func (c CurlyRouter) matchesRouteByPathTokens(routeTokens, requestTokens []string) (matches bool, paramCount int, staticCount int) {
func (c CurlyRouter) matchesRouteByPathTokens(routeTokens, requestTokens []string, routeHasCustomVerb bool) (matches bool, paramCount int, staticCount int) {
if len(routeTokens) < len(requestTokens) {
// proceed in matching only if last routeToken is wildcard
count := len(routeTokens)
@ -72,6 +72,15 @@ func (c CurlyRouter) matchesRouteByPathTokens(routeTokens, requestTokens []strin
return false, 0, 0
}
requestToken := requestTokens[i]
if routeHasCustomVerb && hasCustomVerb(routeToken){
if !isMatchCustomVerb(routeToken, requestToken) {
return false, 0, 0
}
staticCount++
requestToken = removeCustomVerb(requestToken)
routeToken = removeCustomVerb(routeToken)
}
if strings.HasPrefix(routeToken, "{") {
paramCount++
if colon := strings.Index(routeToken, ":"); colon != -1 {

View File

@ -0,0 +1,29 @@
package restful
import (
"fmt"
"regexp"
)
var (
customVerbReg = regexp.MustCompile(":([A-Za-z]+)$")
)
func hasCustomVerb(routeToken string) bool {
return customVerbReg.MatchString(routeToken)
}
func isMatchCustomVerb(routeToken string, pathToken string) bool {
rs := customVerbReg.FindStringSubmatch(routeToken)
if len(rs) < 2 {
return false
}
customVerb := rs[1]
specificVerbReg := regexp.MustCompile(fmt.Sprintf(":%s$", customVerb))
return specificVerbReg.MatchString(pathToken)
}
func removeCustomVerb(str string) string {
return customVerbReg.ReplaceAllString(str, "")
}

View File

@ -28,7 +28,7 @@ This package has the logic to find the best matching Route and if found, call it
The (*Request, *Response) arguments provide functions for reading information from the request and writing information back to the response.
See the example https://github.com/emicklei/go-restful/blob/master/examples/restful-user-resource.go with a full implementation.
See the example https://github.com/emicklei/go-restful/blob/v3/examples/user-resource/restful-user-resource.go with a full implementation.
Regular expression matching Routes
@ -82,7 +82,7 @@ These are processed before calling the function associated with the Route.
// install 2 chained route filters (processed before calling findUser)
ws.Route(ws.GET("/{user-id}").Filter(routeLogging).Filter(NewCountFilter().routeCounter).To(findUser))
See the example https://github.com/emicklei/go-restful/blob/master/examples/restful-filters.go with full implementations.
See the example https://github.com/emicklei/go-restful/blob/v3/examples/filters/restful-filters.go with full implementations.
Response Encoding
@ -93,7 +93,7 @@ Two encodings are supported: gzip and deflate. To enable this for all responses:
If a Http request includes the Accept-Encoding header then the response content will be compressed using the specified encoding.
Alternatively, you can create a Filter that performs the encoding and install it per WebService or Route.
See the example https://github.com/emicklei/go-restful/blob/master/examples/restful-encoding-filter.go
See the example https://github.com/emicklei/go-restful/blob/v3/examples/encoding/restful-encoding-filter.go
OPTIONS support

21
vendor/github.com/emicklei/go-restful/v3/extensions.go generated vendored Normal file
View File

@ -0,0 +1,21 @@
package restful
// Copyright 2021 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
// ExtensionProperties provides storage of vendor extensions for entities
type ExtensionProperties struct {
// Extensions vendor extensions used to describe extra functionality
// (https://swagger.io/docs/specification/2-0/swagger-extensions/)
Extensions map[string]interface{}
}
// AddExtension adds or updates a key=value pair to the extension map.
func (ep *ExtensionProperties) AddExtension(key string, value interface{}) {
if ep.Extensions == nil {
ep.Extensions = map[string]interface{}{key: value}
} else {
ep.Extensions[key] = value
}
}

View File

@ -9,6 +9,8 @@ type FilterChain struct {
Filters []FilterFunction // ordered list of FilterFunction
Index int // index into filters that is currently in progress
Target RouteFunction // function to call after passing all filters
ParameterDocs []*Parameter // the parameter docs for the route
Operation string // the name of the operation
}
// ProcessFilter passes the request,response pair through the next of Filters.

View File

@ -9,6 +9,7 @@ import (
"fmt"
"net/http"
"sort"
"strings"
)
// RouterJSR311 implements the flow for matching Requests to Routes (and consequently Resource Functions)
@ -98,7 +99,18 @@ func (r RouterJSR311) detectRoute(routes []Route, httpRequest *http.Request) (*R
if trace {
traceLogger.Printf("no Route found (in %d routes) that matches HTTP method %s\n", len(previous), httpRequest.Method)
}
return nil, NewError(http.StatusMethodNotAllowed, "405: Method Not Allowed")
allowed := []string{}
allowedLoop:
for _, candidate := range previous {
for _, method := range allowed {
if method == candidate.Method {
continue allowedLoop
}
}
allowed = append(allowed, candidate.Method)
}
header := http.Header{"Allow": []string{strings.Join(allowed, ", ")}}
return nil, NewErrorWithHeader(http.StatusMethodNotAllowed, "405: Method Not Allowed", header)
}
// content-type
@ -135,7 +147,24 @@ func (r RouterJSR311) detectRoute(routes []Route, httpRequest *http.Request) (*R
if trace {
traceLogger.Printf("no Route found (from %d) that matches HTTP Accept: %s\n", len(previous), accept)
}
return nil, NewError(http.StatusNotAcceptable, "406: Not Acceptable")
available := []string{}
for _, candidate := range previous {
available = append(available, candidate.Produces...)
}
// if POST,PUT,PATCH without body
method, length := httpRequest.Method, httpRequest.Header.Get("Content-Length")
if (method == http.MethodPost ||
method == http.MethodPut ||
method == http.MethodPatch) && length == "" {
return nil, NewError(
http.StatusUnsupportedMediaType,
fmt.Sprintf("415: Unsupported Media Type\n\nAvailable representations: %s", strings.Join(available, ", ")),
)
}
return nil, NewError(
http.StatusNotAcceptable,
fmt.Sprintf("406: Not Acceptable\n\nAvailable representations: %s", strings.Join(available, ", ")),
)
}
// return r.bestMatchByMedia(outputMediaOk, contentType, accept), nil
return candidates[0], nil

View File

@ -4,7 +4,7 @@ package restful
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import (
"github.com/emicklei/go-restful/log"
"github.com/emicklei/go-restful/v3/log"
)
var trace bool = false

View File

@ -1,5 +1,7 @@
package restful
import "sort"
// Copyright 2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
@ -52,13 +54,25 @@ type Parameter struct {
// ParameterData represents the state of a Parameter.
// It is made public to make it accessible to e.g. the Swagger package.
type ParameterData struct {
ExtensionProperties
Name, Description, DataType, DataFormat string
Kind int
Required bool
// AllowableValues is deprecated. Use PossibleValues instead
AllowableValues map[string]string
PossibleValues []string
AllowMultiple bool
AllowEmptyValue bool
DefaultValue string
CollectionFormat string
Pattern string
Minimum *float64
Maximum *float64
MinLength *int64
MaxLength *int64
MinItems *int64
MaxItems *int64
UniqueItems bool
}
// Data returns the state of the Parameter
@ -106,9 +120,38 @@ func (p *Parameter) AllowMultiple(multiple bool) *Parameter {
return p
}
// AllowableValues sets the allowableValues field and returns the receiver
// AddExtension adds or updates a key=value pair to the extension map
func (p *Parameter) AddExtension(key string, value interface{}) *Parameter {
p.data.AddExtension(key, value)
return p
}
// AllowEmptyValue sets the AllowEmptyValue field and returns the receiver
func (p *Parameter) AllowEmptyValue(multiple bool) *Parameter {
p.data.AllowEmptyValue = multiple
return p
}
// AllowableValues is deprecated. Use PossibleValues instead. Both will be set.
func (p *Parameter) AllowableValues(values map[string]string) *Parameter {
p.data.AllowableValues = values
allowableSortedKeys := make([]string, 0, len(values))
for k := range values {
allowableSortedKeys = append(allowableSortedKeys, k)
}
sort.Strings(allowableSortedKeys)
p.data.PossibleValues = make([]string, 0, len(values))
for _, k := range allowableSortedKeys {
p.data.PossibleValues = append(p.data.PossibleValues, values[k])
}
return p
}
// PossibleValues sets the possible values field and returns the receiver
func (p *Parameter) PossibleValues(values []string) *Parameter {
p.data.PossibleValues = values
return p
}
@ -141,3 +184,51 @@ func (p *Parameter) CollectionFormat(format CollectionFormat) *Parameter {
p.data.CollectionFormat = format.String()
return p
}
// Pattern sets the pattern field and returns the receiver
func (p *Parameter) Pattern(pattern string) *Parameter {
p.data.Pattern = pattern
return p
}
// Minimum sets the minimum field and returns the receiver
func (p *Parameter) Minimum(minimum float64) *Parameter {
p.data.Minimum = &minimum
return p
}
// Maximum sets the maximum field and returns the receiver
func (p *Parameter) Maximum(maximum float64) *Parameter {
p.data.Maximum = &maximum
return p
}
// MinLength sets the minLength field and returns the receiver
func (p *Parameter) MinLength(minLength int64) *Parameter {
p.data.MinLength = &minLength
return p
}
// MaxLength sets the maxLength field and returns the receiver
func (p *Parameter) MaxLength(maxLength int64) *Parameter {
p.data.MaxLength = &maxLength
return p
}
// MinItems sets the minItems field and returns the receiver
func (p *Parameter) MinItems(minItems int64) *Parameter {
p.data.MinItems = &minItems
return p
}
// MaxItems sets the maxItems field and returns the receiver
func (p *Parameter) MaxItems(maxItems int64) *Parameter {
p.data.MaxItems = &maxItems
return p
}
// UniqueItems sets the uniqueItems field and returns the receiver
func (p *Parameter) UniqueItems(uniqueItems bool) *Parameter {
p.data.UniqueItems = uniqueItems
return p
}

View File

@ -29,7 +29,12 @@ func (d defaultPathProcessor) ExtractParameters(r *Route, _ *WebService, urlPath
} else {
value = urlParts[i]
}
if strings.HasPrefix(key, "{") { // path-parameter
if r.hasCustomVerb && hasCustomVerb(key) {
key = removeCustomVerb(key)
value = removeCustomVerb(value)
}
if strings.Index(key, "{") > -1 { // path-parameter
if colon := strings.Index(key, ":"); colon != -1 {
// extract by regex
regPart := key[colon+1 : len(key)-1]
@ -42,7 +47,13 @@ func (d defaultPathProcessor) ExtractParameters(r *Route, _ *WebService, urlPath
}
} else {
// without enclosing {}
pathParameters[key[1:len(key)-1]] = value
startIndex := strings.Index(key, "{")
endKeyIndex := strings.Index(key, "}")
suffixLength := len(key) - endKeyIndex - 1
endValueIndex := len(value) - suffixLength
pathParameters[key[startIndex+1:endKeyIndex]] = value[startIndex:endValueIndex]
}
}
}

View File

@ -16,7 +16,7 @@ type Request struct {
Request *http.Request
pathParameters map[string]string
attributes map[string]interface{} // for storing request-scoped values
selectedRoutePath string // root path + route path that matched the request, e.g. /meetings/{id}/attendees
selectedRoute *Route // is nil when no route was matched
}
func NewRequest(httpRequest *http.Request) *Request {
@ -113,6 +113,20 @@ func (r Request) Attribute(name string) interface{} {
}
// SelectedRoutePath root path + route path that matched the request, e.g. /meetings/{id}/attendees
// If no route was matched then return an empty string.
func (r Request) SelectedRoutePath() string {
return r.selectedRoutePath
if r.selectedRoute == nil {
return ""
}
// skip creating an accessor
return r.selectedRoute.Path
}
// SelectedRoute returns a reader to access the selected Route by the container
// Returns nil if no route was matched.
func (r Request) SelectedRoute() RouteReader {
if r.selectedRoute == nil {
return nil
}
return routeAccessor{route: r.selectedRoute}
}

View File

@ -174,15 +174,16 @@ func (r *Response) WriteHeaderAndJson(status int, value interface{}, contentType
return writeJSON(r, status, contentType, value)
}
// WriteError write the http status and the error string on the response. err can be nil.
func (r *Response) WriteError(httpStatus int, err error) error {
// WriteError writes the http status and the error string on the response. err can be nil.
// Return an error if writing was not successful.
func (r *Response) WriteError(httpStatus int, err error) (writeErr error) {
r.err = err
if err == nil {
r.WriteErrorString(httpStatus, "")
writeErr = r.WriteErrorString(httpStatus, "")
} else {
r.WriteErrorString(httpStatus, err.Error())
writeErr = r.WriteErrorString(httpStatus, err.Error())
}
return err
return writeErr
}
// WriteServiceError is a convenience method for a responding with a status and a ServiceError

View File

@ -19,6 +19,7 @@ type RouteSelectionConditionFunction func(httpRequest *http.Request) bool
// Route binds a HTTP Method,Path,Consumes combination to a RouteFunction.
type Route struct {
ExtensionProperties
Method string
Produces []string
Consumes []string
@ -49,35 +50,33 @@ type Route struct {
//Overrides the container.contentEncodingEnabled
contentEncodingEnabled *bool
// indicate route path has custom verb
hasCustomVerb bool
// if a request does not include a content-type header then
// depending on the method, it may return a 415 Unsupported Media
// Must have uppercase HTTP Method names such as GET,HEAD,OPTIONS,...
allowedMethodsWithoutContentType []string
}
// Initialize for Route
func (r *Route) postBuild() {
r.pathParts = tokenizePath(r.Path)
r.hasCustomVerb = hasCustomVerb(r.Path)
}
// Create Request and Response from their http versions
func (r *Route) wrapRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request, pathParams map[string]string) (*Request, *Response) {
wrappedRequest := NewRequest(httpRequest)
wrappedRequest.pathParameters = pathParams
wrappedRequest.selectedRoutePath = r.Path
wrappedRequest.selectedRoute = r
wrappedResponse := NewResponse(httpWriter)
wrappedResponse.requestAccept = httpRequest.Header.Get(HEADER_Accept)
wrappedResponse.routeProduces = r.Produces
return wrappedRequest, wrappedResponse
}
// dispatchWithFilters call the function after passing through its own filters
func (r *Route) dispatchWithFilters(wrappedRequest *Request, wrappedResponse *Response) {
if len(r.Filters) > 0 {
chain := FilterChain{Filters: r.Filters, Target: r.Function}
chain.ProcessFilter(wrappedRequest, wrappedResponse)
} else {
// unfiltered
r.Function(wrappedRequest, wrappedResponse)
}
}
func stringTrimSpaceCutset(r rune) bool {
return r == ' '
}
@ -121,9 +120,18 @@ func (r Route) matchesContentType(mimeTypes string) bool {
if len(mimeTypes) == 0 {
// idempotent methods with (most-likely or guaranteed) empty content match missing Content-Type
m := r.Method
// if route specifies less or non-idempotent methods then use that
if len(r.allowedMethodsWithoutContentType) > 0 {
for _, each := range r.allowedMethodsWithoutContentType {
if m == each {
return true
}
}
} else {
if m == "GET" || m == "HEAD" || m == "OPTIONS" || m == "DELETE" || m == "TRACE" {
return true
}
}
// proceed with default
mimeTypes = MIME_OCTET
}
@ -160,11 +168,11 @@ func tokenizePath(path string) []string {
}
// for debugging
func (r Route) String() string {
func (r *Route) String() string {
return r.Method + " " + r.Path
}
// EnableContentEncoding (default=false) allows for GZIP or DEFLATE encoding of responses. Overrides the container.contentEncodingEnabled value.
func (r Route) EnableContentEncoding(enabled bool) {
func (r *Route) EnableContentEncoding(enabled bool) {
r.contentEncodingEnabled = &enabled
}

View File

@ -12,7 +12,7 @@ import (
"strings"
"sync/atomic"
"github.com/emicklei/go-restful/log"
"github.com/emicklei/go-restful/v3/log"
)
// RouteBuilder is a helper to construct Routes.
@ -25,6 +25,7 @@ type RouteBuilder struct {
function RouteFunction // required
filters []FilterFunction
conditions []RouteSelectionConditionFunction
allowedMethodsWithoutContentType []string // see Route
typeNameHandleFunc TypeNameHandleFunction // required
@ -37,6 +38,7 @@ type RouteBuilder struct {
errorMap map[int]ResponseError
defaultResponse *ResponseError
metadata map[string]interface{}
extensions map[string]interface{}
deprecated bool
contentEncodingEnabled *bool
}
@ -176,6 +178,15 @@ func (b *RouteBuilder) Returns(code int, message string, model interface{}) *Rou
return b
}
// ReturnsWithHeaders is similar to Returns, but can specify response headers
func (b *RouteBuilder) ReturnsWithHeaders(code int, message string, model interface{}, headers map[string]Header) *RouteBuilder {
b.Returns(code, message, model)
err := b.errorMap[code]
err.Headers = headers
b.errorMap[code] = err
return b
}
// DefaultReturns is a special Returns call that sets the default of the response.
func (b *RouteBuilder) DefaultReturns(message string, model interface{}) *RouteBuilder {
b.defaultResponse = &ResponseError{
@ -194,20 +205,57 @@ func (b *RouteBuilder) Metadata(key string, value interface{}) *RouteBuilder {
return b
}
// AddExtension adds or updates a key=value pair to the extensions map.
func (b *RouteBuilder) AddExtension(key string, value interface{}) *RouteBuilder {
if b.extensions == nil {
b.extensions = map[string]interface{}{}
}
b.extensions[key] = value
return b
}
// Deprecate sets the value of deprecated to true. Deprecated routes have a special UI treatment to warn against use
func (b *RouteBuilder) Deprecate() *RouteBuilder {
b.deprecated = true
return b
}
// AllowedMethodsWithoutContentType overrides the default list GET,HEAD,OPTIONS,DELETE,TRACE
// If a request does not include a content-type header then
// depending on the method, it may return a 415 Unsupported Media.
// Must have uppercase HTTP Method names such as GET,HEAD,OPTIONS,...
func (b *RouteBuilder) AllowedMethodsWithoutContentType(methods []string) *RouteBuilder {
b.allowedMethodsWithoutContentType = methods
return b
}
// ResponseError represents a response; not necessarily an error.
type ResponseError struct {
ExtensionProperties
Code int
Message string
Model interface{}
Headers map[string]Header
IsDefault bool
}
// Header describes a header for a response of the API
//
// For more information: http://goo.gl/8us55a#headerObject
type Header struct {
*Items
Description string
}
// Items describe swagger simple schemas for headers
type Items struct {
Type string
Format string
Items *Items
CollectionFormat string
Default interface{}
}
func (b *RouteBuilder) servicePath(path string) *RouteBuilder {
b.rootPath = path
return b
@ -296,7 +344,9 @@ func (b *RouteBuilder) Build() Route {
Metadata: b.metadata,
Deprecated: b.deprecated,
contentEncodingEnabled: b.contentEncodingEnabled,
allowedMethodsWithoutContentType: b.allowedMethodsWithoutContentType,
}
route.Extensions = b.extensions
route.postBuild()
return route
}

View File

@ -0,0 +1,66 @@
package restful
// Copyright 2021 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
type RouteReader interface {
Method() string
Consumes() []string
Path() string
Doc() string
Notes() string
Operation() string
ParameterDocs() []*Parameter
// Returns a copy
Metadata() map[string]interface{}
Deprecated() bool
}
type routeAccessor struct {
route *Route
}
func (r routeAccessor) Method() string {
return r.route.Method
}
func (r routeAccessor) Consumes() []string {
return r.route.Consumes[:]
}
func (r routeAccessor) Path() string {
return r.route.Path
}
func (r routeAccessor) Doc() string {
return r.route.Doc
}
func (r routeAccessor) Notes() string {
return r.route.Notes
}
func (r routeAccessor) Operation() string {
return r.route.Operation
}
func (r routeAccessor) ParameterDocs() []*Parameter {
return r.route.ParameterDocs[:]
}
// Returns a copy
func (r routeAccessor) Metadata() map[string]interface{} {
return copyMap(r.route.Metadata)
}
func (r routeAccessor) Deprecated() bool {
return r.route.Deprecated
}
// https://stackoverflow.com/questions/23057785/how-to-copy-a-map
func copyMap(m map[string]interface{}) map[string]interface{} {
cp := make(map[string]interface{})
for k, v := range m {
vm, ok := v.(map[string]interface{})
if ok {
cp[k] = copyMap(vm)
} else {
cp[k] = v
}
}
return cp
}

View File

@ -4,12 +4,16 @@ package restful
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import "fmt"
import (
"fmt"
"net/http"
)
// ServiceError is a transport object to pass information about a non-Http error occurred in a WebService while processing a request.
type ServiceError struct {
Code int
Message string
Header http.Header
}
// NewError returns a ServiceError using the code and reason
@ -17,6 +21,11 @@ func NewError(code int, message string) ServiceError {
return ServiceError{Code: code, Message: message}
}
// NewErrorWithHeader returns a ServiceError using the code, reason and header
func NewErrorWithHeader(code int, message string, header http.Header) ServiceError {
return ServiceError{Code: code, Message: message, Header: header}
}
// Error returns a text representation of the service error
func (s ServiceError) Error() string {
return fmt.Sprintf("[ServiceError:%v] %v", s.Code, s.Message)

View File

@ -6,7 +6,7 @@ import (
"reflect"
"sync"
"github.com/emicklei/go-restful/log"
"github.com/emicklei/go-restful/v3/log"
)
// Copyright 2013 Ernest Micklei. All rights reserved.
@ -181,14 +181,12 @@ func (w *WebService) RemoveRoute(path, method string) error {
}
w.routesLock.Lock()
defer w.routesLock.Unlock()
newRoutes := make([]Route, (len(w.routes) - 1))
current := 0
for ix := range w.routes {
if w.routes[ix].Method == method && w.routes[ix].Path == path {
newRoutes := []Route{}
for _, route := range w.routes {
if route.Method == method && route.Path == path {
continue
}
newRoutes[current] = w.routes[ix]
current = current + 1
newRoutes = append(newRoutes, route)
}
w.routes = newRoutes
return nil
@ -288,3 +286,8 @@ func (w *WebService) PATCH(subPath string) *RouteBuilder {
func (w *WebService) DELETE(subPath string) *RouteBuilder {
return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("DELETE").Path(subPath)
}
// OPTIONS is a shortcut for .Method("OPTIONS").Path(subPath)
func (w *WebService) OPTIONS(subPath string) *RouteBuilder {
return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("OPTIONS").Path(subPath)
}

View File

@ -1,12 +0,0 @@
language: go
script:
- go vet ./...
- go test -v ./...
go:
- 1.12
- 1.13
- 1.14
- 1.15
- tip

View File

@ -1,97 +0,0 @@
## Migration Guide from v2 -> v3
Version 3 adds several new, frequently requested features. To do so, it introduces a few breaking changes. We've worked to keep these as minimal as possible. This guide explains the breaking changes and how you can quickly update your code.
### `Token.Claims` is now an interface type
The most requested feature from the 2.0 verison of this library was the ability to provide a custom type to the JSON parser for claims. This was implemented by introducing a new interface, `Claims`, to replace `map[string]interface{}`. We also included two concrete implementations of `Claims`: `MapClaims` and `StandardClaims`.
`MapClaims` is an alias for `map[string]interface{}` with built in validation behavior. It is the default claims type when using `Parse`. The usage is unchanged except you must type cast the claims property.
The old example for parsing a token looked like this..
```go
if token, err := jwt.Parse(tokenString, keyLookupFunc); err == nil {
fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"])
}
```
is now directly mapped to...
```go
if token, err := jwt.Parse(tokenString, keyLookupFunc); err == nil {
claims := token.Claims.(jwt.MapClaims)
fmt.Printf("Token for user %v expires %v", claims["user"], claims["exp"])
}
```
`StandardClaims` is designed to be embedded in your custom type. You can supply a custom claims type with the new `ParseWithClaims` function. Here's an example of using a custom claims type.
```go
type MyCustomClaims struct {
User string
*StandardClaims
}
if token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, keyLookupFunc); err == nil {
claims := token.Claims.(*MyCustomClaims)
fmt.Printf("Token for user %v expires %v", claims.User, claims.StandardClaims.ExpiresAt)
}
```
### `ParseFromRequest` has been moved
To keep this library focused on the tokens without becoming overburdened with complex request processing logic, `ParseFromRequest` and its new companion `ParseFromRequestWithClaims` have been moved to a subpackage, `request`. The method signatues have also been augmented to receive a new argument: `Extractor`.
`Extractors` do the work of picking the token string out of a request. The interface is simple and composable.
This simple parsing example:
```go
if token, err := jwt.ParseFromRequest(tokenString, req, keyLookupFunc); err == nil {
fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"])
}
```
is directly mapped to:
```go
if token, err := request.ParseFromRequest(req, request.OAuth2Extractor, keyLookupFunc); err == nil {
claims := token.Claims.(jwt.MapClaims)
fmt.Printf("Token for user %v expires %v", claims["user"], claims["exp"])
}
```
There are several concrete `Extractor` types provided for your convenience:
* `HeaderExtractor` will search a list of headers until one contains content.
* `ArgumentExtractor` will search a list of keys in request query and form arguments until one contains content.
* `MultiExtractor` will try a list of `Extractors` in order until one returns content.
* `AuthorizationHeaderExtractor` will look in the `Authorization` header for a `Bearer` token.
* `OAuth2Extractor` searches the places an OAuth2 token would be specified (per the spec): `Authorization` header and `access_token` argument
* `PostExtractionFilter` wraps an `Extractor`, allowing you to process the content before it's parsed. A simple example is stripping the `Bearer ` text from a header
### RSA signing methods no longer accept `[]byte` keys
Due to a [critical vulnerability](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/), we've decided the convenience of accepting `[]byte` instead of `rsa.PublicKey` or `rsa.PrivateKey` isn't worth the risk of misuse.
To replace this behavior, we've added two helper methods: `ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error)` and `ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error)`. These are just simple helpers for unpacking PEM encoded PKCS1 and PKCS8 keys. If your keys are encoded any other way, all you need to do is convert them to the `crypto/rsa` package's types.
```go
func keyLookupFunc(*Token) (interface{}, error) {
// Don't forget to validate the alg is what you expect:
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
// Look up key
key, err := lookupPublicKey(token.Header["kid"])
if err != nil {
return nil, err
}
// Unpack key from PEM encoded PKCS8
return jwt.ParseRSAPublicKeyFromPEM(key)
}
```

View File

@ -1,136 +0,0 @@
package jwt
import (
"crypto/subtle"
"fmt"
"time"
)
// For a type to be a Claims object, it must just have a Valid method that determines
// if the token is invalid for any supported reason
type Claims interface {
Valid() error
}
// Structured version of Claims Section, as referenced at
// https://tools.ietf.org/html/rfc7519#section-4.1
// See examples for how to use this with your own claim types
type StandardClaims struct {
Audience []string `json:"aud,omitempty"`
ExpiresAt int64 `json:"exp,omitempty"`
Id string `json:"jti,omitempty"`
IssuedAt int64 `json:"iat,omitempty"`
Issuer string `json:"iss,omitempty"`
NotBefore int64 `json:"nbf,omitempty"`
Subject string `json:"sub,omitempty"`
}
// Validates time based claims "exp, iat, nbf".
// There is no accounting for clock skew.
// As well, if any of the above claims are not in the token, it will still
// be considered a valid claim.
func (c StandardClaims) Valid() error {
vErr := new(ValidationError)
now := TimeFunc().Unix()
// The claims below are optional, by default, so if they are set to the
// default value in Go, let's not fail the verification for them.
if c.VerifyExpiresAt(now, false) == false {
delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0))
vErr.Inner = fmt.Errorf("token is expired by %v", delta)
vErr.Errors |= ValidationErrorExpired
}
if c.VerifyIssuedAt(now, false) == false {
vErr.Inner = fmt.Errorf("Token used before issued")
vErr.Errors |= ValidationErrorIssuedAt
}
if c.VerifyNotBefore(now, false) == false {
vErr.Inner = fmt.Errorf("token is not valid yet")
vErr.Errors |= ValidationErrorNotValidYet
}
if vErr.valid() {
return nil
}
return vErr
}
// Compares the aud claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool {
return verifyAud(c.Audience, cmp, req)
}
// Compares the exp claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool {
return verifyExp(c.ExpiresAt, cmp, req)
}
// Compares the iat claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool {
return verifyIat(c.IssuedAt, cmp, req)
}
// Compares the iss claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool {
return verifyIss(c.Issuer, cmp, req)
}
// Compares the nbf claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool {
return verifyNbf(c.NotBefore, cmp, req)
}
// ----- helpers
func verifyAud(aud []string, cmp string, required bool) bool {
if len(aud) == 0 {
return !required
}
for _, a := range aud {
if subtle.ConstantTimeCompare([]byte(a), []byte(cmp)) != 0 {
return true
}
}
return false
}
func verifyExp(exp int64, now int64, required bool) bool {
if exp == 0 {
return !required
}
return now <= exp
}
func verifyIat(iat int64, now int64, required bool) bool {
if iat == 0 {
return !required
}
return now >= iat
}
func verifyIss(iss string, cmp string, required bool) bool {
if iss == "" {
return !required
}
if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 {
return true
} else {
return false
}
}
func verifyNbf(nbf int64, now int64, required bool) bool {
if nbf == 0 {
return !required
}
return now >= nbf
}

View File

@ -1,110 +0,0 @@
package jwt
import (
"encoding/json"
"errors"
// "fmt"
)
// Claims type that uses the map[string]interface{} for JSON decoding
// This is the default claims type if you don't supply one
type MapClaims map[string]interface{}
// Compares the aud claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaims) VerifyAudience(cmp string, req bool) bool {
var aud []string
switch v := m["aud"].(type) {
case []string:
aud = v
case []interface{}:
for _, a := range v {
vs, ok := a.(string)
if !ok {
return false
}
aud = append(aud, vs)
}
case string:
aud = append(aud, v)
default:
return false
}
return verifyAud(aud, cmp, req)
}
// Compares the exp claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool {
switch exp := m["exp"].(type) {
case float64:
return verifyExp(int64(exp), cmp, req)
case json.Number:
v, _ := exp.Int64()
return verifyExp(v, cmp, req)
}
return req == false
}
// Compares the iat claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool {
switch iat := m["iat"].(type) {
case float64:
return verifyIat(int64(iat), cmp, req)
case json.Number:
v, _ := iat.Int64()
return verifyIat(v, cmp, req)
}
return req == false
}
// Compares the iss claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaims) VerifyIssuer(cmp string, req bool) bool {
iss, _ := m["iss"].(string)
return verifyIss(iss, cmp, req)
}
// Compares the nbf claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool {
switch nbf := m["nbf"].(type) {
case float64:
return verifyNbf(int64(nbf), cmp, req)
case json.Number:
v, _ := nbf.Int64()
return verifyNbf(v, cmp, req)
}
return req == false
}
// Validates time based claims "exp, iat, nbf".
// There is no accounting for clock skew.
// As well, if any of the above claims are not in the token, it will still
// be considered a valid claim.
func (m MapClaims) Valid() error {
vErr := new(ValidationError)
now := TimeFunc().Unix()
if m.VerifyExpiresAt(now, false) == false {
vErr.Inner = errors.New("Token is expired")
vErr.Errors |= ValidationErrorExpired
}
if m.VerifyIssuedAt(now, false) == false {
vErr.Inner = errors.New("Token used before issued")
vErr.Errors |= ValidationErrorIssuedAt
}
if m.VerifyNotBefore(now, false) == false {
vErr.Inner = errors.New("Token is not valid yet")
vErr.Errors |= ValidationErrorNotValidYet
}
if vErr.valid() {
return nil
}
return vErr
}

View File

@ -1,108 +0,0 @@
package jwt
import (
"encoding/base64"
"encoding/json"
"strings"
"time"
)
// TimeFunc provides the current time when parsing token to validate "exp" claim (expiration time).
// You can override it to use another time value. This is useful for testing or if your
// server uses a different time zone than your tokens.
var TimeFunc = time.Now
// Parse methods use this callback function to supply
// the key for verification. The function receives the parsed,
// but unverified Token. This allows you to use properties in the
// Header of the token (such as `kid`) to identify which key to use.
type Keyfunc func(*Token) (interface{}, error)
// A JWT Token. Different fields will be used depending on whether you're
// creating or parsing/verifying a token.
type Token struct {
Raw string // The raw token. Populated when you Parse a token
Method SigningMethod // The signing method used or to be used
Header map[string]interface{} // The first segment of the token
Claims Claims // The second segment of the token
Signature string // The third segment of the token. Populated when you Parse a token
Valid bool // Is the token valid? Populated when you Parse/Verify a token
}
// Create a new Token. Takes a signing method
func New(method SigningMethod) *Token {
return NewWithClaims(method, MapClaims{})
}
func NewWithClaims(method SigningMethod, claims Claims) *Token {
return &Token{
Header: map[string]interface{}{
"typ": "JWT",
"alg": method.Alg(),
},
Claims: claims,
Method: method,
}
}
// Get the complete, signed token
func (t *Token) SignedString(key interface{}) (string, error) {
var sig, sstr string
var err error
if sstr, err = t.SigningString(); err != nil {
return "", err
}
if sig, err = t.Method.Sign(sstr, key); err != nil {
return "", err
}
return strings.Join([]string{sstr, sig}, "."), nil
}
// Generate the signing string. This is the
// most expensive part of the whole deal. Unless you
// need this for something special, just go straight for
// the SignedString.
func (t *Token) SigningString() (string, error) {
var err error
parts := make([]string, 2)
for i, _ := range parts {
var jsonValue []byte
if i == 0 {
if jsonValue, err = json.Marshal(t.Header); err != nil {
return "", err
}
} else {
if jsonValue, err = json.Marshal(t.Claims); err != nil {
return "", err
}
}
parts[i] = EncodeSegment(jsonValue)
}
return strings.Join(parts, "."), nil
}
// Parse, validate, and return a token.
// keyFunc will receive the parsed token and should return the key for validating.
// If everything is kosher, err will be nil
func Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
return new(Parser).Parse(tokenString, keyFunc)
}
func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) {
return new(Parser).ParseWithClaims(tokenString, claims, keyFunc)
}
// Encode JWT specific base64url encoding with padding stripped
func EncodeSegment(seg []byte) string {
return strings.TrimRight(base64.URLEncoding.EncodeToString(seg), "=")
}
// Decode JWT specific base64url encoding with padding stripped
func DecodeSegment(seg string) ([]byte, error) {
if l := len(seg) % 4; l > 0 {
seg += strings.Repeat("=", 4-l)
}
return base64.URLEncoding.DecodeString(seg)
}

View File

@ -105,14 +105,18 @@ with higher verbosity means more (and less important) logs will be generated.
There are implementations for the following logging libraries:
- **a function** (can bridge to non-structured libraries): [funcr](https://github.com/go-logr/logr/tree/master/funcr)
- **a testing.T** (for use in Go tests, with JSON-like output): [testr](https://github.com/go-logr/logr/tree/master/testr)
- **github.com/google/glog**: [glogr](https://github.com/go-logr/glogr)
- **k8s.io/klog** (for Kubernetes): [klogr](https://git.k8s.io/klog/klogr)
- **a testing.T** (with klog-like text output): [ktesting](https://git.k8s.io/klog/ktesting)
- **go.uber.org/zap**: [zapr](https://github.com/go-logr/zapr)
- **log** (the Go standard library logger): [stdr](https://github.com/go-logr/stdr)
- **github.com/sirupsen/logrus**: [logrusr](https://github.com/bombsimon/logrusr)
- **github.com/wojas/genericr**: [genericr](https://github.com/wojas/genericr) (makes it easy to implement your own backend)
- **logfmt** (Heroku style [logging](https://www.brandur.org/logfmt)): [logfmtr](https://github.com/iand/logfmtr)
- **github.com/rs/zerolog**: [zerologr](https://github.com/go-logr/zerologr)
- **github.com/go-kit/log**: [gokitlogr](https://github.com/tonglil/gokitlogr) (also compatible with github.com/go-kit/kit/log since v0.12.0)
- **bytes.Buffer** (writing to a buffer): [bufrlogr](https://github.com/tonglil/buflogr) (useful for ensuring values were logged, like during testing)
## FAQ

View File

@ -43,7 +43,9 @@ limitations under the License.
//
// Info() and Error() are very similar, but they are separate methods so that
// LogSink implementations can choose to do things like attach additional
// information (such as stack traces) on calls to Error().
// information (such as stack traces) on calls to Error(). Error() messages are
// always logged, regardless of the current verbosity. If there is no error
// instance available, passing nil is valid.
//
// Verbosity
//
@ -53,6 +55,7 @@ limitations under the License.
// Log-lines with V-levels that are not enabled (as per the LogSink) will not
// be written. Level V(0) is the default, and logger.V(0).Info() has the same
// meaning as logger.Info(). Negative V-levels have the same meaning as V(0).
// Error messages do not have a verbosity level and are always logged.
//
// Where we might have written:
// if flVerbose >= 2 {
@ -112,6 +115,15 @@ limitations under the License.
// may be any Go value, but how the value is formatted is determined by the
// LogSink implementation.
//
// Logger instances are meant to be passed around by value. Code that receives
// such a value can call its methods without having to check whether the
// instance is ready for use.
//
// Calling methods with the null logger (Logger{}) as instance will crash
// because it has no LogSink. Therefore this null logger should never be passed
// around. For cases where passing a logger is optional, a pointer to Logger
// should be used.
//
// Key Naming Conventions
//
// Keys are not strictly required to conform to any specification or regex, but
@ -253,11 +265,13 @@ func (l Logger) Info(msg string, keysAndValues ...interface{}) {
// Error logs an error, with the given message and key/value pairs as context.
// It functions similarly to Info, but may have unique behavior, and should be
// preferred for logging errors (see the package documentations for more
// information).
// information). The log message will always be emitted, regardless of
// verbosity level.
//
// The msg argument should be used to add context to any underlying error,
// while the err argument should be used to attach the actual error that
// triggered this log line, if present.
// triggered this log line, if present. The err parameter is optional
// and nil may be passed instead of an error instance.
func (l Logger) Error(err error, msg string, keysAndValues ...interface{}) {
if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
withHelper.GetCallStackHelper()()

View File

@ -1,4 +1,5 @@
Copyright (c) 2012 Dave Grijalva
Copyright (c) 2021 golang-jwt maintainers
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

22
vendor/github.com/golang-jwt/jwt/v4/MIGRATION_GUIDE.md generated vendored Normal file
View File

@ -0,0 +1,22 @@
## Migration Guide (v4.0.0)
Starting from [v4.0.0](https://github.com/golang-jwt/jwt/releases/tag/v4.0.0), the import path will be:
"github.com/golang-jwt/jwt/v4"
The `/v4` version will be backwards compatible with existing `v3.x.y` tags in this repo, as well as
`github.com/dgrijalva/jwt-go`. For most users this should be a drop-in replacement, if you're having
troubles migrating, please open an issue.
You can replace all occurrences of `github.com/dgrijalva/jwt-go` or `github.com/golang-jwt/jwt` with `github.com/golang-jwt/jwt/v4`, either manually or by using tools such as `sed` or `gofmt`.
And then you'd typically run:
```
go get github.com/golang-jwt/jwt/v4
go mod tidy
```
## Older releases (before v3.2.0)
The original migration guide for older releases can be found at https://github.com/dgrijalva/jwt-go/blob/master/MIGRATION_GUIDE.md.

View File

@ -1,25 +1,36 @@
# jwt-go
[![Build Status](https://travis-ci.org/dgrijalva/jwt-go.svg?branch=master)](https://travis-ci.org/dgrijalva/jwt-go)
[![GoDoc](https://godoc.org/github.com/dgrijalva/jwt-go?status.svg)](https://godoc.org/github.com/dgrijalva/jwt-go)
[![build](https://github.com/golang-jwt/jwt/actions/workflows/build.yml/badge.svg)](https://github.com/golang-jwt/jwt/actions/workflows/build.yml)
[![Go Reference](https://pkg.go.dev/badge/github.com/golang-jwt/jwt/v4.svg)](https://pkg.go.dev/github.com/golang-jwt/jwt/v4)
A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html)
A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](https://datatracker.ietf.org/doc/html/rfc7519).
**NEW VERSION COMING:** There have been a lot of improvements suggested since the version 3.0.0 released in 2016. I'm working now on cutting two different releases: 3.2.0 will contain any non-breaking changes or enhancements. 4.0.0 will follow shortly which will include breaking changes. See the 4.0.0 milestone to get an idea of what's coming. If you have other ideas, or would like to participate in 4.0.0, now's the time. If you depend on this library and don't want to be interrupted, I recommend you use your dependency mangement tool to pin to version 3.
Starting with [v4.0.0](https://github.com/golang-jwt/jwt/releases/tag/v4.0.0) this project adds Go module support, but maintains backwards compatibility with older `v3.x.y` tags and upstream `github.com/dgrijalva/jwt-go`.
See the [`MIGRATION_GUIDE.md`](./MIGRATION_GUIDE.md) for more information.
**SECURITY NOTICE:** Some older versions of Go have a security issue in the cryotp/elliptic. Recommendation is to upgrade to at least 1.8.3. See issue #216 for more detail.
> After the original author of the library suggested migrating the maintenance of `jwt-go`, a dedicated team of open source maintainers decided to clone the existing library into this repository. See [dgrijalva/jwt-go#462](https://github.com/dgrijalva/jwt-go/issues/462) for a detailed discussion on this topic.
**SECURITY NOTICE:** Some older versions of Go have a security issue in the crypto/elliptic. Recommendation is to upgrade to at least 1.15 See issue [dgrijalva/jwt-go#216](https://github.com/dgrijalva/jwt-go/issues/216) for more detail.
**SECURITY NOTICE:** It's important that you [validate the `alg` presented is what you expect](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/). This library attempts to make it easy to do the right thing by requiring key types match the expected alg, but you should take the extra step to verify it in your usage. See the examples provided.
### Supported Go versions
Our support of Go versions is aligned with Go's [version release policy](https://golang.org/doc/devel/release#policy).
So we will support a major version of Go until there are two newer major releases.
We no longer support building jwt-go with unsupported Go versions, as these contain security vulnerabilities
which will not be fixed.
## What the heck is a JWT?
JWT.io has [a great introduction](https://jwt.io/introduction) to JSON Web Tokens.
In short, it's a signed JSON object that does something useful (for example, authentication). It's commonly used for `Bearer` tokens in Oauth 2. A token is made of three parts, separated by `.`'s. The first two parts are JSON objects, that have been [base64url](http://tools.ietf.org/html/rfc4648) encoded. The last part is the signature, encoded the same way.
In short, it's a signed JSON object that does something useful (for example, authentication). It's commonly used for `Bearer` tokens in Oauth 2. A token is made of three parts, separated by `.`'s. The first two parts are JSON objects, that have been [base64url](https://datatracker.ietf.org/doc/html/rfc4648) encoded. The last part is the signature, encoded the same way.
The first part is called the header. It contains the necessary information for verifying the last part, the signature. For example, which encryption method was used for signing and what key was used.
The part in the middle is the interesting bit. It's called the Claims and contains the actual stuff you care about. Refer to [the RFC](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html) for information about reserved keys and the proper way to add your own.
The part in the middle is the interesting bit. It's called the Claims and contains the actual stuff you care about. Refer to [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519) for information about reserved keys and the proper way to add your own.
## What's in the box?
@ -27,11 +38,11 @@ This library supports the parsing and verification as well as the generation and
## Examples
See [the project documentation](https://godoc.org/github.com/dgrijalva/jwt-go) for examples of usage:
See [the project documentation](https://pkg.go.dev/github.com/golang-jwt/jwt) for examples of usage:
* [Simple example of parsing and validating a token](https://godoc.org/github.com/dgrijalva/jwt-go#example-Parse--Hmac)
* [Simple example of building and signing a token](https://godoc.org/github.com/dgrijalva/jwt-go#example-New--Hmac)
* [Directory of Examples](https://godoc.org/github.com/dgrijalva/jwt-go#pkg-examples)
* [Simple example of parsing and validating a token](https://pkg.go.dev/github.com/golang-jwt/jwt#example-Parse-Hmac)
* [Simple example of building and signing a token](https://pkg.go.dev/github.com/golang-jwt/jwt#example-New-Hmac)
* [Directory of Examples](https://pkg.go.dev/github.com/golang-jwt/jwt#pkg-examples)
## Extensions
@ -41,20 +52,18 @@ Here's an example of an extension that integrates with multiple Google Cloud Pla
## Compliance
This library was last reviewed to comply with [RTF 7519](http://www.rfc-editor.org/info/rfc7519) dated May 2015 with a few notable differences:
This library was last reviewed to comply with [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519) dated May 2015 with a few notable differences:
* In order to protect against accidental use of [Unsecured JWTs](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#UnsecuredJWT), tokens using `alg=none` will only be accepted if the constant `jwt.UnsafeAllowNoneSignatureType` is provided as the key.
* In order to protect against accidental use of [Unsecured JWTs](https://datatracker.ietf.org/doc/html/rfc7519#section-6), tokens using `alg=none` will only be accepted if the constant `jwt.UnsafeAllowNoneSignatureType` is provided as the key.
## Project Status & Versioning
This library is considered production ready. Feedback and feature requests are appreciated. The API should be considered stable. There should be very few backwards-incompatible changes outside of major version updates (and only with good reason).
This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull requests will land on `master`. Periodically, versions will be tagged from `master`. You can find all the releases on [the project releases page](https://github.com/dgrijalva/jwt-go/releases).
While we try to make it obvious when we make breaking changes, there isn't a great mechanism for pushing announcements out to users. You may want to use this alternative package include: `gopkg.in/dgrijalva/jwt-go.v3`. It will do the right thing WRT semantic versioning.
This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull requests will land on `main`. Periodically, versions will be tagged from `main`. You can find all the releases on [the project releases page](https://github.com/golang-jwt/jwt/releases).
**BREAKING CHANGES:***
* Version 3.0.0 includes _a lot_ of changes from the 2.x line, including a few that break the API. We've tried to break as few things as possible, so there should just be a few type signature changes. A full list of breaking changes is available in `VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating your code.
A full list of breaking changes is available in `VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating your code.
## Usage Tips
@ -79,9 +88,10 @@ Asymmetric signing methods, such as RSA, use different keys for signing and veri
Each signing method expects a different object type for its signing keys. See the package documentation for details. Here are the most common ones:
* The [HMAC signing method](https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodHMAC) (`HS256`,`HS384`,`HS512`) expect `[]byte` values for signing and validation
* The [RSA signing method](https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodRSA) (`RS256`,`RS384`,`RS512`) expect `*rsa.PrivateKey` for signing and `*rsa.PublicKey` for validation
* The [ECDSA signing method](https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodECDSA) (`ES256`,`ES384`,`ES512`) expect `*ecdsa.PrivateKey` for signing and `*ecdsa.PublicKey` for validation
* The [HMAC signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodHMAC) (`HS256`,`HS384`,`HS512`) expect `[]byte` values for signing and validation
* The [RSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodRSA) (`RS256`,`RS384`,`RS512`) expect `*rsa.PrivateKey` for signing and `*rsa.PublicKey` for validation
* The [ECDSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodECDSA) (`ES256`,`ES384`,`ES512`) expect `*ecdsa.PrivateKey` for signing and `*ecdsa.PublicKey` for validation
* The [EdDSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodEd25519) (`Ed25519`) expect `ed25519.PrivateKey` for signing and `ed25519.PublicKey` for validation
### JWT and OAuth
@ -99,6 +109,6 @@ This library uses descriptive error messages whenever possible. If you are not g
## More
Documentation can be found [on godoc.org](http://godoc.org/github.com/dgrijalva/jwt-go).
Documentation can be found [on pkg.go.dev](https://pkg.go.dev/github.com/golang-jwt/jwt).
The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration. You'll also find several implementation examples in the documentation.

View File

@ -1,5 +1,22 @@
## `jwt-go` Version History
#### 4.0.0
* Introduces support for Go modules. The `v4` version will be backwards compatible with `v3.x.y`.
#### 3.2.2
* Starting from this release, we are adopting the policy to support the most 2 recent versions of Go currently available. By the time of this release, this is Go 1.15 and 1.16 ([#28](https://github.com/golang-jwt/jwt/pull/28)).
* Fixed a potential issue that could occur when the verification of `exp`, `iat` or `nbf` was not required and contained invalid contents, i.e. non-numeric/date. Thanks for @thaJeztah for making us aware of that and @giorgos-f3 for originally reporting it to the formtech fork ([#40](https://github.com/golang-jwt/jwt/pull/40)).
* Added support for EdDSA / ED25519 ([#36](https://github.com/golang-jwt/jwt/pull/36)).
* Optimized allocations ([#33](https://github.com/golang-jwt/jwt/pull/33)).
#### 3.2.1
* **Import Path Change**: See MIGRATION_GUIDE.md for tips on updating your code
* Changed the import path from `github.com/dgrijalva/jwt-go` to `github.com/golang-jwt/jwt`
* Fixed type confusing issue between `string` and `[]string` in `VerifyAudience` ([#12](https://github.com/golang-jwt/jwt/pull/12)). This fixes CVE-2020-26160
#### 3.2.0
* Added method `ParseUnverified` to allow users to split up the tasks of parsing and validation

273
vendor/github.com/golang-jwt/jwt/v4/claims.go generated vendored Normal file
View File

@ -0,0 +1,273 @@
package jwt
import (
"crypto/subtle"
"fmt"
"time"
)
// Claims must just have a Valid method that determines
// if the token is invalid for any supported reason
type Claims interface {
Valid() error
}
// RegisteredClaims are a structured version of the JWT Claims Set,
// restricted to Registered Claim Names, as referenced at
// https://datatracker.ietf.org/doc/html/rfc7519#section-4.1
//
// This type can be used on its own, but then additional private and
// public claims embedded in the JWT will not be parsed. The typical usecase
// therefore is to embedded this in a user-defined claim type.
//
// See examples for how to use this with your own claim types.
type RegisteredClaims struct {
// the `iss` (Issuer) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1
Issuer string `json:"iss,omitempty"`
// the `sub` (Subject) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2
Subject string `json:"sub,omitempty"`
// the `aud` (Audience) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3
Audience ClaimStrings `json:"aud,omitempty"`
// the `exp` (Expiration Time) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4
ExpiresAt *NumericDate `json:"exp,omitempty"`
// the `nbf` (Not Before) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5
NotBefore *NumericDate `json:"nbf,omitempty"`
// the `iat` (Issued At) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6
IssuedAt *NumericDate `json:"iat,omitempty"`
// the `jti` (JWT ID) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.7
ID string `json:"jti,omitempty"`
}
// Valid validates time based claims "exp, iat, nbf".
// There is no accounting for clock skew.
// As well, if any of the above claims are not in the token, it will still
// be considered a valid claim.
func (c RegisteredClaims) Valid() error {
vErr := new(ValidationError)
now := TimeFunc()
// The claims below are optional, by default, so if they are set to the
// default value in Go, let's not fail the verification for them.
if !c.VerifyExpiresAt(now, false) {
delta := now.Sub(c.ExpiresAt.Time)
vErr.Inner = fmt.Errorf("token is expired by %v", delta)
vErr.Errors |= ValidationErrorExpired
}
if !c.VerifyIssuedAt(now, false) {
vErr.Inner = fmt.Errorf("token used before issued")
vErr.Errors |= ValidationErrorIssuedAt
}
if !c.VerifyNotBefore(now, false) {
vErr.Inner = fmt.Errorf("token is not valid yet")
vErr.Errors |= ValidationErrorNotValidYet
}
if vErr.valid() {
return nil
}
return vErr
}
// VerifyAudience compares the aud claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *RegisteredClaims) VerifyAudience(cmp string, req bool) bool {
return verifyAud(c.Audience, cmp, req)
}
// VerifyExpiresAt compares the exp claim against cmp (cmp < exp).
// If req is false, it will return true, if exp is unset.
func (c *RegisteredClaims) VerifyExpiresAt(cmp time.Time, req bool) bool {
if c.ExpiresAt == nil {
return verifyExp(nil, cmp, req)
}
return verifyExp(&c.ExpiresAt.Time, cmp, req)
}
// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
// If req is false, it will return true, if iat is unset.
func (c *RegisteredClaims) VerifyIssuedAt(cmp time.Time, req bool) bool {
if c.IssuedAt == nil {
return verifyIat(nil, cmp, req)
}
return verifyIat(&c.IssuedAt.Time, cmp, req)
}
// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
// If req is false, it will return true, if nbf is unset.
func (c *RegisteredClaims) VerifyNotBefore(cmp time.Time, req bool) bool {
if c.NotBefore == nil {
return verifyNbf(nil, cmp, req)
}
return verifyNbf(&c.NotBefore.Time, cmp, req)
}
// VerifyIssuer compares the iss claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *RegisteredClaims) VerifyIssuer(cmp string, req bool) bool {
return verifyIss(c.Issuer, cmp, req)
}
// StandardClaims are a structured version of the JWT Claims Set, as referenced at
// https://datatracker.ietf.org/doc/html/rfc7519#section-4. They do not follow the
// specification exactly, since they were based on an earlier draft of the
// specification and not updated. The main difference is that they only
// support integer-based date fields and singular audiences. This might lead to
// incompatibilities with other JWT implementations. The use of this is discouraged, instead
// the newer RegisteredClaims struct should be used.
//
// Deprecated: Use RegisteredClaims instead for a forward-compatible way to access registered claims in a struct.
type StandardClaims struct {
Audience string `json:"aud,omitempty"`
ExpiresAt int64 `json:"exp,omitempty"`
Id string `json:"jti,omitempty"`
IssuedAt int64 `json:"iat,omitempty"`
Issuer string `json:"iss,omitempty"`
NotBefore int64 `json:"nbf,omitempty"`
Subject string `json:"sub,omitempty"`
}
// Valid validates time based claims "exp, iat, nbf". There is no accounting for clock skew.
// As well, if any of the above claims are not in the token, it will still
// be considered a valid claim.
func (c StandardClaims) Valid() error {
vErr := new(ValidationError)
now := TimeFunc().Unix()
// The claims below are optional, by default, so if they are set to the
// default value in Go, let's not fail the verification for them.
if !c.VerifyExpiresAt(now, false) {
delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0))
vErr.Inner = fmt.Errorf("token is expired by %v", delta)
vErr.Errors |= ValidationErrorExpired
}
if !c.VerifyIssuedAt(now, false) {
vErr.Inner = fmt.Errorf("token used before issued")
vErr.Errors |= ValidationErrorIssuedAt
}
if !c.VerifyNotBefore(now, false) {
vErr.Inner = fmt.Errorf("token is not valid yet")
vErr.Errors |= ValidationErrorNotValidYet
}
if vErr.valid() {
return nil
}
return vErr
}
// VerifyAudience compares the aud claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool {
return verifyAud([]string{c.Audience}, cmp, req)
}
// VerifyExpiresAt compares the exp claim against cmp (cmp < exp).
// If req is false, it will return true, if exp is unset.
func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool {
if c.ExpiresAt == 0 {
return verifyExp(nil, time.Unix(cmp, 0), req)
}
t := time.Unix(c.ExpiresAt, 0)
return verifyExp(&t, time.Unix(cmp, 0), req)
}
// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
// If req is false, it will return true, if iat is unset.
func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool {
if c.IssuedAt == 0 {
return verifyIat(nil, time.Unix(cmp, 0), req)
}
t := time.Unix(c.IssuedAt, 0)
return verifyIat(&t, time.Unix(cmp, 0), req)
}
// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
// If req is false, it will return true, if nbf is unset.
func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool {
if c.NotBefore == 0 {
return verifyNbf(nil, time.Unix(cmp, 0), req)
}
t := time.Unix(c.NotBefore, 0)
return verifyNbf(&t, time.Unix(cmp, 0), req)
}
// VerifyIssuer compares the iss claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool {
return verifyIss(c.Issuer, cmp, req)
}
// ----- helpers
func verifyAud(aud []string, cmp string, required bool) bool {
if len(aud) == 0 {
return !required
}
// use a var here to keep constant time compare when looping over a number of claims
result := false
var stringClaims string
for _, a := range aud {
if subtle.ConstantTimeCompare([]byte(a), []byte(cmp)) != 0 {
result = true
}
stringClaims = stringClaims + a
}
// case where "" is sent in one or many aud claims
if len(stringClaims) == 0 {
return !required
}
return result
}
func verifyExp(exp *time.Time, now time.Time, required bool) bool {
if exp == nil {
return !required
}
return now.Before(*exp)
}
func verifyIat(iat *time.Time, now time.Time, required bool) bool {
if iat == nil {
return !required
}
return now.After(*iat) || now.Equal(*iat)
}
func verifyNbf(nbf *time.Time, now time.Time, required bool) bool {
if nbf == nil {
return !required
}
return now.After(*nbf) || now.Equal(*nbf)
}
func verifyIss(iss string, cmp string, required bool) bool {
if iss == "" {
return !required
}
if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 {
return true
} else {
return false
}
}

View File

@ -13,7 +13,7 @@ var (
ErrECDSAVerification = errors.New("crypto/ecdsa: verification error")
)
// Implements the ECDSA family of signing methods signing methods
// SigningMethodECDSA implements the ECDSA family of signing methods.
// Expects *ecdsa.PrivateKey for signing and *ecdsa.PublicKey for verification
type SigningMethodECDSA struct {
Name string
@ -53,7 +53,7 @@ func (m *SigningMethodECDSA) Alg() string {
return m.Name
}
// Implements the Verify method from SigningMethod
// Verify implements token verification for the SigningMethod.
// For this verify method, key must be an ecdsa.PublicKey struct
func (m *SigningMethodECDSA) Verify(signingString, signature string, key interface{}) error {
var err error
@ -88,14 +88,14 @@ func (m *SigningMethodECDSA) Verify(signingString, signature string, key interfa
hasher.Write([]byte(signingString))
// Verify the signature
if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus == true {
if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus {
return nil
} else {
return ErrECDSAVerification
}
}
// Implements the Sign method from SigningMethod
return ErrECDSAVerification
}
// Sign implements token signing for the SigningMethod.
// For this signing method, key must be an ecdsa.PrivateKey struct
func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string, error) {
// Get the key
@ -128,18 +128,12 @@ func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string
keyBytes += 1
}
// We serialize the outpus (r and s) into big-endian byte arrays and pad
// them with zeros on the left to make sure the sizes work out. Both arrays
// must be keyBytes long, and the output must be 2*keyBytes long.
rBytes := r.Bytes()
rBytesPadded := make([]byte, keyBytes)
copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
sBytes := s.Bytes()
sBytesPadded := make([]byte, keyBytes)
copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
out := append(rBytesPadded, sBytesPadded...)
// We serialize the outputs (r and s) into big-endian byte arrays
// padded with zeros on the left to make sure the sizes work out.
// Output must be 2*keyBytes long.
out := make([]byte, 2*keyBytes)
r.FillBytes(out[0:keyBytes]) // r is assigned to the first half of output.
s.FillBytes(out[keyBytes:]) // s is assigned to the second half of output.
return EncodeSegment(out), nil
} else {

View File

@ -8,11 +8,11 @@ import (
)
var (
ErrNotECPublicKey = errors.New("Key is not a valid ECDSA public key")
ErrNotECPrivateKey = errors.New("Key is not a valid ECDSA private key")
ErrNotECPublicKey = errors.New("key is not a valid ECDSA public key")
ErrNotECPrivateKey = errors.New("key is not a valid ECDSA private key")
)
// Parse PEM encoded Elliptic Curve Private Key Structure
// ParseECPrivateKeyFromPEM parses a PEM encoded Elliptic Curve Private Key Structure
func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) {
var err error
@ -39,7 +39,7 @@ func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) {
return pkey, nil
}
// Parse PEM encoded PKCS1 or PKCS8 public key
// ParseECPublicKeyFromPEM parses a PEM encoded PKCS1 or PKCS8 public key
func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) {
var err error

85
vendor/github.com/golang-jwt/jwt/v4/ed25519.go generated vendored Normal file
View File

@ -0,0 +1,85 @@
package jwt
import (
"errors"
"crypto"
"crypto/ed25519"
"crypto/rand"
)
var (
ErrEd25519Verification = errors.New("ed25519: verification error")
)
// SigningMethodEd25519 implements the EdDSA family.
// Expects ed25519.PrivateKey for signing and ed25519.PublicKey for verification
type SigningMethodEd25519 struct{}
// Specific instance for EdDSA
var (
SigningMethodEdDSA *SigningMethodEd25519
)
func init() {
SigningMethodEdDSA = &SigningMethodEd25519{}
RegisterSigningMethod(SigningMethodEdDSA.Alg(), func() SigningMethod {
return SigningMethodEdDSA
})
}
func (m *SigningMethodEd25519) Alg() string {
return "EdDSA"
}
// Verify implements token verification for the SigningMethod.
// For this verify method, key must be an ed25519.PublicKey
func (m *SigningMethodEd25519) Verify(signingString, signature string, key interface{}) error {
var err error
var ed25519Key ed25519.PublicKey
var ok bool
if ed25519Key, ok = key.(ed25519.PublicKey); !ok {
return ErrInvalidKeyType
}
if len(ed25519Key) != ed25519.PublicKeySize {
return ErrInvalidKey
}
// Decode the signature
var sig []byte
if sig, err = DecodeSegment(signature); err != nil {
return err
}
// Verify the signature
if !ed25519.Verify(ed25519Key, []byte(signingString), sig) {
return ErrEd25519Verification
}
return nil
}
// Sign implements token signing for the SigningMethod.
// For this signing method, key must be an ed25519.PrivateKey
func (m *SigningMethodEd25519) Sign(signingString string, key interface{}) (string, error) {
var ed25519Key crypto.Signer
var ok bool
if ed25519Key, ok = key.(crypto.Signer); !ok {
return "", ErrInvalidKeyType
}
if _, ok := ed25519Key.Public().(ed25519.PublicKey); !ok {
return "", ErrInvalidKey
}
// Sign the string and return the encoded result
// ed25519 performs a two-pass hash as part of its algorithm. Therefore, we need to pass a non-prehashed message into the Sign function, as indicated by crypto.Hash(0)
sig, err := ed25519Key.Sign(rand.Reader, []byte(signingString), crypto.Hash(0))
if err != nil {
return "", err
}
return EncodeSegment(sig), nil
}

64
vendor/github.com/golang-jwt/jwt/v4/ed25519_utils.go generated vendored Normal file
View File

@ -0,0 +1,64 @@
package jwt
import (
"crypto"
"crypto/ed25519"
"crypto/x509"
"encoding/pem"
"errors"
)
var (
ErrNotEdPrivateKey = errors.New("key is not a valid Ed25519 private key")
ErrNotEdPublicKey = errors.New("key is not a valid Ed25519 public key")
)
// ParseEdPrivateKeyFromPEM parses a PEM-encoded Edwards curve private key
func ParseEdPrivateKeyFromPEM(key []byte) (crypto.PrivateKey, error) {
var err error
// Parse PEM block
var block *pem.Block
if block, _ = pem.Decode(key); block == nil {
return nil, ErrKeyMustBePEMEncoded
}
// Parse the key
var parsedKey interface{}
if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil {
return nil, err
}
var pkey ed25519.PrivateKey
var ok bool
if pkey, ok = parsedKey.(ed25519.PrivateKey); !ok {
return nil, ErrNotEdPrivateKey
}
return pkey, nil
}
// ParseEdPublicKeyFromPEM parses a PEM-encoded Edwards curve public key
func ParseEdPublicKeyFromPEM(key []byte) (crypto.PublicKey, error) {
var err error
// Parse PEM block
var block *pem.Block
if block, _ = pem.Decode(key); block == nil {
return nil, ErrKeyMustBePEMEncoded
}
// Parse the key
var parsedKey interface{}
if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
return nil, err
}
var pkey ed25519.PublicKey
var ok bool
if pkey, ok = parsedKey.(ed25519.PublicKey); !ok {
return nil, ErrNotEdPublicKey
}
return pkey, nil
}

View File

@ -27,7 +27,7 @@ const (
ValidationErrorClaimsInvalid // Generic claims validation error
)
// Helper for constructing a ValidationError with a string error message
// NewValidationError is a helper for constructing a ValidationError with a string error message
func NewValidationError(errorText string, errorFlags uint32) *ValidationError {
return &ValidationError{
text: errorText,
@ -35,14 +35,14 @@ func NewValidationError(errorText string, errorFlags uint32) *ValidationError {
}
}
// The error from Parse if token is not valid
// ValidationError represents an error from Parse if token is not valid
type ValidationError struct {
Inner error // stores the error returned by external dependencies, i.e.: KeyFunc
Errors uint32 // bitfield. see ValidationError... constants
text string // errors that do not have a valid error just have text
}
// Validation error is an error type
// Error is the implementation of the err interface.
func (e ValidationError) Error() string {
if e.Inner != nil {
return e.Inner.Error()
@ -53,6 +53,11 @@ func (e ValidationError) Error() string {
}
}
// Unwrap gives errors.Is and errors.As access to the inner error.
func (e *ValidationError) Unwrap() error {
return e.Inner
}
// No errors
func (e *ValidationError) valid() bool {
return e.Errors == 0

View File

@ -6,7 +6,7 @@ import (
"errors"
)
// Implements the HMAC-SHA family of signing methods signing methods
// SigningMethodHMAC implements the HMAC-SHA family of signing methods.
// Expects key type of []byte for both signing and validation
type SigningMethodHMAC struct {
Name string
@ -45,7 +45,7 @@ func (m *SigningMethodHMAC) Alg() string {
return m.Name
}
// Verify the signature of HSXXX tokens. Returns nil if the signature is valid.
// Verify implements token verification for the SigningMethod. Returns nil if the signature is valid.
func (m *SigningMethodHMAC) Verify(signingString, signature string, key interface{}) error {
// Verify the key is the right type
keyBytes, ok := key.([]byte)
@ -77,7 +77,7 @@ func (m *SigningMethodHMAC) Verify(signingString, signature string, key interfac
return nil
}
// Implements the Sign method from SigningMethod for this signing method.
// Sign implements token signing for the SigningMethod.
// Key must be []byte
func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) (string, error) {
if keyBytes, ok := key.([]byte); ok {

148
vendor/github.com/golang-jwt/jwt/v4/map_claims.go generated vendored Normal file
View File

@ -0,0 +1,148 @@
package jwt
import (
"encoding/json"
"errors"
"time"
// "fmt"
)
// MapClaims is a claims type that uses the map[string]interface{} for JSON decoding.
// This is the default claims type if you don't supply one
type MapClaims map[string]interface{}
// VerifyAudience Compares the aud claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaims) VerifyAudience(cmp string, req bool) bool {
var aud []string
switch v := m["aud"].(type) {
case string:
aud = append(aud, v)
case []string:
aud = v
case []interface{}:
for _, a := range v {
vs, ok := a.(string)
if !ok {
return false
}
aud = append(aud, vs)
}
}
return verifyAud(aud, cmp, req)
}
// VerifyExpiresAt compares the exp claim against cmp (cmp <= exp).
// If req is false, it will return true, if exp is unset.
func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool {
cmpTime := time.Unix(cmp, 0)
v, ok := m["exp"]
if !ok {
return !req
}
switch exp := v.(type) {
case float64:
if exp == 0 {
return verifyExp(nil, cmpTime, req)
}
return verifyExp(&newNumericDateFromSeconds(exp).Time, cmpTime, req)
case json.Number:
v, _ := exp.Float64()
return verifyExp(&newNumericDateFromSeconds(v).Time, cmpTime, req)
}
return false
}
// VerifyIssuedAt compares the exp claim against cmp (cmp >= iat).
// If req is false, it will return true, if iat is unset.
func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool {
cmpTime := time.Unix(cmp, 0)
v, ok := m["iat"]
if !ok {
return !req
}
switch iat := v.(type) {
case float64:
if iat == 0 {
return verifyIat(nil, cmpTime, req)
}
return verifyIat(&newNumericDateFromSeconds(iat).Time, cmpTime, req)
case json.Number:
v, _ := iat.Float64()
return verifyIat(&newNumericDateFromSeconds(v).Time, cmpTime, req)
}
return false
}
// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
// If req is false, it will return true, if nbf is unset.
func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool {
cmpTime := time.Unix(cmp, 0)
v, ok := m["nbf"]
if !ok {
return !req
}
switch nbf := v.(type) {
case float64:
if nbf == 0 {
return verifyNbf(nil, cmpTime, req)
}
return verifyNbf(&newNumericDateFromSeconds(nbf).Time, cmpTime, req)
case json.Number:
v, _ := nbf.Float64()
return verifyNbf(&newNumericDateFromSeconds(v).Time, cmpTime, req)
}
return false
}
// VerifyIssuer compares the iss claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaims) VerifyIssuer(cmp string, req bool) bool {
iss, _ := m["iss"].(string)
return verifyIss(iss, cmp, req)
}
// Valid validates time based claims "exp, iat, nbf".
// There is no accounting for clock skew.
// As well, if any of the above claims are not in the token, it will still
// be considered a valid claim.
func (m MapClaims) Valid() error {
vErr := new(ValidationError)
now := TimeFunc().Unix()
if !m.VerifyExpiresAt(now, false) {
vErr.Inner = errors.New("Token is expired")
vErr.Errors |= ValidationErrorExpired
}
if !m.VerifyIssuedAt(now, false) {
vErr.Inner = errors.New("Token used before issued")
vErr.Errors |= ValidationErrorIssuedAt
}
if !m.VerifyNotBefore(now, false) {
vErr.Inner = errors.New("Token is not valid yet")
vErr.Errors |= ValidationErrorNotValidYet
}
if vErr.valid() {
return nil
}
return vErr
}

View File

@ -1,6 +1,6 @@
package jwt
// Implements the none signing method. This is required by the spec
// SigningMethodNone implements the none signing method. This is required by the spec
// but you probably should never use it.
var SigningMethodNone *signingMethodNone

View File

@ -8,14 +8,36 @@ import (
)
type Parser struct {
ValidMethods []string // If populated, only these methods will be considered valid
UseJSONNumber bool // Use JSON Number format in JSON decoder
SkipClaimsValidation bool // Skip claims validation during token parsing
// If populated, only these methods will be considered valid.
//
// Deprecated: In future releases, this field will not be exported anymore and should be set with an option to NewParser instead.
ValidMethods []string
// Use JSON Number format in JSON decoder.
//
// Deprecated: In future releases, this field will not be exported anymore and should be set with an option to NewParser instead.
UseJSONNumber bool
// Skip claims validation during token parsing.
//
// Deprecated: In future releases, this field will not be exported anymore and should be set with an option to NewParser instead.
SkipClaimsValidation bool
}
// Parse, validate, and return a token.
// NewParser creates a new Parser with the specified options
func NewParser(options ...ParserOption) *Parser {
p := &Parser{}
// loop through our parsing options and apply them
for _, option := range options {
option(p)
}
return p
}
// Parse parses, validates, verifies the signature and returns the parsed token.
// keyFunc will receive the parsed token and should return the key for validating.
// If everything is kosher, err will be nil
func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc)
}
@ -87,12 +109,12 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf
return token, vErr
}
// WARNING: Don't use this method unless you know what you're doing
// ParseUnverified parses the token but doesn't validate the signature.
//
// This method parses the token but doesn't validate the signature. It's only
// ever useful in cases where you know the signature is valid (because it has
// been checked previously in the stack) and you want to extract values from
// it.
// WARNING: Don't use this method unless you know what you're doing.
//
// It's only ever useful in cases where you know the signature is valid (because it has
// been checked previously in the stack) and you want to extract values from it.
func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error) {
parts = strings.Split(tokenString, ".")
if len(parts) != 3 {

29
vendor/github.com/golang-jwt/jwt/v4/parser_option.go generated vendored Normal file
View File

@ -0,0 +1,29 @@
package jwt
// ParserOption is used to implement functional-style options that modify the behaviour of the parser. To add
// new options, just create a function (ideally beginning with With or Without) that returns an anonymous function that
// takes a *Parser type as input and manipulates its configuration accordingly.
type ParserOption func(*Parser)
// WithValidMethods is an option to supply algorithm methods that the parser will check. Only those methods will be considered valid.
// It is heavily encouraged to use this option in order to prevent attacks such as https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/.
func WithValidMethods(methods []string) ParserOption {
return func(p *Parser) {
p.ValidMethods = methods
}
}
// WithJSONNumber is an option to configure the underyling JSON parser with UseNumber
func WithJSONNumber() ParserOption {
return func(p *Parser) {
p.UseJSONNumber = true
}
}
// WithoutClaimsValidation is an option to disable claims validation. This option should only be used if you exactly know
// what you are doing.
func WithoutClaimsValidation() ParserOption {
return func(p *Parser) {
p.SkipClaimsValidation = true
}
}

Some files were not shown because too many files have changed in this diff Show More