Commit 29b4bd8a authored by Brad Davidson's avatar Brad Davidson Committed by Brad Davidson

Update etcd to v3.4.18-k3s1

parent 267858c0
...@@ -31,7 +31,7 @@ replace ( ...@@ -31,7 +31,7 @@ replace (
github.com/opencontainers/runtime-spec => github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6 github.com/opencontainers/runtime-spec => github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6
github.com/rancher/k3s/pkg/data => ./pkg/data github.com/rancher/k3s/pkg/data => ./pkg/data
github.com/rancher/wrangler => github.com/rancher/wrangler v0.6.2 github.com/rancher/wrangler => github.com/rancher/wrangler v0.6.2
go.etcd.io/etcd => github.com/k3s-io/etcd v0.5.0-alpha.5.0.20201208200253-50621aee4aea go.etcd.io/etcd => github.com/k3s-io/etcd v0.5.0-alpha.5.0.20220113195313-6c2233a709e8 // k3s-release-3.4
golang.org/x/crypto => golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 golang.org/x/crypto => golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
golang.org/x/net => golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 golang.org/x/net => golang.org/x/net v0.0.0-20210224082022-3d97a244fca7
golang.org/x/sys => golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 golang.org/x/sys => golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073
......
...@@ -196,7 +196,7 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc ...@@ -196,7 +196,7 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
...@@ -209,7 +209,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs ...@@ -209,7 +209,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd h1:uVsMphB1eRx7xB1njzL3fuMdWRN8HtVzoUOItHMwv5c= github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd h1:uVsMphB1eRx7xB1njzL3fuMdWRN8HtVzoUOItHMwv5c=
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
github.com/denverdino/aliyungo v0.0.0-20170629053852-f6cab0c35083/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/denverdino/aliyungo v0.0.0-20170629053852-f6cab0c35083/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
...@@ -356,6 +355,8 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV ...@@ -356,6 +355,8 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 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 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
...@@ -427,7 +428,6 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51 ...@@ -427,7 +428,6 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
...@@ -511,8 +511,8 @@ github.com/k3s-io/cri v1.4.0-k3s.7 h1:1ycdF3dMDJMW/k/UxDC6eMsyGSMZ/p0AoUBVdJvNGQ ...@@ -511,8 +511,8 @@ github.com/k3s-io/cri v1.4.0-k3s.7 h1:1ycdF3dMDJMW/k/UxDC6eMsyGSMZ/p0AoUBVdJvNGQ
github.com/k3s-io/cri v1.4.0-k3s.7/go.mod h1:fGPUUHMKQik/vIegSe05DtX/m4miovdtvVLqRUFAkK0= github.com/k3s-io/cri v1.4.0-k3s.7/go.mod h1:fGPUUHMKQik/vIegSe05DtX/m4miovdtvVLqRUFAkK0=
github.com/k3s-io/cri-tools v1.20.0-k3s1 h1:pd6/NWvCwMeBiBvvkGSCBQHlVJFQ5Vj2f1oXXIA18RY= github.com/k3s-io/cri-tools v1.20.0-k3s1 h1:pd6/NWvCwMeBiBvvkGSCBQHlVJFQ5Vj2f1oXXIA18RY=
github.com/k3s-io/cri-tools v1.20.0-k3s1/go.mod h1:APtxWLVkA6SNZxmG5/ETT3QAxG0BptTtFc8EywLSdLQ= github.com/k3s-io/cri-tools v1.20.0-k3s1/go.mod h1:APtxWLVkA6SNZxmG5/ETT3QAxG0BptTtFc8EywLSdLQ=
github.com/k3s-io/etcd v0.5.0-alpha.5.0.20201208200253-50621aee4aea h1:7cwby0GoNAi8IsVrT0q+JfQpB6V76ZaEGhj6qts/mvU= github.com/k3s-io/etcd v0.5.0-alpha.5.0.20220113195313-6c2233a709e8 h1:EEZ7gWyQ3xOR4vL5j067MJmDy/kOKmShIW277mYQB+E=
github.com/k3s-io/etcd v0.5.0-alpha.5.0.20201208200253-50621aee4aea/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= github.com/k3s-io/etcd v0.5.0-alpha.5.0.20220113195313-6c2233a709e8/go.mod h1:t1cqOhpjW3SEYhH7Wzlg51xzyIM2c5HMB9kvPO5k4gY=
github.com/k3s-io/helm-controller v0.10.8 h1:O7zoqUBp3W+6+nRCUWNiAoQMzOX6xw9IsBDXc5lP3Ns= github.com/k3s-io/helm-controller v0.10.8 h1:O7zoqUBp3W+6+nRCUWNiAoQMzOX6xw9IsBDXc5lP3Ns=
github.com/k3s-io/helm-controller v0.10.8/go.mod h1:nZP8FH3KZrNNUf5r+SwwiMR63HS6lxdHdpHijgPfF74= github.com/k3s-io/helm-controller v0.10.8/go.mod h1:nZP8FH3KZrNNUf5r+SwwiMR63HS6lxdHdpHijgPfF74=
github.com/k3s-io/kine v0.6.5 h1:gYjkuVUUhuIMthIAQSecb2APSx+JZLFO/GWHbDp2NFI= github.com/k3s-io/kine v0.6.5 h1:gYjkuVUUhuIMthIAQSecb2APSx+JZLFO/GWHbDp2NFI=
...@@ -879,9 +879,9 @@ github.com/tencentcloud/tencentcloud-sdk-go v1.0.67/go.mod h1:asUz5BPXxgoPGaRgZa ...@@ -879,9 +879,9 @@ github.com/tencentcloud/tencentcloud-sdk-go v1.0.67/go.mod h1:asUz5BPXxgoPGaRgZa
github.com/thecodeteam/goscaleio v0.1.0/go.mod h1:68sdkZAsK8bvEwBlbQnlLS+xU+hvLYM/iQ8KXej1AwM= github.com/thecodeteam/goscaleio v0.1.0/go.mod h1:68sdkZAsK8bvEwBlbQnlLS+xU+hvLYM/iQ8KXej1AwM=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966 h1:j6JEOq5QWFker+d7mFQYOhjTZonQ7YkLTHm56dbn+yM=
github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/u-root/u-root v7.0.0+incompatible/go.mod h1:RYkpo8pTHrNjW08opNd/U6p/RJE7K0D8fXO0d47+3YY= github.com/u-root/u-root v7.0.0+incompatible/go.mod h1:RYkpo8pTHrNjW08opNd/U6p/RJE7K0D8fXO0d47+3YY=
github.com/ugorji/go v0.0.0-20170107133203-ded73eae5db7/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= github.com/ugorji/go v0.0.0-20170107133203-ded73eae5db7/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
......
## 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)
}
```
...@@ -5,9 +5,7 @@ script: ...@@ -5,9 +5,7 @@ script:
- go test -v ./... - go test -v ./...
go: go:
- 1.3
- 1.4
- 1.5
- 1.6
- 1.7 - 1.7
- tip - 1.8
- 1.9
- 1.10
Copyright (c) 2012 Dave Grijalva 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: 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:
......
## Migration Guide (v3.2.1)
Starting from [v3.2.1](https://github.com/golang-jwt/jwt/releases/tag/v3.2.1]), the import path has changed from `github.com/dgrijalva/jwt-go` to `github.com/golang-jwt/jwt`. Future releases will be using the `github.com/golang-jwt/jwt` import path and continue the existing versioning scheme of `v3.x.x+incompatible`. Backwards-compatible patches and fixes will be done on the `v3` release branch, where as new build-breaking features will be developed in a `v4` release, possibly including a SIV-style import path.
### go.mod replacement
In a first step, the easiest way is to use `go mod edit` to issue a replacement.
```
go mod edit -replace github.com/dgrijalva/jwt-go=github.com/golang-jwt/jwt@v3.2.1+incompatible
go mod tidy
```
This will still keep the old import path in your code but replace it with the new package and also introduce a new indirect dependency to `github.com/golang-jwt/jwt`. Try to compile your project; it should still work.
### Cleanup
If your code still consistently builds, you can replace all occurences of `github.com/dgrijalva/jwt-go` with `github.com/golang-jwt/jwt`, either manually or by using tools such as `sed`. Finally, the `replace` directive in the `go.mod` file can be removed.
## 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.
\ No newline at end of file
## `jwt-go` Version History ## `jwt-go` Version History
#### 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 #### 3.2.0
* Added method `ParseUnverified` to allow users to split up the tasks of parsing and validation * Added method `ParseUnverified` to allow users to split up the tasks of parsing and validation
...@@ -115,4 +121,4 @@ It is likely the only integration change required here will be to change `func(t ...@@ -115,4 +121,4 @@ It is likely the only integration change required here will be to change `func(t
* First versioned release * First versioned release
* API stabilized * API stabilized
* Supports creating, signing, parsing, and validating JWT tokens * Supports creating, signing, parsing, and validating JWT tokens
* Supports RS256 and HS256 signing methods * Supports RS256 and HS256 signing methods
\ No newline at end of file
...@@ -35,18 +35,18 @@ func (c StandardClaims) Valid() error { ...@@ -35,18 +35,18 @@ func (c StandardClaims) Valid() error {
// The claims below are optional, by default, so if they are set to the // 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. // default value in Go, let's not fail the verification for them.
if c.VerifyExpiresAt(now, false) == false { if !c.VerifyExpiresAt(now, false) {
delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0)) delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0))
vErr.Inner = fmt.Errorf("token is expired by %v", delta) vErr.Inner = fmt.Errorf("token is expired by %v", delta)
vErr.Errors |= ValidationErrorExpired vErr.Errors |= ValidationErrorExpired
} }
if c.VerifyIssuedAt(now, false) == false { if !c.VerifyIssuedAt(now, false) {
vErr.Inner = fmt.Errorf("Token used before issued") vErr.Inner = fmt.Errorf("Token used before issued")
vErr.Errors |= ValidationErrorIssuedAt vErr.Errors |= ValidationErrorIssuedAt
} }
if c.VerifyNotBefore(now, false) == false { if !c.VerifyNotBefore(now, false) {
vErr.Inner = fmt.Errorf("token is not valid yet") vErr.Inner = fmt.Errorf("token is not valid yet")
vErr.Errors |= ValidationErrorNotValidYet vErr.Errors |= ValidationErrorNotValidYet
} }
...@@ -61,7 +61,7 @@ func (c StandardClaims) Valid() error { ...@@ -61,7 +61,7 @@ func (c StandardClaims) Valid() error {
// Compares the aud claim against cmp. // Compares the aud claim against cmp.
// If required is false, this method will return true if the value matches or is unset // 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 { func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool {
return verifyAud(c.Audience, cmp, req) return verifyAud([]string{c.Audience}, cmp, req)
} }
// Compares the exp claim against cmp. // Compares the exp claim against cmp.
...@@ -90,15 +90,27 @@ func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool { ...@@ -90,15 +90,27 @@ func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool {
// ----- helpers // ----- helpers
func verifyAud(aud string, cmp string, required bool) bool { func verifyAud(aud []string, cmp string, required bool) bool {
if aud == "" { if len(aud) == 0 {
return !required return !required
} }
if subtle.ConstantTimeCompare([]byte(aud), []byte(cmp)) != 0 { // use a var here to keep constant time compare when looping over a number of claims
return true result := false
} else {
return 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 int64, now int64, required bool) bool { func verifyExp(exp int64, now int64, required bool) bool {
......
...@@ -88,11 +88,11 @@ func (m *SigningMethodECDSA) Verify(signingString, signature string, key interfa ...@@ -88,11 +88,11 @@ func (m *SigningMethodECDSA) Verify(signingString, signature string, key interfa
hasher.Write([]byte(signingString)) hasher.Write([]byte(signingString))
// Verify the signature // 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 return nil
} else {
return ErrECDSAVerification
} }
return ErrECDSAVerification
} }
// Implements the Sign method from SigningMethod // Implements the Sign method from SigningMethod
......
...@@ -25,7 +25,9 @@ func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) { ...@@ -25,7 +25,9 @@ func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) {
// Parse the key // Parse the key
var parsedKey interface{} var parsedKey interface{}
if parsedKey, err = x509.ParseECPrivateKey(block.Bytes); err != nil { if parsedKey, err = x509.ParseECPrivateKey(block.Bytes); err != nil {
return nil, err if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil {
return nil, err
}
} }
var pkey *ecdsa.PrivateKey var pkey *ecdsa.PrivateKey
......
...@@ -10,10 +10,24 @@ import ( ...@@ -10,10 +10,24 @@ import (
// This is the default claims type if you don't supply one // This is the default claims type if you don't supply one
type MapClaims map[string]interface{} type MapClaims map[string]interface{}
// Compares the aud claim against cmp. // VerifyAudience Compares the aud claim against cmp.
// If required is false, this method will return true if the value matches or is unset // 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 { func (m MapClaims) VerifyAudience(cmp string, req bool) bool {
aud, _ := m["aud"].(string) 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) return verifyAud(aud, cmp, req)
} }
...@@ -27,7 +41,7 @@ func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool { ...@@ -27,7 +41,7 @@ func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool {
v, _ := exp.Int64() v, _ := exp.Int64()
return verifyExp(v, cmp, req) return verifyExp(v, cmp, req)
} }
return req == false return !req
} }
// Compares the iat claim against cmp. // Compares the iat claim against cmp.
...@@ -40,7 +54,7 @@ func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool { ...@@ -40,7 +54,7 @@ func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool {
v, _ := iat.Int64() v, _ := iat.Int64()
return verifyIat(v, cmp, req) return verifyIat(v, cmp, req)
} }
return req == false return !req
} }
// Compares the iss claim against cmp. // Compares the iss claim against cmp.
...@@ -60,7 +74,7 @@ func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool { ...@@ -60,7 +74,7 @@ func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool {
v, _ := nbf.Int64() v, _ := nbf.Int64()
return verifyNbf(v, cmp, req) return verifyNbf(v, cmp, req)
} }
return req == false return !req
} }
// Validates time based claims "exp, iat, nbf". // Validates time based claims "exp, iat, nbf".
...@@ -71,17 +85,17 @@ func (m MapClaims) Valid() error { ...@@ -71,17 +85,17 @@ func (m MapClaims) Valid() error {
vErr := new(ValidationError) vErr := new(ValidationError)
now := TimeFunc().Unix() now := TimeFunc().Unix()
if m.VerifyExpiresAt(now, false) == false { if !m.VerifyExpiresAt(now, false) {
vErr.Inner = errors.New("Token is expired") vErr.Inner = errors.New("Token is expired")
vErr.Errors |= ValidationErrorExpired vErr.Errors |= ValidationErrorExpired
} }
if m.VerifyIssuedAt(now, false) == false { if !m.VerifyIssuedAt(now, false) {
vErr.Inner = errors.New("Token used before issued") vErr.Inner = errors.New("Token used before issued")
vErr.Errors |= ValidationErrorIssuedAt vErr.Errors |= ValidationErrorIssuedAt
} }
if m.VerifyNotBefore(now, false) == false { if !m.VerifyNotBefore(now, false) {
vErr.Inner = errors.New("Token is not valid yet") vErr.Inner = errors.New("Token is not valid yet")
vErr.Errors |= ValidationErrorNotValidYet vErr.Errors |= ValidationErrorNotValidYet
} }
......
...@@ -12,9 +12,14 @@ import ( ...@@ -12,9 +12,14 @@ import (
type SigningMethodRSAPSS struct { type SigningMethodRSAPSS struct {
*SigningMethodRSA *SigningMethodRSA
Options *rsa.PSSOptions Options *rsa.PSSOptions
// VerifyOptions is optional. If set overrides Options for rsa.VerifyPPS.
// Used to accept tokens signed with rsa.PSSSaltLengthAuto, what doesn't follow
// https://tools.ietf.org/html/rfc7518#section-3.5 but was used previously.
// See https://github.com/dgrijalva/jwt-go/issues/285#issuecomment-437451244 for details.
VerifyOptions *rsa.PSSOptions
} }
// Specific instances for RS/PS and company // Specific instances for RS/PS and company.
var ( var (
SigningMethodPS256 *SigningMethodRSAPSS SigningMethodPS256 *SigningMethodRSAPSS
SigningMethodPS384 *SigningMethodRSAPSS SigningMethodPS384 *SigningMethodRSAPSS
...@@ -24,13 +29,15 @@ var ( ...@@ -24,13 +29,15 @@ var (
func init() { func init() {
// PS256 // PS256
SigningMethodPS256 = &SigningMethodRSAPSS{ SigningMethodPS256 = &SigningMethodRSAPSS{
&SigningMethodRSA{ SigningMethodRSA: &SigningMethodRSA{
Name: "PS256", Name: "PS256",
Hash: crypto.SHA256, Hash: crypto.SHA256,
}, },
&rsa.PSSOptions{ Options: &rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthEqualsHash,
},
VerifyOptions: &rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthAuto, SaltLength: rsa.PSSSaltLengthAuto,
Hash: crypto.SHA256,
}, },
} }
RegisterSigningMethod(SigningMethodPS256.Alg(), func() SigningMethod { RegisterSigningMethod(SigningMethodPS256.Alg(), func() SigningMethod {
...@@ -39,13 +46,15 @@ func init() { ...@@ -39,13 +46,15 @@ func init() {
// PS384 // PS384
SigningMethodPS384 = &SigningMethodRSAPSS{ SigningMethodPS384 = &SigningMethodRSAPSS{
&SigningMethodRSA{ SigningMethodRSA: &SigningMethodRSA{
Name: "PS384", Name: "PS384",
Hash: crypto.SHA384, Hash: crypto.SHA384,
}, },
&rsa.PSSOptions{ Options: &rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthEqualsHash,
},
VerifyOptions: &rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthAuto, SaltLength: rsa.PSSSaltLengthAuto,
Hash: crypto.SHA384,
}, },
} }
RegisterSigningMethod(SigningMethodPS384.Alg(), func() SigningMethod { RegisterSigningMethod(SigningMethodPS384.Alg(), func() SigningMethod {
...@@ -54,13 +63,15 @@ func init() { ...@@ -54,13 +63,15 @@ func init() {
// PS512 // PS512
SigningMethodPS512 = &SigningMethodRSAPSS{ SigningMethodPS512 = &SigningMethodRSAPSS{
&SigningMethodRSA{ SigningMethodRSA: &SigningMethodRSA{
Name: "PS512", Name: "PS512",
Hash: crypto.SHA512, Hash: crypto.SHA512,
}, },
&rsa.PSSOptions{ Options: &rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthEqualsHash,
},
VerifyOptions: &rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthAuto, SaltLength: rsa.PSSSaltLengthAuto,
Hash: crypto.SHA512,
}, },
} }
RegisterSigningMethod(SigningMethodPS512.Alg(), func() SigningMethod { RegisterSigningMethod(SigningMethodPS512.Alg(), func() SigningMethod {
...@@ -94,7 +105,12 @@ func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interf ...@@ -94,7 +105,12 @@ func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interf
hasher := m.Hash.New() hasher := m.Hash.New()
hasher.Write([]byte(signingString)) hasher.Write([]byte(signingString))
return rsa.VerifyPSS(rsaKey, m.Hash, hasher.Sum(nil), sig, m.Options) opts := m.Options
if m.VerifyOptions != nil {
opts = m.VerifyOptions
}
return rsa.VerifyPSS(rsaKey, m.Hash, hasher.Sum(nil), sig, opts)
} }
// Implements the Sign method from SigningMethod // Implements the Sign method from SigningMethod
......
...@@ -8,7 +8,7 @@ import ( ...@@ -8,7 +8,7 @@ import (
) )
var ( var (
ErrKeyMustBePEMEncoded = errors.New("Invalid Key: Key must be PEM encoded PKCS1 or PKCS8 private key") ErrKeyMustBePEMEncoded = errors.New("Invalid Key: Key must be a PEM encoded PKCS1 or PKCS8 key")
ErrNotRSAPrivateKey = errors.New("Key is not a valid RSA private key") ErrNotRSAPrivateKey = errors.New("Key is not a valid RSA private key")
ErrNotRSAPublicKey = errors.New("Key is not a valid RSA public key") ErrNotRSAPublicKey = errors.New("Key is not a valid RSA public key")
) )
......
...@@ -65,7 +65,7 @@ func (t *Token) SignedString(key interface{}) (string, error) { ...@@ -65,7 +65,7 @@ func (t *Token) SignedString(key interface{}) (string, error) {
func (t *Token) SigningString() (string, error) { func (t *Token) SigningString() (string, error) {
var err error var err error
parts := make([]string, 2) parts := make([]string, 2)
for i, _ := range parts { for i := range parts {
var jsonValue []byte var jsonValue []byte
if i == 0 { if i == 0 {
if jsonValue, err = json.Marshal(t.Header); err != nil { if jsonValue, err = json.Marshal(t.Header); err != nil {
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"io" "io"
"net/http" "net/http"
"strings" "strings"
"time"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
...@@ -27,12 +28,16 @@ type RequestMutatorFunc func(incoming *http.Request, outgoing *http.Request) *ht ...@@ -27,12 +28,16 @@ type RequestMutatorFunc func(incoming *http.Request, outgoing *http.Request) *ht
// Proxy provides websocket transport upgrade to compatible endpoints. // Proxy provides websocket transport upgrade to compatible endpoints.
type Proxy struct { type Proxy struct {
h http.Handler h http.Handler
logger Logger logger Logger
methodOverrideParam string maxRespBodyBufferBytes int
tokenCookieName string methodOverrideParam string
requestMutator RequestMutatorFunc tokenCookieName string
headerForwarder func(header string) bool requestMutator RequestMutatorFunc
headerForwarder func(header string) bool
pingInterval time.Duration
pingWait time.Duration
pongWait time.Duration
} }
// Logger collects log messages. // Logger collects log messages.
...@@ -52,6 +57,15 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { ...@@ -52,6 +57,15 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Option allows customization of the proxy. // Option allows customization of the proxy.
type Option func(*Proxy) type Option func(*Proxy)
// WithMaxRespBodyBufferSize allows specification of a custom size for the
// buffer used while reading the response body. By default, the bufio.Scanner
// used to read the response body sets the maximum token size to MaxScanTokenSize.
func WithMaxRespBodyBufferSize(nBytes int) Option {
return func(p *Proxy) {
p.maxRespBodyBufferBytes = nBytes
}
}
// WithMethodParamOverride allows specification of the special http parameter that is used in the proxied streaming request. // WithMethodParamOverride allows specification of the special http parameter that is used in the proxied streaming request.
func WithMethodParamOverride(param string) Option { func WithMethodParamOverride(param string) Option {
return func(p *Proxy) { return func(p *Proxy) {
...@@ -87,6 +101,17 @@ func WithLogger(logger Logger) Option { ...@@ -87,6 +101,17 @@ func WithLogger(logger Logger) Option {
} }
} }
// WithPingControl allows specification of ping pong control. The interval
// parameter specifies the pingInterval between pings. The allowed wait time
// for a pong response is (pingInterval * 10) / 9.
func WithPingControl(interval time.Duration) Option {
return func(proxy *Proxy) {
proxy.pingInterval = interval
proxy.pongWait = (interval * 10) / 9
proxy.pingWait = proxy.pongWait / 6
}
}
var defaultHeadersToForward = map[string]bool{ var defaultHeadersToForward = map[string]bool{
"Origin": true, "Origin": true,
"origin": true, "origin": true,
...@@ -201,6 +226,10 @@ func (p *Proxy) proxy(w http.ResponseWriter, r *http.Request) { ...@@ -201,6 +226,10 @@ func (p *Proxy) proxy(w http.ResponseWriter, r *http.Request) {
// read loop -- take messages from websocket and write to http request // read loop -- take messages from websocket and write to http request
go func() { go func() {
if p.pingInterval > 0 && p.pingWait > 0 && p.pongWait > 0 {
conn.SetReadDeadline(time.Now().Add(p.pongWait))
conn.SetPongHandler(func(string) error { conn.SetReadDeadline(time.Now().Add(p.pongWait)); return nil })
}
defer func() { defer func() {
cancelFn() cancelFn()
}() }()
...@@ -232,8 +261,38 @@ func (p *Proxy) proxy(w http.ResponseWriter, r *http.Request) { ...@@ -232,8 +261,38 @@ func (p *Proxy) proxy(w http.ResponseWriter, r *http.Request) {
} }
} }
}() }()
// ping write loop
if p.pingInterval > 0 && p.pingWait > 0 && p.pongWait > 0 {
go func() {
ticker := time.NewTicker(p.pingInterval)
defer func() {
ticker.Stop()
conn.Close()
}()
for {
select {
case <-ctx.Done():
p.logger.Debugln("ping loop done")
return
case <-ticker.C:
conn.SetWriteDeadline(time.Now().Add(p.pingWait))
if err := conn.WriteMessage(websocket.PingMessage, nil); err != nil {
return
}
}
}
}()
}
// write loop -- take messages from response and write to websocket // write loop -- take messages from response and write to websocket
scanner := bufio.NewScanner(responseBodyR) scanner := bufio.NewScanner(responseBodyR)
// if maxRespBodyBufferSize has been specified, use custom buffer for scanner
var scannerBuf []byte
if p.maxRespBodyBufferBytes > 0 {
scannerBuf = make([]byte, 0, 64*1024)
scanner.Buffer(scannerBuf, p.maxRespBodyBufferBytes)
}
for scanner.Scan() { for scanner.Scan() {
if len(scanner.Bytes()) == 0 { if len(scanner.Bytes()) == 0 {
p.logger.Warnln("[write] empty scan", scanner.Err()) p.logger.Warnln("[write] empty scan", scanner.Err())
......
...@@ -21,7 +21,7 @@ import ( ...@@ -21,7 +21,7 @@ import (
"errors" "errors"
"time" "time"
jwt "github.com/dgrijalva/jwt-go" "github.com/golang-jwt/jwt"
"go.uber.org/zap" "go.uber.org/zap"
) )
......
...@@ -21,7 +21,7 @@ import ( ...@@ -21,7 +21,7 @@ import (
"io/ioutil" "io/ioutil"
"time" "time"
jwt "github.com/dgrijalva/jwt-go" "github.com/golang-jwt/jwt"
) )
const ( const (
......
...@@ -105,6 +105,16 @@ func (c *Client) streamClientInterceptor(logger *zap.Logger, optFuncs ...retryOp ...@@ -105,6 +105,16 @@ func (c *Client) streamClientInterceptor(logger *zap.Logger, optFuncs ...retryOp
intOpts := reuseOrNewWithCallOptions(defaultOptions, optFuncs) intOpts := reuseOrNewWithCallOptions(defaultOptions, optFuncs)
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
ctx = withVersion(ctx) ctx = withVersion(ctx)
// getToken automatically
// TODO(cfc4n): keep this code block, remove codes about getToken in client.go after pr #12165 merged.
if c.authTokenBundle != nil {
// equal to c.Username != "" && c.Password != ""
err := c.getToken(ctx)
if err != nil && rpctypes.Error(err) != rpctypes.ErrAuthNotEnabled {
logger.Error("clientv3/retry_interceptor: getToken failed", zap.Error(err))
return nil, err
}
}
grpcOpts, retryOpts := filterCallOptions(opts) grpcOpts, retryOpts := filterCallOptions(opts)
callOpts := reuseOrNewWithCallOptions(intOpts, retryOpts) callOpts := reuseOrNewWithCallOptions(intOpts, retryOpts)
// short circuit for simplicity, and avoiding allocations. // short circuit for simplicity, and avoiding allocations.
......
...@@ -53,6 +53,7 @@ const ( ...@@ -53,6 +53,7 @@ const (
DefaultMaxSnapshots = 5 DefaultMaxSnapshots = 5
DefaultMaxWALs = 5 DefaultMaxWALs = 5
DefaultMaxTxnOps = uint(128) DefaultMaxTxnOps = uint(128)
DefaultWarningApplyDuration = 100 * time.Millisecond
DefaultMaxRequestBytes = 1.5 * 1024 * 1024 DefaultMaxRequestBytes = 1.5 * 1024 * 1024
DefaultGRPCKeepAliveMinTime = 5 * time.Second DefaultGRPCKeepAliveMinTime = 5 * time.Second
DefaultGRPCKeepAliveInterval = 2 * time.Hour DefaultGRPCKeepAliveInterval = 2 * time.Hour
...@@ -285,6 +286,9 @@ type Config struct { ...@@ -285,6 +286,9 @@ type Config struct {
ExperimentalEnableLeaseCheckpoint bool `json:"experimental-enable-lease-checkpoint"` ExperimentalEnableLeaseCheckpoint bool `json:"experimental-enable-lease-checkpoint"`
ExperimentalCompactionBatchLimit int `json:"experimental-compaction-batch-limit"` ExperimentalCompactionBatchLimit int `json:"experimental-compaction-batch-limit"`
ExperimentalWatchProgressNotifyInterval time.Duration `json:"experimental-watch-progress-notify-interval"` ExperimentalWatchProgressNotifyInterval time.Duration `json:"experimental-watch-progress-notify-interval"`
// ExperimentalWarningApplyDuration is the time duration after which a warning is generated if applying request
// takes more time than this value.
ExperimentalWarningApplyDuration time.Duration `json:"experimental-warning-apply-duration"`
// ForceNewCluster starts a new cluster even if previously started; unsafe. // ForceNewCluster starts a new cluster even if previously started; unsafe.
ForceNewCluster bool `json:"force-new-cluster"` ForceNewCluster bool `json:"force-new-cluster"`
...@@ -388,8 +392,9 @@ func NewConfig() *Config { ...@@ -388,8 +392,9 @@ func NewConfig() *Config {
SnapshotCount: etcdserver.DefaultSnapshotCount, SnapshotCount: etcdserver.DefaultSnapshotCount,
SnapshotCatchUpEntries: etcdserver.DefaultSnapshotCatchUpEntries, SnapshotCatchUpEntries: etcdserver.DefaultSnapshotCatchUpEntries,
MaxTxnOps: DefaultMaxTxnOps, MaxTxnOps: DefaultMaxTxnOps,
MaxRequestBytes: DefaultMaxRequestBytes, MaxRequestBytes: DefaultMaxRequestBytes,
ExperimentalWarningApplyDuration: DefaultWarningApplyDuration,
GRPCKeepAliveMinTime: DefaultGRPCKeepAliveMinTime, GRPCKeepAliveMinTime: DefaultGRPCKeepAliveMinTime,
GRPCKeepAliveInterval: DefaultGRPCKeepAliveInterval, GRPCKeepAliveInterval: DefaultGRPCKeepAliveInterval,
......
...@@ -209,6 +209,7 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) { ...@@ -209,6 +209,7 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) {
EnableLeaseCheckpoint: cfg.ExperimentalEnableLeaseCheckpoint, EnableLeaseCheckpoint: cfg.ExperimentalEnableLeaseCheckpoint,
CompactionBatchLimit: cfg.ExperimentalCompactionBatchLimit, CompactionBatchLimit: cfg.ExperimentalCompactionBatchLimit,
WatchProgressNotifyInterval: cfg.ExperimentalWatchProgressNotifyInterval, WatchProgressNotifyInterval: cfg.ExperimentalWatchProgressNotifyInterval,
WarningApplyDuration: cfg.ExperimentalWarningApplyDuration,
} }
print(e.cfg.logger, *cfg, srvcfg, memberInitialized) print(e.cfg.logger, *cfg, srvcfg, memberInitialized)
if e.Server, err = etcdserver.NewServer(srvcfg); err != nil { if e.Server, err = etcdserver.NewServer(srvcfg); err != nil {
......
...@@ -19,6 +19,7 @@ import ( ...@@ -19,6 +19,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
defaultLog "log" defaultLog "log"
"math"
"net" "net"
"net/http" "net/http"
"strings" "strings"
...@@ -229,6 +230,10 @@ func (sctx *serveCtx) registerGateway(opts []grpc.DialOption) (*gw.ServeMux, err ...@@ -229,6 +230,10 @@ func (sctx *serveCtx) registerGateway(opts []grpc.DialOption) (*gw.ServeMux, err
addr = fmt.Sprintf("%s://%s", network, addr) addr = fmt.Sprintf("%s://%s", network, addr)
} }
opts = append(opts, grpc.WithDefaultCallOptions([]grpc.CallOption{
grpc.MaxCallRecvMsgSize(math.MaxInt32),
}...))
conn, err := grpc.DialContext(ctx, addr, opts...) conn, err := grpc.DialContext(ctx, addr, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -286,6 +291,7 @@ func (sctx *serveCtx) createMux(gwmux *gw.ServeMux, handler http.Handler) *http. ...@@ -286,6 +291,7 @@ func (sctx *serveCtx) createMux(gwmux *gw.ServeMux, handler http.Handler) *http.
return outgoing return outgoing
}, },
), ),
wsproxy.WithMaxRespBodyBufferSize(0x7fffffff),
), ),
) )
} }
......
...@@ -36,7 +36,7 @@ const ( ...@@ -36,7 +36,7 @@ const (
// HandleMetricsHealth registers metrics and health handlers. // HandleMetricsHealth registers metrics and health handlers.
func HandleMetricsHealth(mux *http.ServeMux, srv etcdserver.ServerV2) { func HandleMetricsHealth(mux *http.ServeMux, srv etcdserver.ServerV2) {
mux.Handle(PathMetrics, promhttp.Handler()) mux.Handle(PathMetrics, promhttp.Handler())
mux.Handle(PathHealth, NewHealthHandler(func() Health { return checkHealth(srv) })) mux.Handle(PathHealth, NewHealthHandler(func(excludedAlarms AlarmSet) Health { return checkHealth(srv, excludedAlarms) }))
} }
// HandlePrometheus registers prometheus handler on '/metrics'. // HandlePrometheus registers prometheus handler on '/metrics'.
...@@ -45,7 +45,7 @@ func HandlePrometheus(mux *http.ServeMux) { ...@@ -45,7 +45,7 @@ func HandlePrometheus(mux *http.ServeMux) {
} }
// NewHealthHandler handles '/health' requests. // NewHealthHandler handles '/health' requests.
func NewHealthHandler(hfunc func() Health) http.HandlerFunc { func NewHealthHandler(hfunc func(excludedAlarms AlarmSet) Health) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet { if r.Method != http.MethodGet {
w.Header().Set("Allow", http.MethodGet) w.Header().Set("Allow", http.MethodGet)
...@@ -53,7 +53,8 @@ func NewHealthHandler(hfunc func() Health) http.HandlerFunc { ...@@ -53,7 +53,8 @@ func NewHealthHandler(hfunc func() Health) http.HandlerFunc {
plog.Warningf("/health error (status code %d)", http.StatusMethodNotAllowed) plog.Warningf("/health error (status code %d)", http.StatusMethodNotAllowed)
return return
} }
h := hfunc() excludedAlarms := getExcludedAlarms(r)
h := hfunc(excludedAlarms)
d, _ := json.Marshal(h) d, _ := json.Marshal(h)
if h.Health != "true" { if h.Health != "true" {
http.Error(w, string(d), http.StatusServiceUnavailable) http.Error(w, string(d), http.StatusServiceUnavailable)
...@@ -90,19 +91,46 @@ type Health struct { ...@@ -90,19 +91,46 @@ type Health struct {
Health string `json:"health"` Health string `json:"health"`
} }
type AlarmSet map[string]struct{}
func getExcludedAlarms(r *http.Request) (alarms AlarmSet) {
alarms = make(map[string]struct{}, 2)
alms, found := r.URL.Query()["exclude"]
if found {
for _, alm := range alms {
if len(alms) == 0 {
continue
}
alarms[alm] = struct{}{}
}
}
return alarms
}
// TODO: server NOSPACE, etcdserver.ErrNoLeader in health API // TODO: server NOSPACE, etcdserver.ErrNoLeader in health API
func checkHealth(srv etcdserver.ServerV2) Health { func checkHealth(srv etcdserver.ServerV2, excludedAlarms AlarmSet) Health {
h := Health{Health: "true"} h := Health{Health: "true"}
as := srv.Alarms() as := srv.Alarms()
if len(as) > 0 { if len(as) > 0 {
h.Health = "false"
for _, v := range as { for _, v := range as {
plog.Warningf("/health error due to an alarm %s", v.String()) alarmName := v.Alarm.String()
if _, found := excludedAlarms[alarmName]; found {
plog.Debugf("/health excluded alarm %s", alarmName)
delete(excludedAlarms, alarmName)
continue
}
h.Health = "false"
plog.Warningf("/health error due to %s", v.String())
return h
} }
} }
if len(excludedAlarms) > 0 {
plog.Warningf("fail exclude alarms from health check, exclude alarms %+v", excludedAlarms)
}
if h.Health == "true" { if h.Health == "true" {
if uint64(srv.Leader()) == raft.None { if uint64(srv.Leader()) == raft.None {
h.Health = "false" h.Health = "false"
...@@ -122,7 +150,7 @@ func checkHealth(srv etcdserver.ServerV2) Health { ...@@ -122,7 +150,7 @@ func checkHealth(srv etcdserver.ServerV2) Health {
if h.Health == "true" { if h.Health == "true" {
healthSuccess.Inc() healthSuccess.Inc()
plog.Infof("/health OK (status code %d)", http.StatusOK) plog.Debugf("/health OK (status code %d)", http.StatusOK)
} else { } else {
healthFailed.Inc() healthFailed.Inc()
} }
......
...@@ -763,16 +763,21 @@ func ValidateClusterAndAssignIDs(lg *zap.Logger, local *RaftCluster, existing *R ...@@ -763,16 +763,21 @@ func ValidateClusterAndAssignIDs(lg *zap.Logger, local *RaftCluster, existing *R
if len(ems) != len(lms) { if len(ems) != len(lms) {
return fmt.Errorf("member count is unequal") return fmt.Errorf("member count is unequal")
} }
sort.Sort(MembersByPeerURLs(ems))
sort.Sort(MembersByPeerURLs(lms))
ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second)
defer cancel() defer cancel()
for i := range ems { for i := range ems {
if ok, err := netutil.URLStringsEqual(ctx, lg, ems[i].PeerURLs, lms[i].PeerURLs); !ok { var err error
return fmt.Errorf("unmatched member while checking PeerURLs (%v)", err) ok := false
for j := range lms {
if ok, err = netutil.URLStringsEqual(ctx, lg, ems[i].PeerURLs, lms[j].PeerURLs); ok {
lms[j].ID = ems[i].ID
break
}
}
if !ok {
return fmt.Errorf("PeerURLs: no match found for existing member (%v, %v), last resolver error (%v)", ems[i].ID, ems[i].PeerURLs, err)
} }
lms[i].ID = ems[i].ID
} }
local.members = make(map[types.ID]*Member) local.members = make(map[types.ID]*Member)
for _, m := range lms { for _, m := range lms {
......
...@@ -217,8 +217,8 @@ func newStreamInterceptor(s *etcdserver.EtcdServer) grpc.StreamServerInterceptor ...@@ -217,8 +217,8 @@ func newStreamInterceptor(s *etcdserver.EtcdServer) grpc.StreamServerInterceptor
return rpctypes.ErrGRPCNoLeader return rpctypes.ErrGRPCNoLeader
} }
cctx, cancel := context.WithCancel(ss.Context()) ctx := newCancellableContext(ss.Context())
ss = serverStreamWithCtx{ctx: cctx, cancel: &cancel, ServerStream: ss} ss = serverStreamWithCtx{ctx: ctx, ServerStream: ss}
smap.mu.Lock() smap.mu.Lock()
smap.streams[ss] = struct{}{} smap.streams[ss] = struct{}{}
...@@ -228,7 +228,8 @@ func newStreamInterceptor(s *etcdserver.EtcdServer) grpc.StreamServerInterceptor ...@@ -228,7 +228,8 @@ func newStreamInterceptor(s *etcdserver.EtcdServer) grpc.StreamServerInterceptor
smap.mu.Lock() smap.mu.Lock()
delete(smap.streams, ss) delete(smap.streams, ss)
smap.mu.Unlock() smap.mu.Unlock()
cancel() // TODO: investigate whether the reason for cancellation here is useful to know
ctx.Cancel(nil)
}() }()
} }
} }
...@@ -237,10 +238,52 @@ func newStreamInterceptor(s *etcdserver.EtcdServer) grpc.StreamServerInterceptor ...@@ -237,10 +238,52 @@ func newStreamInterceptor(s *etcdserver.EtcdServer) grpc.StreamServerInterceptor
} }
} }
// cancellableContext wraps a context with new cancellable context that allows a
// specific cancellation error to be preserved and later retrieved using the
// Context.Err() function. This is so downstream context users can disambiguate
// the reason for the cancellation which could be from the client (for example)
// or from this interceptor code.
type cancellableContext struct {
context.Context
lock sync.RWMutex
cancel context.CancelFunc
cancelReason error
}
func newCancellableContext(parent context.Context) *cancellableContext {
ctx, cancel := context.WithCancel(parent)
return &cancellableContext{
Context: ctx,
cancel: cancel,
}
}
// Cancel stores the cancellation reason and then delegates to context.WithCancel
// against the parent context.
func (c *cancellableContext) Cancel(reason error) {
c.lock.Lock()
c.cancelReason = reason
c.lock.Unlock()
c.cancel()
}
// Err will return the preserved cancel reason error if present, and will
// otherwise return the underlying error from the parent context.
func (c *cancellableContext) Err() error {
c.lock.RLock()
defer c.lock.RUnlock()
if c.cancelReason != nil {
return c.cancelReason
}
return c.Context.Err()
}
type serverStreamWithCtx struct { type serverStreamWithCtx struct {
grpc.ServerStream grpc.ServerStream
ctx context.Context
cancel *context.CancelFunc // ctx is used so that we can preserve a reason for cancellation.
ctx *cancellableContext
} }
func (ssc serverStreamWithCtx) Context() context.Context { return ssc.ctx } func (ssc serverStreamWithCtx) Context() context.Context { return ssc.ctx }
...@@ -272,7 +315,7 @@ func monitorLeader(s *etcdserver.EtcdServer) *streamsMap { ...@@ -272,7 +315,7 @@ func monitorLeader(s *etcdserver.EtcdServer) *streamsMap {
smap.mu.Lock() smap.mu.Lock()
for ss := range smap.streams { for ss := range smap.streams {
if ssWithCtx, ok := ss.(serverStreamWithCtx); ok { if ssWithCtx, ok := ss.(serverStreamWithCtx); ok {
(*ssWithCtx.cancel)() ssWithCtx.ctx.Cancel(rpctypes.ErrGRPCNoLeader)
<-ss.Context().Done() <-ss.Context().Done()
} }
} }
......
...@@ -35,6 +35,8 @@ var ( ...@@ -35,6 +35,8 @@ var (
ErrGRPCLeaseExist = status.New(codes.FailedPrecondition, "etcdserver: lease already exists").Err() ErrGRPCLeaseExist = status.New(codes.FailedPrecondition, "etcdserver: lease already exists").Err()
ErrGRPCLeaseTTLTooLarge = status.New(codes.OutOfRange, "etcdserver: too large lease TTL").Err() ErrGRPCLeaseTTLTooLarge = status.New(codes.OutOfRange, "etcdserver: too large lease TTL").Err()
ErrGRPCWatchCanceled = status.New(codes.Canceled, "etcdserver: watch canceled").Err()
ErrGRPCMemberExist = status.New(codes.FailedPrecondition, "etcdserver: member ID already exist").Err() ErrGRPCMemberExist = status.New(codes.FailedPrecondition, "etcdserver: member ID already exist").Err()
ErrGRPCPeerURLExist = status.New(codes.FailedPrecondition, "etcdserver: Peer URLs already exists").Err() ErrGRPCPeerURLExist = status.New(codes.FailedPrecondition, "etcdserver: Peer URLs already exists").Err()
ErrGRPCMemberNotEnoughStarted = status.New(codes.FailedPrecondition, "etcdserver: re-configuration failed due to not enough started members").Err() ErrGRPCMemberNotEnoughStarted = status.New(codes.FailedPrecondition, "etcdserver: re-configuration failed due to not enough started members").Err()
......
...@@ -206,15 +206,25 @@ func (ws *watchServer) Watch(stream pb.Watch_WatchServer) (err error) { ...@@ -206,15 +206,25 @@ func (ws *watchServer) Watch(stream pb.Watch_WatchServer) (err error) {
} }
}() }()
// TODO: There's a race here. When a stream is closed (e.g. due to a cancellation),
// the underlying error (e.g. a gRPC stream error) may be returned and handled
// through errc if the recv goroutine finishes before the send goroutine.
// When the recv goroutine wins, the stream error is retained. When recv loses
// the race, the underlying error is lost (unless the root error is propagated
// through Context.Err() which is not always the case (as callers have to decide
// to implement a custom context to do so). The stdlib context package builtins
// may be insufficient to carry semantically useful errors around and should be
// revisited.
select { select {
case err = <-errc: case err = <-errc:
if err == context.Canceled {
err = rpctypes.ErrGRPCWatchCanceled
}
close(sws.ctrlStream) close(sws.ctrlStream)
case <-stream.Context().Done(): case <-stream.Context().Done():
err = stream.Context().Err() err = stream.Context().Err()
// the only server-side cancellation is noleader for now.
if err == context.Canceled { if err == context.Canceled {
err = rpctypes.ErrGRPCNoLeader err = rpctypes.ErrGRPCWatchCanceled
} }
} }
......
...@@ -33,10 +33,6 @@ import ( ...@@ -33,10 +33,6 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
) )
const (
warnApplyDuration = 100 * time.Millisecond
)
type applyResult struct { type applyResult struct {
resp proto.Message resp proto.Message
err error err error
...@@ -115,7 +111,7 @@ func (s *EtcdServer) newApplierV3() applierV3 { ...@@ -115,7 +111,7 @@ func (s *EtcdServer) newApplierV3() applierV3 {
func (a *applierV3backend) Apply(r *pb.InternalRaftRequest) *applyResult { func (a *applierV3backend) Apply(r *pb.InternalRaftRequest) *applyResult {
ar := &applyResult{} ar := &applyResult{}
defer func(start time.Time) { defer func(start time.Time) {
warnOfExpensiveRequest(a.s.getLogger(), start, &pb.InternalRaftStringer{Request: r}, ar.resp, ar.err) warnOfExpensiveRequest(a.s.getLogger(), a.s.Cfg.WarningApplyDuration, start, &pb.InternalRaftStringer{Request: r}, ar.resp, ar.err)
if ar.err != nil { if ar.err != nil {
warnOfFailedRequest(a.s.getLogger(), start, &pb.InternalRaftStringer{Request: r}, ar.resp, ar.err) warnOfFailedRequest(a.s.getLogger(), start, &pb.InternalRaftStringer{Request: r}, ar.resp, ar.err)
} }
...@@ -185,7 +181,7 @@ func (a *applierV3backend) Put(txn mvcc.TxnWrite, p *pb.PutRequest) (resp *pb.Pu ...@@ -185,7 +181,7 @@ func (a *applierV3backend) Put(txn mvcc.TxnWrite, p *pb.PutRequest) (resp *pb.Pu
trace = traceutil.New("put", trace = traceutil.New("put",
a.s.getLogger(), a.s.getLogger(),
traceutil.Field{Key: "key", Value: string(p.Key)}, traceutil.Field{Key: "key", Value: string(p.Key)},
traceutil.Field{Key: "req_size", Value: proto.Size(p)}, traceutil.Field{Key: "req_size", Value: p.Size()},
) )
val, leaseID := p.Value, lease.LeaseID(p.Lease) val, leaseID := p.Value, lease.LeaseID(p.Lease)
if txn == nil { if txn == nil {
......
...@@ -119,7 +119,7 @@ func (s *EtcdServer) applyV2Request(r *RequestV2) Response { ...@@ -119,7 +119,7 @@ func (s *EtcdServer) applyV2Request(r *RequestV2) Response {
stringer: r, stringer: r,
alternative: func() string { return fmt.Sprintf("id:%d,method:%s,path:%s", r.ID, r.Method, r.Path) }, alternative: func() string { return fmt.Sprintf("id:%d,method:%s,path:%s", r.ID, r.Method, r.Path) },
} }
defer warnOfExpensiveRequest(s.getLogger(), time.Now(), stringer, nil, nil) defer warnOfExpensiveRequest(s.getLogger(), s.Cfg.WarningApplyDuration, time.Now(), stringer, nil, nil)
switch r.Method { switch r.Method {
case "POST": case "POST":
......
...@@ -119,6 +119,8 @@ type ServerConfig struct { ...@@ -119,6 +119,8 @@ type ServerConfig struct {
// MaxRequestBytes is the maximum request size to send over raft. // MaxRequestBytes is the maximum request size to send over raft.
MaxRequestBytes uint MaxRequestBytes uint
WarningApplyDuration time.Duration
StrictReconfigCheck bool StrictReconfigCheck bool
// ClientCertAuthEnabled is true when cert has been signed by the client CA. // ClientCertAuthEnabled is true when cert has been signed by the client CA.
......
...@@ -675,7 +675,7 @@ func restartAsStandaloneNode(cfg ServerConfig, snapshot *raftpb.Snapshot) (types ...@@ -675,7 +675,7 @@ func restartAsStandaloneNode(cfg ServerConfig, snapshot *raftpb.Snapshot) (types
} }
// getIDs returns an ordered set of IDs included in the given snapshot and // getIDs returns an ordered set of IDs included in the given snapshot and
// the entries. The given snapshot/entries can contain two kinds of // the entries. The given snapshot/entries can contain three kinds of
// ID-related entry: // ID-related entry:
// - ConfChangeAddNode, in which case the contained ID will be added into the set. // - ConfChangeAddNode, in which case the contained ID will be added into the set.
// - ConfChangeRemoveNode, in which case the contained ID will be removed from the set. // - ConfChangeRemoveNode, in which case the contained ID will be removed from the set.
......
...@@ -103,12 +103,12 @@ func (nc *notifier) notify(err error) { ...@@ -103,12 +103,12 @@ func (nc *notifier) notify(err error) {
close(nc.c) close(nc.c)
} }
func warnOfExpensiveRequest(lg *zap.Logger, now time.Time, reqStringer fmt.Stringer, respMsg proto.Message, err error) { func warnOfExpensiveRequest(lg *zap.Logger, warningApplyDuration time.Duration, now time.Time, reqStringer fmt.Stringer, respMsg proto.Message, err error) {
var resp string var resp string
if !isNil(respMsg) { if !isNil(respMsg) {
resp = fmt.Sprintf("size:%d", proto.Size(respMsg)) resp = fmt.Sprintf("size:%d", proto.Size(respMsg))
} }
warnOfExpensiveGenericRequest(lg, now, reqStringer, "", resp, err) warnOfExpensiveGenericRequest(lg, warningApplyDuration, now, reqStringer, "", resp, err)
} }
func warnOfFailedRequest(lg *zap.Logger, now time.Time, reqStringer fmt.Stringer, respMsg proto.Message, err error) { func warnOfFailedRequest(lg *zap.Logger, now time.Time, reqStringer fmt.Stringer, respMsg proto.Message, err error) {
...@@ -130,7 +130,7 @@ func warnOfFailedRequest(lg *zap.Logger, now time.Time, reqStringer fmt.Stringer ...@@ -130,7 +130,7 @@ func warnOfFailedRequest(lg *zap.Logger, now time.Time, reqStringer fmt.Stringer
} }
} }
func warnOfExpensiveReadOnlyTxnRequest(lg *zap.Logger, now time.Time, r *pb.TxnRequest, txnResponse *pb.TxnResponse, err error) { func warnOfExpensiveReadOnlyTxnRequest(lg *zap.Logger, warningApplyDuration time.Duration, now time.Time, r *pb.TxnRequest, txnResponse *pb.TxnResponse, err error) {
reqStringer := pb.NewLoggableTxnRequest(r) reqStringer := pb.NewLoggableTxnRequest(r)
var resp string var resp string
if !isNil(txnResponse) { if !isNil(txnResponse) {
...@@ -143,27 +143,27 @@ func warnOfExpensiveReadOnlyTxnRequest(lg *zap.Logger, now time.Time, r *pb.TxnR ...@@ -143,27 +143,27 @@ func warnOfExpensiveReadOnlyTxnRequest(lg *zap.Logger, now time.Time, r *pb.TxnR
// only range responses should be in a read only txn request // only range responses should be in a read only txn request
} }
} }
resp = fmt.Sprintf("responses:<%s> size:%d", strings.Join(resps, " "), proto.Size(txnResponse)) resp = fmt.Sprintf("responses:<%s> size:%d", strings.Join(resps, " "), txnResponse.Size())
} }
warnOfExpensiveGenericRequest(lg, now, reqStringer, "read-only range ", resp, err) warnOfExpensiveGenericRequest(lg, warningApplyDuration, now, reqStringer, "read-only range ", resp, err)
} }
func warnOfExpensiveReadOnlyRangeRequest(lg *zap.Logger, now time.Time, reqStringer fmt.Stringer, rangeResponse *pb.RangeResponse, err error) { func warnOfExpensiveReadOnlyRangeRequest(lg *zap.Logger, warningApplyDuration time.Duration, now time.Time, reqStringer fmt.Stringer, rangeResponse *pb.RangeResponse, err error) {
var resp string var resp string
if !isNil(rangeResponse) { if !isNil(rangeResponse) {
resp = fmt.Sprintf("range_response_count:%d size:%d", len(rangeResponse.Kvs), proto.Size(rangeResponse)) resp = fmt.Sprintf("range_response_count:%d size:%d", len(rangeResponse.Kvs), rangeResponse.Size())
} }
warnOfExpensiveGenericRequest(lg, now, reqStringer, "read-only range ", resp, err) warnOfExpensiveGenericRequest(lg, warningApplyDuration, now, reqStringer, "read-only range ", resp, err)
} }
func warnOfExpensiveGenericRequest(lg *zap.Logger, now time.Time, reqStringer fmt.Stringer, prefix string, resp string, err error) { func warnOfExpensiveGenericRequest(lg *zap.Logger, warningApplyDuration time.Duration, now time.Time, reqStringer fmt.Stringer, prefix string, resp string, err error) {
d := time.Since(now) d := time.Since(now)
if d > warnApplyDuration { if d > warningApplyDuration {
if lg != nil { if lg != nil {
lg.Warn( lg.Warn(
"apply request took too long", "apply request took too long",
zap.Duration("took", d), zap.Duration("took", d),
zap.Duration("expected-duration", warnApplyDuration), zap.Duration("expected-duration", warningApplyDuration),
zap.String("prefix", prefix), zap.String("prefix", prefix),
zap.String("request", reqStringer.String()), zap.String("request", reqStringer.String()),
zap.String("response", resp), zap.String("response", resp),
......
...@@ -97,7 +97,7 @@ func (s *EtcdServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeRe ...@@ -97,7 +97,7 @@ func (s *EtcdServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeRe
var resp *pb.RangeResponse var resp *pb.RangeResponse
var err error var err error
defer func(start time.Time) { defer func(start time.Time) {
warnOfExpensiveReadOnlyRangeRequest(s.getLogger(), start, r, resp, err) warnOfExpensiveReadOnlyRangeRequest(s.getLogger(), s.Cfg.WarningApplyDuration, start, r, resp, err)
if resp != nil { if resp != nil {
trace.AddField( trace.AddField(
traceutil.Field{Key: "response_count", Value: len(resp.Kvs)}, traceutil.Field{Key: "response_count", Value: len(resp.Kvs)},
...@@ -158,7 +158,7 @@ func (s *EtcdServer) Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse ...@@ -158,7 +158,7 @@ func (s *EtcdServer) Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse
} }
defer func(start time.Time) { defer func(start time.Time) {
warnOfExpensiveReadOnlyTxnRequest(s.getLogger(), start, r, resp, err) warnOfExpensiveReadOnlyTxnRequest(s.getLogger(), s.Cfg.WarningApplyDuration, start, r, resp, err)
}(time.Now()) }(time.Now())
get := func() { resp, err = s.applyV3Base.Txn(r) } get := func() { resp, err = s.applyV3Base.Txn(r) }
......
...@@ -354,6 +354,8 @@ func (b *backend) Defrag() error { ...@@ -354,6 +354,8 @@ func (b *backend) Defrag() error {
func (b *backend) defrag() error { func (b *backend) defrag() error {
now := time.Now() now := time.Now()
isDefragActive.Set(1)
defer isDefragActive.Set(0)
// TODO: make this non-blocking? // TODO: make this non-blocking?
// lock batchTx to ensure nobody is using previous tx, and then // lock batchTx to ensure nobody is using previous tx, and then
......
...@@ -83,6 +83,13 @@ var ( ...@@ -83,6 +83,13 @@ var (
// highest bucket start of 0.01 sec * 2^16 == 655.36 sec // highest bucket start of 0.01 sec * 2^16 == 655.36 sec
Buckets: prometheus.ExponentialBuckets(.01, 2, 17), Buckets: prometheus.ExponentialBuckets(.01, 2, 17),
}) })
isDefragActive = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: "etcd",
Subsystem: "disk",
Name: "defrag_inflight",
Help: "Whether or not defrag is active on the member. 1 means active, 0 means not.",
})
) )
func init() { func init() {
...@@ -92,4 +99,5 @@ func init() { ...@@ -92,4 +99,5 @@ func init() {
prometheus.MustRegister(writeSec) prometheus.MustRegister(writeSec)
prometheus.MustRegister(defragSec) prometheus.MustRegister(defragSec)
prometheus.MustRegister(snapshotTransferSec) prometheus.MustRegister(snapshotTransferSec)
prometheus.MustRegister(isDefragActive)
} }
...@@ -182,8 +182,8 @@ func (tw *storeTxnWrite) put(key, value []byte, leaseID lease.LeaseID) { ...@@ -182,8 +182,8 @@ func (tw *storeTxnWrite) put(key, value []byte, leaseID lease.LeaseID) {
if err == nil { if err == nil {
c = created.main c = created.main
oldLease = tw.s.le.GetLease(lease.LeaseItem{Key: string(key)}) oldLease = tw.s.le.GetLease(lease.LeaseItem{Key: string(key)})
tw.trace.Step("get key's previous created_revision and leaseID")
} }
tw.trace.Step("get key's previous created_revision and leaseID")
ibytes := newRevBytes() ibytes := newRevBytes()
idxRev := revision{main: rev, sub: int64(len(tw.changes))} idxRev := revision{main: rev, sub: int64(len(tw.changes))}
revToBytes(idxRev, ibytes) revToBytes(idxRev, ibytes)
......
...@@ -21,19 +21,14 @@ import ( ...@@ -21,19 +21,14 @@ import (
"io" "io"
"os" "os"
"syscall" "syscall"
"golang.org/x/sys/unix"
) )
// This used to call syscall.Flock() but that call fails with EBADF on NFS. // This used to call syscall.Flock() but that call fails with EBADF on NFS.
// An alternative is lockf() which works on NFS but that call lets a process lock // An alternative is lockf() which works on NFS but that call lets a process lock
// the same file twice. Instead, use Linux's non-standard open file descriptor // the same file twice. Instead, use Linux's non-standard open file descriptor
// locks which will block if the process already holds the file lock. // locks which will block if the process already holds the file lock.
//
// constants from /usr/include/bits/fcntl-linux.h
const (
F_OFD_GETLK = 37
F_OFD_SETLK = 37
F_OFD_SETLKW = 38
)
var ( var (
wrlck = syscall.Flock_t{ wrlck = syscall.Flock_t{
...@@ -50,7 +45,7 @@ var ( ...@@ -50,7 +45,7 @@ var (
func init() { func init() {
// use open file descriptor locks if the system supports it // use open file descriptor locks if the system supports it
getlk := syscall.Flock_t{Type: syscall.F_RDLCK} getlk := syscall.Flock_t{Type: syscall.F_RDLCK}
if err := syscall.FcntlFlock(0, F_OFD_GETLK, &getlk); err == nil { if err := syscall.FcntlFlock(0, unix.F_OFD_GETLK, &getlk); err == nil {
linuxTryLockFile = ofdTryLockFile linuxTryLockFile = ofdTryLockFile
linuxLockFile = ofdLockFile linuxLockFile = ofdLockFile
} }
...@@ -67,7 +62,7 @@ func ofdTryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error ...@@ -67,7 +62,7 @@ func ofdTryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error
} }
flock := wrlck flock := wrlck
if err = syscall.FcntlFlock(f.Fd(), F_OFD_SETLK, &flock); err != nil { if err = syscall.FcntlFlock(f.Fd(), unix.F_OFD_SETLK, &flock); err != nil {
f.Close() f.Close()
if err == syscall.EWOULDBLOCK { if err == syscall.EWOULDBLOCK {
err = ErrLocked err = ErrLocked
...@@ -88,7 +83,7 @@ func ofdLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) { ...@@ -88,7 +83,7 @@ func ofdLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
} }
flock := wrlck flock := wrlck
err = syscall.FcntlFlock(f.Fd(), F_OFD_SETLKW, &flock) err = syscall.FcntlFlock(f.Fd(), unix.F_OFD_SETLKW, &flock)
if err != nil { if err != nil {
f.Close() f.Close()
return nil, err return nil, err
......
// Copyright 2015 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package netutil
import (
"fmt"
"os/exec"
)
// DropPort drops all tcp packets that are received from the given port and sent to the given port.
func DropPort(port int) error {
cmdStr := fmt.Sprintf("sudo iptables -A OUTPUT -p tcp --destination-port %d -j DROP", port)
if _, err := exec.Command("/bin/sh", "-c", cmdStr).Output(); err != nil {
return err
}
cmdStr = fmt.Sprintf("sudo iptables -A INPUT -p tcp --destination-port %d -j DROP", port)
_, err := exec.Command("/bin/sh", "-c", cmdStr).Output()
return err
}
// RecoverPort stops dropping tcp packets at given port.
func RecoverPort(port int) error {
cmdStr := fmt.Sprintf("sudo iptables -D OUTPUT -p tcp --destination-port %d -j DROP", port)
if _, err := exec.Command("/bin/sh", "-c", cmdStr).Output(); err != nil {
return err
}
cmdStr = fmt.Sprintf("sudo iptables -D INPUT -p tcp --destination-port %d -j DROP", port)
_, err := exec.Command("/bin/sh", "-c", cmdStr).Output()
return err
}
// SetLatency adds latency in millisecond scale with random variations.
func SetLatency(ms, rv int) error {
ifces, err := GetDefaultInterfaces()
if err != nil {
return err
}
if rv > ms {
rv = 1
}
for ifce := range ifces {
cmdStr := fmt.Sprintf("sudo tc qdisc add dev %s root netem delay %dms %dms distribution normal", ifce, ms, rv)
_, err = exec.Command("/bin/sh", "-c", cmdStr).Output()
if err != nil {
// the rule has already been added. Overwrite it.
cmdStr = fmt.Sprintf("sudo tc qdisc change dev %s root netem delay %dms %dms distribution normal", ifce, ms, rv)
_, err = exec.Command("/bin/sh", "-c", cmdStr).Output()
if err != nil {
return err
}
}
}
return nil
}
// RemoveLatency resets latency configurations.
func RemoveLatency() error {
ifces, err := GetDefaultInterfaces()
if err != nil {
return err
}
for ifce := range ifces {
_, err = exec.Command("/bin/sh", "-c", fmt.Sprintf("sudo tc qdisc del dev %s root netem", ifce)).Output()
if err != nil {
return err
}
}
return nil
}
// Copyright 2015 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !linux
package netutil
func DropPort(port int) error { return nil }
func RecoverPort(port int) error { return nil }
func SetLatency(ms, rv int) error { return nil }
func RemoveLatency() error { return nil }
// +build !go1.15
// Copyright 2018 The etcd Authors // Copyright 2018 The etcd Authors
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
......
// +build go1.15
// Copyright 2018 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tlsutil
import "crypto/tls"
// GetCipherSuite returns the corresponding cipher suite,
// and boolean value if it is supported.
func GetCipherSuite(s string) (uint16, bool) {
for _, c := range tls.CipherSuites() {
if s == c.Name {
return c.ID, true
}
}
for _, c := range tls.InsecureCipherSuites() {
if s == c.Name {
return c.ID, true
}
}
switch s {
case "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305":
return tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, true
case "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":
return tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, true
}
return 0, false
}
...@@ -26,7 +26,7 @@ import ( ...@@ -26,7 +26,7 @@ import (
var ( var (
// MinClusterVersion is the min cluster version this etcd binary is compatible with. // MinClusterVersion is the min cluster version this etcd binary is compatible with.
MinClusterVersion = "3.0.0" MinClusterVersion = "3.0.0"
Version = "3.4.13" Version = "3.4.18"
APIVersion = "unknown" APIVersion = "unknown"
// Git SHA Value will be set during build // Git SHA Value will be set during build
......
...@@ -789,14 +789,16 @@ func (w *WAL) cut() error { ...@@ -789,14 +789,16 @@ func (w *WAL) cut() error {
} }
func (w *WAL) sync() error { func (w *WAL) sync() error {
if w.unsafeNoSync {
return nil
}
if w.encoder != nil { if w.encoder != nil {
if err := w.encoder.flush(); err != nil { if err := w.encoder.flush(); err != nil {
return err return err
} }
} }
if w.unsafeNoSync {
return nil
}
start := time.Now() start := time.Now()
err := fileutil.Fdatasync(w.tail().File) err := fileutil.Fdatasync(w.tail().File)
......
...@@ -422,8 +422,6 @@ github.com/cyphar/filepath-securejoin ...@@ -422,8 +422,6 @@ github.com/cyphar/filepath-securejoin
github.com/davecgh/go-spew/spew github.com/davecgh/go-spew/spew
# github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd # github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd
github.com/daviddengcn/go-colortext github.com/daviddengcn/go-colortext
# github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/dgrijalva/jwt-go
# github.com/docker/distribution v2.7.1+incompatible => github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible # github.com/docker/distribution v2.7.1+incompatible => github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible
github.com/docker/distribution/digestset github.com/docker/distribution/digestset
github.com/docker/distribution/reference github.com/docker/distribution/reference
...@@ -535,6 +533,8 @@ github.com/gogo/protobuf/proto ...@@ -535,6 +533,8 @@ github.com/gogo/protobuf/proto
github.com/gogo/protobuf/protoc-gen-gogo/descriptor github.com/gogo/protobuf/protoc-gen-gogo/descriptor
github.com/gogo/protobuf/sortkeys github.com/gogo/protobuf/sortkeys
github.com/gogo/protobuf/types github.com/gogo/protobuf/types
# github.com/golang-jwt/jwt v3.2.1+incompatible
github.com/golang-jwt/jwt
# github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e # github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e
github.com/golang/groupcache/lru github.com/golang/groupcache/lru
# github.com/golang/mock v1.4.1 # github.com/golang/mock v1.4.1
...@@ -994,7 +994,7 @@ github.com/syndtr/gocapability/capability ...@@ -994,7 +994,7 @@ github.com/syndtr/gocapability/capability
# github.com/tchap/go-patricia v2.3.0+incompatible # github.com/tchap/go-patricia v2.3.0+incompatible
## explicit ## explicit
github.com/tchap/go-patricia/patricia github.com/tchap/go-patricia/patricia
# github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 # github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966
github.com/tmc/grpc-websocket-proxy/wsproxy github.com/tmc/grpc-websocket-proxy/wsproxy
# github.com/urfave/cli v1.22.2 # github.com/urfave/cli v1.22.2
## explicit ## explicit
...@@ -1039,7 +1039,7 @@ github.com/willf/bitset ...@@ -1039,7 +1039,7 @@ github.com/willf/bitset
github.com/xiang90/probing github.com/xiang90/probing
# go.etcd.io/bbolt v1.3.5 # go.etcd.io/bbolt v1.3.5
go.etcd.io/bbolt go.etcd.io/bbolt
# go.etcd.io/etcd v0.5.0-alpha.5.0.20201208200253-50621aee4aea => github.com/k3s-io/etcd v0.5.0-alpha.5.0.20201208200253-50621aee4aea # go.etcd.io/etcd v0.5.0-alpha.5.0.20201208200253-50621aee4aea => github.com/k3s-io/etcd v0.5.0-alpha.5.0.20220113195313-6c2233a709e8
## explicit ## explicit
go.etcd.io/etcd/auth go.etcd.io/etcd/auth
go.etcd.io/etcd/auth/authpb go.etcd.io/etcd/auth/authpb
...@@ -3037,7 +3037,7 @@ sigs.k8s.io/yaml ...@@ -3037,7 +3037,7 @@ sigs.k8s.io/yaml
# github.com/opencontainers/runtime-spec => github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6 # github.com/opencontainers/runtime-spec => github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6
# github.com/rancher/k3s/pkg/data => ./pkg/data # github.com/rancher/k3s/pkg/data => ./pkg/data
# github.com/rancher/wrangler => github.com/rancher/wrangler v0.6.2 # github.com/rancher/wrangler => github.com/rancher/wrangler v0.6.2
# go.etcd.io/etcd => github.com/k3s-io/etcd v0.5.0-alpha.5.0.20201208200253-50621aee4aea # go.etcd.io/etcd => github.com/k3s-io/etcd v0.5.0-alpha.5.0.20220113195313-6c2233a709e8
# golang.org/x/crypto => golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 # golang.org/x/crypto => golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
# golang.org/x/net => golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 # golang.org/x/net => golang.org/x/net v0.0.0-20210224082022-3d97a244fca7
# golang.org/x/sys => golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 # golang.org/x/sys => golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment