vendor: update buildkit to 664c2b469f19

Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax 2024-07-31 16:47:50 +02:00
parent 38a8261f05
commit 4b27fb3022
No known key found for this signature in database
GPG Key ID: ADE44D8C9D44FBE4
82 changed files with 2517 additions and 1981 deletions

25
go.mod
View File

@ -8,7 +8,7 @@ require (
github.com/aws/aws-sdk-go-v2/config v1.26.6 github.com/aws/aws-sdk-go-v2/config v1.26.6
github.com/compose-spec/compose-go/v2 v2.1.6 github.com/compose-spec/compose-go/v2 v2.1.6
github.com/containerd/console v1.0.4 github.com/containerd/console v1.0.4
github.com/containerd/containerd v1.7.19 github.com/containerd/containerd v1.7.20
github.com/containerd/continuity v0.4.3 github.com/containerd/continuity v0.4.3
github.com/containerd/errdefs v0.1.0 github.com/containerd/errdefs v0.1.0
github.com/containerd/log v0.1.0 github.com/containerd/log v0.1.0
@ -16,9 +16,9 @@ require (
github.com/containerd/typeurl/v2 v2.1.1 github.com/containerd/typeurl/v2 v2.1.1
github.com/creack/pty v1.1.21 github.com/creack/pty v1.1.21
github.com/distribution/reference v0.6.0 github.com/distribution/reference v0.6.0
github.com/docker/cli v27.0.3+incompatible github.com/docker/cli v27.1.1+incompatible
github.com/docker/cli-docs-tool v0.8.0 github.com/docker/cli-docs-tool v0.8.0
github.com/docker/docker v27.0.3+incompatible github.com/docker/docker v27.1.1+incompatible
github.com/docker/go-units v0.5.0 github.com/docker/go-units v0.5.0
github.com/gofrs/flock v0.12.1 github.com/gofrs/flock v0.12.1
github.com/gogo/protobuf v1.3.2 github.com/gogo/protobuf v1.3.2
@ -29,7 +29,7 @@ require (
github.com/hashicorp/hcl/v2 v2.20.1 github.com/hashicorp/hcl/v2 v2.20.1
github.com/in-toto/in-toto-golang v0.5.0 github.com/in-toto/in-toto-golang v0.5.0
github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/moby/buildkit v0.15.1 github.com/moby/buildkit v0.15.1-0.20240810140024-664c2b469f19
github.com/moby/sys/mountinfo v0.7.1 github.com/moby/sys/mountinfo v0.7.1
github.com/moby/sys/signal v0.7.0 github.com/moby/sys/signal v0.7.0
github.com/morikuni/aec v1.0.0 github.com/morikuni/aec v1.0.0
@ -54,7 +54,7 @@ require (
golang.org/x/sys v0.22.0 golang.org/x/sys v0.22.0
golang.org/x/term v0.20.0 golang.org/x/term v0.20.0
golang.org/x/text v0.15.0 golang.org/x/text v0.15.0
google.golang.org/grpc v1.59.0 google.golang.org/grpc v1.60.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.29.2 k8s.io/api v0.29.2
k8s.io/apimachinery v0.29.2 k8s.io/apimachinery v0.29.2
@ -128,7 +128,7 @@ require (
github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/spdystream v0.2.0 // indirect github.com/moby/spdystream v0.2.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/user v0.1.0 // indirect github.com/moby/sys/user v0.3.0 // indirect
github.com/moby/term v0.5.0 // indirect github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
@ -152,9 +152,8 @@ require (
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.42.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 // indirect
@ -163,13 +162,13 @@ require (
golang.org/x/crypto v0.23.0 // indirect golang.org/x/crypto v0.23.0 // indirect
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
golang.org/x/net v0.25.0 // indirect golang.org/x/net v0.25.0 // indirect
golang.org/x/oauth2 v0.11.0 // indirect golang.org/x/oauth2 v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.17.0 // indirect golang.org/x/tools v0.17.0 // indirect
google.golang.org/appengine v1.6.8 // indirect google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect
google.golang.org/protobuf v1.33.0 // indirect google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect

64
go.sum
View File

@ -1,6 +1,6 @@
cloud.google.com/go v0.110.8 h1:tyNdfIxjzaWctIiLYOTalaLKZ17SI44SKFW26QbOhME= cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y=
cloud.google.com/go/compute v1.23.1 h1:V97tBoDaZHb6leicZ1G6DLK2BAaZLJ/7+9BB/En3hR0= cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78= cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
@ -90,8 +90,8 @@ github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaD
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/containerd v1.7.19 h1:/xQ4XRJ0tamDkdzrrBAUy/LE5nCcxFKdBm4EcPrSMEE= github.com/containerd/containerd v1.7.20 h1:Sl6jQYk3TRavaU83h66QMbI2Nqg9Jm6qzwX57Vsn1SQ=
github.com/containerd/containerd v1.7.19/go.mod h1:h4FtNYUUMB4Phr6v+xG89RYKj9XccvbNSCKjdufCrkc= github.com/containerd/containerd v1.7.20/go.mod h1:52GsS5CwquuqPuLncsXwG0t2CiUce+KsNHJZQJvAgR0=
github.com/containerd/containerd/api v1.7.19 h1:VWbJL+8Ap4Ju2mx9c9qS1uFSB1OVYr5JJrW2yT5vFoA= github.com/containerd/containerd/api v1.7.19 h1:VWbJL+8Ap4Ju2mx9c9qS1uFSB1OVYr5JJrW2yT5vFoA=
github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig= github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig=
github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8=
@ -102,8 +102,8 @@ github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY
github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o= github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/nydus-snapshotter v0.13.7 h1:x7DHvGnzJOu1ZPwPYkeOPk5MjZZYbdddygEjaSDoFTk= github.com/containerd/nydus-snapshotter v0.14.0 h1:6/eAi6d7MjaeLLuMO8Udfe5GVsDudmrDNO4SGETMBco=
github.com/containerd/nydus-snapshotter v0.13.7/go.mod h1:VPVKQ3jmHFIcUIV2yiQ1kImZuBFS3GXDohKs9mRABVE= github.com/containerd/nydus-snapshotter v0.14.0/go.mod h1:TT4jv2SnIDxEBu4H2YOvWQHPOap031ydTaHTuvc5VQk=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/containerd/stargz-snapshotter v0.15.1 h1:fpsP4kf/Z4n2EYnU0WT8ZCE3eiKDwikDhL6VwxIlgeA= github.com/containerd/stargz-snapshotter v0.15.1 h1:fpsP4kf/Z4n2EYnU0WT8ZCE3eiKDwikDhL6VwxIlgeA=
@ -124,15 +124,15 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v27.0.3+incompatible h1:usGs0/BoBW8MWxGeEtqPMkzOY56jZ6kYlSN5BLDioCQ= github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE=
github.com/docker/cli v27.0.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli-docs-tool v0.8.0 h1:YcDWl7rQJC3lJ7WVZRwSs3bc9nka97QLWfyJQli8yJU= github.com/docker/cli-docs-tool v0.8.0 h1:YcDWl7rQJC3lJ7WVZRwSs3bc9nka97QLWfyJQli8yJU=
github.com/docker/cli-docs-tool v0.8.0/go.mod h1:8TQQ3E7mOXoYUs811LiPdUnAhXrcVsBIrW21a5pUbdk= github.com/docker/cli-docs-tool v0.8.0/go.mod h1:8TQQ3E7mOXoYUs811LiPdUnAhXrcVsBIrW21a5pUbdk=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v27.0.3+incompatible h1:aBGI9TeQ4MPlhquTQKq9XbK79rKFVwXNUAYz9aXyEBE= github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
github.com/docker/docker v27.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
@ -307,8 +307,8 @@ github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/z
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/buildkit v0.15.1 h1:J6wrew7hphKqlq1wuu6yaUb/1Ra7gEzDAovylGztAKM= github.com/moby/buildkit v0.15.1-0.20240810140024-664c2b469f19 h1:0T8RSjj+Li33TiaWxzxUPJ15kGSuOwSjkiL4H86v/Tc=
github.com/moby/buildkit v0.15.1/go.mod h1:Yis8ZMUJTHX9XhH9zVyK2igqSHV3sxi3UN0uztZocZk= github.com/moby/buildkit v0.15.1-0.20240810140024-664c2b469f19/go.mod h1:uJOz8k3rWgVrcMPArjtz85jrwOWD4MaLBzk64mCe1yY=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
@ -323,8 +323,8 @@ github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI= github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI=
github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg=
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -395,8 +395,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE= github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE=
@ -477,12 +477,10 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJ
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo=
go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 h1:ZtfnDL+tUrs1F0Pzfwbg2d59Gru9NCH3bgSHBM6LDwU= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 h1:jd0+5t/YynESZqsSyPz+7PAFdEop0dlN0+PkyHYo8oI=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0/go.mod h1:hG4Fj/y8TR/tlEDREo8tWstl9fO9gcFkn4xrx0Io8xU= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0/go.mod h1:U707O40ee1FpQGyhvqnzmCJm1Wh6OX6GGBVn0E6Uyyk=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 h1:NmnYCiR0qNufkldjVvyQfZTHSdzeHoZ41zggMsdMcLM= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 h1:bflGWrfYyuulcdxf14V6n9+CoQcu5SAAdHmDPAJnlps=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0/go.mod h1:UVAO61+umUsHLtYb8KXXRoHtxUkdOPkYidzW3gipRLQ= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0/go.mod h1:qcTO4xHAxZLaLxPd60TdE88rxtItPHgHWqOhOGRr0as=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.42.0 h1:wNMDy/LVGLj2h3p6zg4d0gypKfWKSWI14E1C4smOgl8=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.42.0/go.mod h1:YfbDdXAAkemWJK3H/DshvlrxqFB2rtW4rY6ky/3x/H0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk=
@ -530,8 +528,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -588,15 +586,15 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b h1:+YaDE2r2OG8t/z5qmsh7Y+XXwCbvadxxZ0YY6mTdrVA= google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 h1:1hfbdAfFbkmpg41000wDVqr7jUpK/Yo+LPnIxxGzmkg=
google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic=
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k= google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f h1:2yNACc1O40tTnrsbk9Cv6oxiW8pxI/pXj0wRtdlYmgY=
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo= google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA=
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 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.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=

View File

@ -23,7 +23,7 @@ var (
Package = "github.com/containerd/containerd" Package = "github.com/containerd/containerd"
// Version holds the complete version number. Filled in at linking time. // Version holds the complete version number. Filled in at linking time.
Version = "1.7.19+unknown" Version = "1.7.20+unknown"
// Revision is filled with the VCS (e.g. git) revision being used to build // Revision is filled with the VCS (e.g. git) revision being used to build
// the program at linking time. // the program at linking time.

View File

@ -324,7 +324,7 @@ func newAPIClientFromEndpoint(ep docker.Endpoint, configFile *configfile.ConfigF
if len(configFile.HTTPHeaders) > 0 { if len(configFile.HTTPHeaders) > 0 {
opts = append(opts, client.WithHTTPHeaders(configFile.HTTPHeaders)) opts = append(opts, client.WithHTTPHeaders(configFile.HTTPHeaders))
} }
opts = append(opts, client.WithUserAgent(UserAgent())) opts = append(opts, withCustomHeadersFromEnv(), client.WithUserAgent(UserAgent()))
return client.NewClientWithOpts(opts...) return client.NewClientWithOpts(opts...)
} }

View File

@ -2,13 +2,18 @@ package command
import ( import (
"context" "context"
"encoding/csv"
"io" "io"
"net/http"
"os" "os"
"strconv" "strconv"
"strings"
"github.com/docker/cli/cli/streams" "github.com/docker/cli/cli/streams"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/docker/docker/errdefs"
"github.com/moby/term" "github.com/moby/term"
"github.com/pkg/errors"
) )
// CLIOption is a functional argument to apply options to a [DockerCli]. These // CLIOption is a functional argument to apply options to a [DockerCli]. These
@ -108,3 +113,107 @@ func WithAPIClient(c client.APIClient) CLIOption {
return nil return nil
} }
} }
// envOverrideHTTPHeaders is the name of the environment-variable that can be
// used to set custom HTTP headers to be sent by the client. This environment
// variable is the equivalent to the HttpHeaders field in the configuration
// file.
//
// WARNING: If both config and environment-variable are set, the environment
// variable currently overrides all headers set in the configuration file.
// This behavior may change in a future update, as we are considering the
// environment variable to be appending to existing headers (and to only
// override headers with the same name).
//
// While this env-var allows for custom headers to be set, it does not allow
// for built-in headers (such as "User-Agent", if set) to be overridden.
// Also see [client.WithHTTPHeaders] and [client.WithUserAgent].
//
// This environment variable can be used in situations where headers must be
// set for a specific invocation of the CLI, but should not be set by default,
// and therefore cannot be set in the config-file.
//
// envOverrideHTTPHeaders accepts a comma-separated (CSV) list of key=value pairs,
// where key must be a non-empty, valid MIME header format. Whitespaces surrounding
// the key are trimmed, and the key is normalised. Whitespaces in values are
// preserved, but "key=value" pairs with an empty value (e.g. "key=") are ignored.
// Tuples without a "=" produce an error.
//
// It follows CSV rules for escaping, allowing "key=value" pairs to be quoted
// if they must contain commas, which allows for multiple values for a single
// header to be set. If a key is repeated in the list, later values override
// prior values.
//
// For example, the following value:
//
// one=one-value,"two=two,value","three= a value with whitespace ",four=,five=five=one,five=five-two
//
// Produces four headers (four is omitted as it has an empty value set):
//
// - one (value is "one-value")
// - two (value is "two,value")
// - three (value is " a value with whitespace ")
// - five (value is "five-two", the later value has overridden the prior value)
const envOverrideHTTPHeaders = "DOCKER_CUSTOM_HEADERS"
// withCustomHeadersFromEnv overriding custom HTTP headers to be sent by the
// client through the [envOverrideHTTPHeaders] environment-variable. This
// environment variable is the equivalent to the HttpHeaders field in the
// configuration file.
//
// WARNING: If both config and environment-variable are set, the environment-
// variable currently overrides all headers set in the configuration file.
// This behavior may change in a future update, as we are considering the
// environment-variable to be appending to existing headers (and to only
// override headers with the same name).
//
// TODO(thaJeztah): this is a client Option, and should be moved to the client. It is non-exported for that reason.
func withCustomHeadersFromEnv() client.Opt {
return func(apiClient *client.Client) error {
value := os.Getenv(envOverrideHTTPHeaders)
if value == "" {
return nil
}
csvReader := csv.NewReader(strings.NewReader(value))
fields, err := csvReader.Read()
if err != nil {
return errdefs.InvalidParameter(errors.Errorf("failed to parse custom headers from %s environment variable: value must be formatted as comma-separated key=value pairs", envOverrideHTTPHeaders))
}
if len(fields) == 0 {
return nil
}
env := map[string]string{}
for _, kv := range fields {
k, v, hasValue := strings.Cut(kv, "=")
// Only strip whitespace in keys; preserve whitespace in values.
k = strings.TrimSpace(k)
if k == "" {
return errdefs.InvalidParameter(errors.Errorf(`failed to set custom headers from %s environment variable: value contains a key=value pair with an empty key: '%s'`, envOverrideHTTPHeaders, kv))
}
// We don't currently allow empty key=value pairs, and produce an error.
// This is something we could allow in future (e.g. to read value
// from an environment variable with the same name). In the meantime,
// produce an error to prevent users from depending on this.
if !hasValue {
return errdefs.InvalidParameter(errors.Errorf(`failed to set custom headers from %s environment variable: missing "=" in key=value pair: '%s'`, envOverrideHTTPHeaders, kv))
}
env[http.CanonicalHeaderKey(k)] = v
}
if len(env) == 0 {
// We should probably not hit this case, as we don't skip values
// (only return errors), but we don't want to discard existing
// headers with an empty set.
return nil
}
// TODO(thaJeztah): add a client.WithExtraHTTPHeaders() function to allow these headers to be _added_ to existing ones, instead of _replacing_
// see https://github.com/docker/cli/pull/5098#issuecomment-2147403871 (when updating, also update the WARNING in the function and env-var GoDoc)
return client.WithHTTPHeaders(env)(apiClient)
}
}

View File

@ -1,10 +1,8 @@
package command package command
import ( import (
"bufio"
"context" "context"
"fmt" "fmt"
"io"
"os" "os"
"runtime" "runtime"
"strings" "strings"
@ -18,7 +16,6 @@ import (
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
registrytypes "github.com/docker/docker/api/types/registry" registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/registry" "github.com/docker/docker/registry"
"github.com/moby/term"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -44,7 +41,7 @@ func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInf
default: default:
} }
err = ConfigureAuth(cli, "", "", &authConfig, isDefaultRegistry) err = ConfigureAuth(ctx, cli, "", "", &authConfig, isDefaultRegistry)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -90,7 +87,7 @@ func GetDefaultAuthConfig(cfg *configfile.ConfigFile, checkCredStore bool, serve
} }
// ConfigureAuth handles prompting of user's username and password if needed // ConfigureAuth handles prompting of user's username and password if needed
func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *registrytypes.AuthConfig, isDefaultRegistry bool) error { func ConfigureAuth(ctx context.Context, cli Cli, flUser, flPassword string, authconfig *registrytypes.AuthConfig, isDefaultRegistry bool) error {
// On Windows, force the use of the regular OS stdin stream. // On Windows, force the use of the regular OS stdin stream.
// //
// See: // See:
@ -125,9 +122,15 @@ func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *registrytypes
fmt.Fprintln(cli.Out()) fmt.Fprintln(cli.Out())
} }
} }
promptWithDefault(cli.Out(), "Username", authconfig.Username)
var prompt string
if authconfig.Username == "" {
prompt = "Username: "
} else {
prompt = fmt.Sprintf("Username (%s): ", authconfig.Username)
}
var err error var err error
flUser, err = readInput(cli.In()) flUser, err = PromptForInput(ctx, cli.In(), cli.Out(), prompt)
if err != nil { if err != nil {
return err return err
} }
@ -139,16 +142,13 @@ func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *registrytypes
return errors.Errorf("Error: Non-null Username Required") return errors.Errorf("Error: Non-null Username Required")
} }
if flPassword == "" { if flPassword == "" {
oldState, err := term.SaveState(cli.In().FD()) restoreInput, err := DisableInputEcho(cli.In())
if err != nil { if err != nil {
return err return err
} }
fmt.Fprintf(cli.Out(), "Password: ") defer restoreInput()
_ = term.DisableEcho(cli.In().FD(), oldState)
defer func() { flPassword, err = PromptForInput(ctx, cli.In(), cli.Out(), "Password: ")
_ = term.RestoreTerminal(cli.In().FD(), oldState)
}()
flPassword, err = readInput(cli.In())
if err != nil { if err != nil {
return err return err
} }
@ -164,25 +164,6 @@ func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *registrytypes
return nil return nil
} }
// readInput reads, and returns user input from in. It tries to return a
// single line, not including the end-of-line bytes, and trims leading
// and trailing whitespace.
func readInput(in io.Reader) (string, error) {
line, _, err := bufio.NewReader(in).ReadLine()
if err != nil {
return "", errors.Wrap(err, "error while reading input")
}
return strings.TrimSpace(string(line)), nil
}
func promptWithDefault(out io.Writer, prompt string, configDefault string) {
if configDefault == "" {
fmt.Fprintf(out, "%s: ", prompt)
} else {
fmt.Fprintf(out, "%s (%s): ", prompt, configDefault)
}
}
// RetrieveAuthTokenFromImage retrieves an encoded auth token given a complete // RetrieveAuthTokenFromImage retrieves an encoded auth token given a complete
// image. The auth configuration is serialized as a base64url encoded RFC4648, // image. The auth configuration is serialized as a base64url encoded RFC4648,
// section 5) JSON string for sending through the X-Registry-Auth header. // section 5) JSON string for sending through the X-Registry-Auth header.

View File

@ -19,6 +19,7 @@ import (
"github.com/docker/docker/api/types/versions" "github.com/docker/docker/api/types/versions"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
"github.com/moby/sys/sequential" "github.com/moby/sys/sequential"
"github.com/moby/term"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
@ -76,6 +77,48 @@ func PrettyPrint(i any) string {
var ErrPromptTerminated = errdefs.Cancelled(errors.New("prompt terminated")) var ErrPromptTerminated = errdefs.Cancelled(errors.New("prompt terminated"))
// DisableInputEcho disables input echo on the provided streams.In.
// This is useful when the user provides sensitive information like passwords.
// The function returns a restore function that should be called to restore the
// terminal state.
func DisableInputEcho(ins *streams.In) (restore func() error, err error) {
oldState, err := term.SaveState(ins.FD())
if err != nil {
return nil, err
}
restore = func() error {
return term.RestoreTerminal(ins.FD(), oldState)
}
return restore, term.DisableEcho(ins.FD(), oldState)
}
// PromptForInput requests input from the user.
//
// If the user terminates the CLI with SIGINT or SIGTERM while the prompt is
// active, the prompt will return an empty string ("") with an ErrPromptTerminated error.
// When the prompt returns an error, the caller should propagate the error up
// the stack and close the io.Reader used for the prompt which will prevent the
// background goroutine from blocking indefinitely.
func PromptForInput(ctx context.Context, in io.Reader, out io.Writer, message string) (string, error) {
_, _ = fmt.Fprint(out, message)
result := make(chan string)
go func() {
scanner := bufio.NewScanner(in)
if scanner.Scan() {
result <- strings.TrimSpace(scanner.Text())
}
}()
select {
case <-ctx.Done():
_, _ = fmt.Fprintln(out, "")
return "", ErrPromptTerminated
case r := <-result:
return r, nil
}
}
// PromptForConfirmation requests and checks confirmation from the user. // PromptForConfirmation requests and checks confirmation from the user.
// This will display the provided message followed by ' [y/N] '. If the user // This will display the provided message followed by ' [y/N] '. If the user
// input 'y' or 'Y' it returns true otherwise false. If no message is provided, // input 'y' or 'Y' it returns true otherwise false. If no message is provided,

View File

@ -303,6 +303,7 @@ func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig,
for registryHostname := range configFile.CredentialHelpers { for registryHostname := range configFile.CredentialHelpers {
newAuth, err := configFile.GetAuthConfig(registryHostname) newAuth, err := configFile.GetAuthConfig(registryHostname)
if err != nil { if err != nil {
// TODO(thaJeztah): use context-logger, so that this output can be suppressed (in tests).
logrus.WithError(err).Warnf("Failed to get credentials for registry: %s", registryHostname) logrus.WithError(err).Warnf("Failed to get credentials for registry: %s", registryHostname)
continue continue
} }

View File

@ -124,6 +124,9 @@ func (s *ContextStore) List() ([]Metadata, error) {
// Names return Metadata names for a Lister // Names return Metadata names for a Lister
func Names(s Lister) ([]string, error) { func Names(s Lister) ([]string, error) {
if s == nil {
return nil, errors.New("nil lister")
}
list, err := s.List() list, err := s.List()
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -149,6 +149,7 @@ func ConvertPortToPortConfig(
for _, binding := range portBindings[port] { for _, binding := range portBindings[port] {
if p := net.ParseIP(binding.HostIP); p != nil && !p.IsUnspecified() { if p := net.ParseIP(binding.HostIP); p != nil && !p.IsUnspecified() {
// TODO(thaJeztah): use context-logger, so that this output can be suppressed (in tests).
logrus.Warnf("ignoring IP-address (%s:%s) service will listen on '0.0.0.0'", net.JoinHostPort(binding.HostIP, binding.HostPort), port) logrus.Warnf("ignoring IP-address (%s:%s) service will listen on '0.0.0.0'", net.JoinHostPort(binding.HostIP, binding.HostPort), port)
} }

View File

@ -5334,7 +5334,7 @@ definitions:
The version Go used to compile the daemon, and the version of the Go The version Go used to compile the daemon, and the version of the Go
runtime in use. runtime in use.
type: "string" type: "string"
example: "go1.21.11" example: "go1.21.12"
Os: Os:
description: | description: |
The operating system that the daemon is running on ("linux" or "windows") The operating system that the daemon is running on ("linux" or "windows")
@ -9563,7 +9563,7 @@ paths:
Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `exec_die`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, `update`, and `prune` Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `exec_die`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, `update`, and `prune`
Images report these events: `create, `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, `untag`, and `prune` Images report these events: `create`, `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, `untag`, and `prune`
Volumes report these events: `create`, `mount`, `unmount`, `destroy`, and `prune` Volumes report these events: `create`, `mount`, `unmount`, `destroy`, and `prune`

View File

@ -77,9 +77,6 @@ type Info struct {
Containerd *ContainerdInfo `json:",omitempty"` Containerd *ContainerdInfo `json:",omitempty"`
// Legacy API fields for older API versions.
legacyFields
// Warnings contains a slice of warnings that occurred while collecting // Warnings contains a slice of warnings that occurred while collecting
// system information. These warnings are intended to be informational // system information. These warnings are intended to be informational
// messages for the user, and are not intended to be parsed / used for // messages for the user, and are not intended to be parsed / used for
@ -124,10 +121,6 @@ type ContainerdNamespaces struct {
Plugins string Plugins string
} }
type legacyFields struct {
ExecutionDriver string `json:",omitempty"` // Deprecated: deprecated since API v1.25, but returned for older versions.
}
// PluginsInfo is a temp struct holding Plugins name // PluginsInfo is a temp struct holding Plugins name
// registered with docker daemon. It is used by [Info] struct // registered with docker daemon. It is used by [Info] struct
type PluginsInfo struct { type PluginsInfo struct {

View File

@ -1520,6 +1520,7 @@ type BuildHistoryRecord struct {
NumTotalSteps int32 `protobuf:"varint,16,opt,name=numTotalSteps,proto3" json:"numTotalSteps,omitempty"` NumTotalSteps int32 `protobuf:"varint,16,opt,name=numTotalSteps,proto3" json:"numTotalSteps,omitempty"`
NumCompletedSteps int32 `protobuf:"varint,17,opt,name=numCompletedSteps,proto3" json:"numCompletedSteps,omitempty"` NumCompletedSteps int32 `protobuf:"varint,17,opt,name=numCompletedSteps,proto3" json:"numCompletedSteps,omitempty"`
ExternalError *Descriptor `protobuf:"bytes,18,opt,name=externalError,proto3" json:"externalError,omitempty"` ExternalError *Descriptor `protobuf:"bytes,18,opt,name=externalError,proto3" json:"externalError,omitempty"`
NumWarnings int32 `protobuf:"varint,19,opt,name=numWarnings,proto3" json:"numWarnings,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -1684,6 +1685,13 @@ func (m *BuildHistoryRecord) GetExternalError() *Descriptor {
return nil return nil
} }
func (m *BuildHistoryRecord) GetNumWarnings() int32 {
if m != nil {
return m.NumWarnings
}
return 0
}
type UpdateBuildHistoryRequest struct { type UpdateBuildHistoryRequest struct {
Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"`
Pinned bool `protobuf:"varint,2,opt,name=Pinned,proto3" json:"Pinned,omitempty"` Pinned bool `protobuf:"varint,2,opt,name=Pinned,proto3" json:"Pinned,omitempty"`
@ -2025,154 +2033,155 @@ func init() {
func init() { proto.RegisterFile("control.proto", fileDescriptor_0c5120591600887d) } func init() { proto.RegisterFile("control.proto", fileDescriptor_0c5120591600887d) }
var fileDescriptor_0c5120591600887d = []byte{ var fileDescriptor_0c5120591600887d = []byte{
// 2340 bytes of a gzipped FileDescriptorProto // 2354 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x19, 0x4d, 0x73, 0x1b, 0x59, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x19, 0x4d, 0x73, 0x1b, 0x59,
0x31, 0x23, 0xc9, 0xb2, 0xd4, 0x92, 0x1c, 0xf9, 0x25, 0x1b, 0x86, 0x21, 0x6b, 0x3b, 0xb3, 0x09, 0x31, 0x23, 0xc9, 0xb2, 0xd4, 0x92, 0x1c, 0xf9, 0x25, 0x1b, 0x86, 0x21, 0x6b, 0x3b, 0xb3, 0x09,
0xb8, 0x42, 0x32, 0xf2, 0x0a, 0x42, 0xb2, 0x0e, 0x84, 0x58, 0x96, 0xd8, 0x38, 0xc4, 0x15, 0xef, 0xb8, 0x42, 0x32, 0xf2, 0x0a, 0x42, 0xb2, 0x0e, 0x84, 0x58, 0x96, 0xd8, 0x38, 0xc4, 0x15, 0xef,
0xb3, 0xb3, 0xa1, 0xb6, 0x6a, 0xa1, 0xc6, 0xd2, 0xb3, 0x32, 0xe5, 0xd1, 0xcc, 0xf0, 0xde, 0x93, 0xb3, 0xb3, 0xa1, 0xb6, 0x6a, 0xa1, 0xc6, 0xd2, 0xb3, 0x32, 0xe5, 0xd1, 0xcc, 0xf0, 0xde, 0x93,
0x37, 0xe2, 0xc4, 0x89, 0x2a, 0x2e, 0x14, 0x17, 0x8a, 0x0b, 0x77, 0x4e, 0x9c, 0x39, 0x71, 0xe0, 0x37, 0xe2, 0xc4, 0x89, 0x2a, 0x2e, 0x14, 0x17, 0x8a, 0x0b, 0x77, 0x4e, 0x9c, 0x39, 0x73, 0xa0,
0x40, 0x55, 0x8e, 0x9c, 0xf7, 0x10, 0xa8, 0xfc, 0x00, 0x8a, 0x23, 0x47, 0xea, 0x7d, 0x8c, 0x34, 0x2a, 0x47, 0xce, 0x7b, 0xc8, 0x52, 0xf9, 0x01, 0x14, 0x47, 0x8e, 0xd4, 0xfb, 0x18, 0x69, 0x24,
0x92, 0x46, 0xb6, 0x9c, 0xe4, 0xa4, 0xd7, 0xef, 0x75, 0xf7, 0x74, 0xf7, 0xeb, 0xee, 0xd7, 0xdd, 0x8d, 0x6c, 0x39, 0xc9, 0x49, 0xaf, 0xdf, 0xeb, 0xee, 0xe9, 0xee, 0xd7, 0xdd, 0xaf, 0xbb, 0x05,
0x82, 0x4a, 0x3b, 0x0c, 0x38, 0x0d, 0x7d, 0x27, 0xa2, 0x21, 0x0f, 0x51, 0xb5, 0x17, 0x1e, 0x0e, 0x95, 0x76, 0x18, 0x70, 0x1a, 0xfa, 0x4e, 0x44, 0x43, 0x1e, 0xa2, 0x6a, 0x2f, 0x3c, 0x1c, 0x38,
0x9c, 0xc3, 0xbe, 0xe7, 0x77, 0x8e, 0x3d, 0xee, 0x9c, 0x7c, 0x6c, 0xd5, 0xbb, 0x1e, 0x7f, 0xd1, 0x87, 0x7d, 0xcf, 0xef, 0x1c, 0x7b, 0xdc, 0x39, 0xf9, 0xd8, 0xaa, 0x77, 0x3d, 0xfe, 0xa2, 0x7f,
0x3f, 0x74, 0xda, 0x61, 0xaf, 0xd6, 0x0d, 0xbb, 0x61, 0xad, 0x1b, 0x86, 0x5d, 0x9f, 0xb8, 0x91, 0xe8, 0xb4, 0xc3, 0x5e, 0xad, 0x1b, 0x76, 0xc3, 0x5a, 0x37, 0x0c, 0xbb, 0x3e, 0x71, 0x23, 0x8f,
0xc7, 0xf4, 0xb2, 0x46, 0xa3, 0x76, 0x8d, 0x71, 0x97, 0xf7, 0x99, 0xe2, 0x62, 0xdd, 0x9e, 0xa4, 0xe9, 0x65, 0x8d, 0x46, 0xed, 0x1a, 0xe3, 0x2e, 0xef, 0x33, 0xc5, 0xc5, 0xba, 0x3d, 0x49, 0x23,
0x91, 0xdb, 0x87, 0xfd, 0x23, 0x09, 0x49, 0x40, 0xae, 0x34, 0x7a, 0x2d, 0x81, 0x2e, 0xbe, 0x5f, 0xb7, 0x0f, 0xfb, 0x47, 0x12, 0x92, 0x80, 0x5c, 0x69, 0xf4, 0x5a, 0x02, 0x5d, 0x7c, 0xbf, 0x16,
0x8b, 0xbf, 0x5f, 0x73, 0x23, 0xaf, 0xc6, 0x07, 0x11, 0x61, 0xb5, 0xaf, 0x42, 0x7a, 0x4c, 0xa8, 0x7f, 0xbf, 0xe6, 0x46, 0x5e, 0x8d, 0x0f, 0x22, 0xc2, 0x6a, 0x5f, 0x85, 0xf4, 0x98, 0x50, 0x4d,
0x26, 0xb8, 0x35, 0x93, 0x80, 0x85, 0xfe, 0x09, 0xa1, 0xb5, 0xe8, 0xb0, 0x16, 0x46, 0xb1, 0x34, 0x70, 0x6b, 0x26, 0x01, 0x0b, 0xfd, 0x13, 0x42, 0x6b, 0xd1, 0x61, 0x2d, 0x8c, 0x62, 0x69, 0xee,
0x77, 0x4e, 0xc1, 0xee, 0xd3, 0x36, 0x89, 0x42, 0xdf, 0x6b, 0x0f, 0x04, 0x8d, 0x5a, 0x69, 0xb2, 0x9c, 0x82, 0xdd, 0xa7, 0x6d, 0x12, 0x85, 0xbe, 0xd7, 0x1e, 0x08, 0x1a, 0xb5, 0xd2, 0x64, 0xab,
0x55, 0xad, 0xdd, 0x50, 0x76, 0xee, 0xf5, 0x08, 0xe3, 0x6e, 0x2f, 0x52, 0x08, 0xf6, 0x6f, 0x0c, 0x5a, 0xbb, 0xa1, 0xec, 0xdc, 0xeb, 0x11, 0xc6, 0xdd, 0x5e, 0xa4, 0x10, 0xec, 0xdf, 0x19, 0x50,
0x28, 0xef, 0xd1, 0x7e, 0x40, 0x30, 0xf9, 0x65, 0x9f, 0x30, 0x8e, 0xae, 0x40, 0xfe, 0xc8, 0xf3, 0xde, 0xa3, 0xfd, 0x80, 0x60, 0xf2, 0xeb, 0x3e, 0x61, 0x1c, 0x5d, 0x81, 0xfc, 0x91, 0xe7, 0x73,
0x39, 0xa1, 0xa6, 0xb1, 0x96, 0x5d, 0x2f, 0x62, 0x0d, 0xa1, 0x2a, 0x64, 0x5d, 0xdf, 0x37, 0x33, 0x42, 0x4d, 0x63, 0x2d, 0xbb, 0x5e, 0xc4, 0x1a, 0x42, 0x55, 0xc8, 0xba, 0xbe, 0x6f, 0x66, 0xd6,
0x6b, 0xc6, 0x7a, 0x01, 0x8b, 0x25, 0x5a, 0x87, 0xf2, 0x31, 0x21, 0x51, 0xb3, 0x4f, 0x5d, 0xee, 0x8c, 0xf5, 0x02, 0x16, 0x4b, 0xb4, 0x0e, 0xe5, 0x63, 0x42, 0xa2, 0x66, 0x9f, 0xba, 0xdc, 0x0b,
0x85, 0x81, 0x99, 0x5d, 0x33, 0xd6, 0xb3, 0x8d, 0xdc, 0xab, 0xd7, 0xab, 0x06, 0x1e, 0x3b, 0x41, 0x03, 0x33, 0xbb, 0x66, 0xac, 0x67, 0x1b, 0xb9, 0x57, 0xaf, 0x57, 0x0d, 0x3c, 0x76, 0x82, 0x6c,
0x36, 0x14, 0x05, 0xdc, 0x18, 0x70, 0xc2, 0xcc, 0x5c, 0x02, 0x6d, 0xb4, 0x6d, 0xdf, 0x84, 0x6a, 0x28, 0x0a, 0xb8, 0x31, 0xe0, 0x84, 0x99, 0xb9, 0x04, 0xda, 0x68, 0xdb, 0xbe, 0x09, 0xd5, 0xa6,
0xd3, 0x63, 0xc7, 0xcf, 0x98, 0xdb, 0x3d, 0x4b, 0x16, 0xfb, 0x31, 0x2c, 0x27, 0x70, 0x59, 0x14, 0xc7, 0x8e, 0x9f, 0x31, 0xb7, 0x7b, 0x96, 0x2c, 0xf6, 0x63, 0x58, 0x4e, 0xe0, 0xb2, 0x28, 0x0c,
0x06, 0x8c, 0xa0, 0x3b, 0x90, 0xa7, 0xa4, 0x1d, 0xd2, 0x8e, 0x44, 0x2e, 0xd5, 0x3f, 0x74, 0x26, 0x18, 0x41, 0x77, 0x20, 0x4f, 0x49, 0x3b, 0xa4, 0x1d, 0x89, 0x5c, 0xaa, 0x7f, 0xe8, 0x4c, 0xba,
0xdd, 0xc0, 0xd1, 0x04, 0x02, 0x09, 0x6b, 0x64, 0xfb, 0x8f, 0x59, 0x28, 0x25, 0xf6, 0xd1, 0x12, 0x81, 0xa3, 0x09, 0x04, 0x12, 0xd6, 0xc8, 0xf6, 0x9f, 0xb3, 0x50, 0x4a, 0xec, 0xa3, 0x25, 0xc8,
0x64, 0x76, 0x9a, 0xa6, 0xb1, 0x66, 0xac, 0x17, 0x71, 0x66, 0xa7, 0x89, 0x4c, 0x58, 0xdc, 0xed, 0xec, 0x34, 0x4d, 0x63, 0xcd, 0x58, 0x2f, 0xe2, 0xcc, 0x4e, 0x13, 0x99, 0xb0, 0xb8, 0xdb, 0xe7,
0x73, 0xf7, 0xd0, 0x27, 0x5a, 0xf7, 0x18, 0x44, 0x97, 0x61, 0x61, 0x27, 0x78, 0xc6, 0x88, 0x54, 0xee, 0xa1, 0x4f, 0xb4, 0xee, 0x31, 0x88, 0x2e, 0xc3, 0xc2, 0x4e, 0xf0, 0x8c, 0x11, 0xa9, 0x78,
0xbc, 0x80, 0x15, 0x80, 0x10, 0xe4, 0xf6, 0xbd, 0x5f, 0x11, 0xa5, 0x26, 0x96, 0x6b, 0x64, 0x41, 0x01, 0x2b, 0x00, 0x21, 0xc8, 0xed, 0x7b, 0xbf, 0x21, 0x4a, 0x4d, 0x2c, 0xd7, 0xc8, 0x82, 0xfc,
0x7e, 0xcf, 0xa5, 0x24, 0xe0, 0xe6, 0x82, 0xe0, 0xdb, 0xc8, 0x98, 0x06, 0xd6, 0x3b, 0xa8, 0x01, 0x9e, 0x4b, 0x49, 0xc0, 0xcd, 0x05, 0xc1, 0xb7, 0x91, 0x31, 0x0d, 0xac, 0x77, 0x50, 0x03, 0x8a,
0xc5, 0x6d, 0x4a, 0x5c, 0x4e, 0x3a, 0x5b, 0xdc, 0xcc, 0xaf, 0x19, 0xeb, 0xa5, 0xba, 0xe5, 0xa8, 0xdb, 0x94, 0xb8, 0x9c, 0x74, 0xb6, 0xb8, 0x99, 0x5f, 0x33, 0xd6, 0x4b, 0x75, 0xcb, 0x51, 0xb7,
0x5b, 0x73, 0xe2, 0x5b, 0x73, 0x0e, 0xe2, 0x5b, 0x6b, 0x14, 0x5e, 0xbd, 0x5e, 0xbd, 0xf0, 0xfb, 0xe6, 0xc4, 0xb7, 0xe6, 0x1c, 0xc4, 0xb7, 0xd6, 0x28, 0xbc, 0x7a, 0xbd, 0x7a, 0xe1, 0x8f, 0xdf,
0x7f, 0x09, 0xdb, 0x0d, 0xc9, 0xd0, 0x43, 0x80, 0x27, 0x2e, 0xe3, 0xcf, 0x98, 0x64, 0xb2, 0x78, 0x08, 0xdb, 0x0d, 0xc9, 0xd0, 0x43, 0x80, 0x27, 0x2e, 0xe3, 0xcf, 0x98, 0x64, 0xb2, 0x78, 0x26,
0x26, 0x93, 0x9c, 0x64, 0x90, 0xa0, 0x41, 0x2b, 0x00, 0xd2, 0x08, 0xdb, 0x61, 0x3f, 0xe0, 0x66, 0x93, 0x9c, 0x64, 0x90, 0xa0, 0x41, 0x2b, 0x00, 0xd2, 0x08, 0xdb, 0x61, 0x3f, 0xe0, 0x66, 0x41,
0x41, 0xca, 0x9e, 0xd8, 0x41, 0x6b, 0x50, 0x6a, 0x12, 0xd6, 0xa6, 0x5e, 0x24, 0xaf, 0xba, 0x28, 0xca, 0x9e, 0xd8, 0x41, 0x6b, 0x50, 0x6a, 0x12, 0xd6, 0xa6, 0x5e, 0x24, 0xaf, 0xba, 0x28, 0xcd,
0xcd, 0x93, 0xdc, 0x12, 0x1c, 0x94, 0x05, 0x0f, 0x06, 0x11, 0x31, 0x41, 0x22, 0x24, 0x76, 0xc4, 0x93, 0xdc, 0x12, 0x1c, 0x94, 0x05, 0x0f, 0x06, 0x11, 0x31, 0x41, 0x22, 0x24, 0x76, 0xc4, 0x5d,
0x5d, 0xee, 0xbf, 0x70, 0x29, 0xe9, 0x98, 0x25, 0x69, 0x2e, 0x0d, 0x09, 0xfb, 0x2a, 0x4b, 0x30, 0xee, 0xbf, 0x70, 0x29, 0xe9, 0x98, 0x25, 0x69, 0x2e, 0x0d, 0x09, 0xfb, 0x2a, 0x4b, 0x30, 0xb3,
0xb3, 0x2c, 0x2f, 0x39, 0x06, 0xed, 0x5f, 0x17, 0xa0, 0xbc, 0x2f, 0x42, 0x21, 0x76, 0x87, 0x2a, 0x2c, 0x2f, 0x39, 0x06, 0xed, 0xdf, 0x16, 0xa0, 0xbc, 0x2f, 0x42, 0x21, 0x76, 0x87, 0x2a, 0x64,
0x64, 0x31, 0x39, 0xd2, 0x77, 0x23, 0x96, 0xc8, 0x01, 0x68, 0x92, 0x23, 0x2f, 0xf0, 0xa4, 0x54, 0x31, 0x39, 0xd2, 0x77, 0x23, 0x96, 0xc8, 0x01, 0x68, 0x92, 0x23, 0x2f, 0xf0, 0xa4, 0x54, 0x19,
0x19, 0xa9, 0xf8, 0x92, 0x13, 0x1d, 0x3a, 0xa3, 0x5d, 0x9c, 0xc0, 0x40, 0x0e, 0xa0, 0xd6, 0xcb, 0xa9, 0xf8, 0x92, 0x13, 0x1d, 0x3a, 0xa3, 0x5d, 0x9c, 0xc0, 0x40, 0x0e, 0xa0, 0xd6, 0xcb, 0x28,
0x28, 0xa4, 0x9c, 0xd0, 0x26, 0x89, 0x28, 0x69, 0x0b, 0x03, 0xca, 0xfb, 0x2b, 0xe2, 0x94, 0x13, 0xa4, 0x9c, 0xd0, 0x26, 0x89, 0x28, 0x69, 0x0b, 0x03, 0xca, 0xfb, 0x2b, 0xe2, 0x94, 0x13, 0xd4,
0xd4, 0x87, 0x6f, 0xc4, 0xbb, 0x5b, 0x9c, 0x53, 0x96, 0x20, 0xca, 0x49, 0x27, 0xbb, 0x3f, 0xed, 0x87, 0x6f, 0xc5, 0xbb, 0x5b, 0x9c, 0x53, 0x96, 0x20, 0xca, 0x49, 0x27, 0xbb, 0x3f, 0xed, 0x64,
0x64, 0x49, 0x91, 0x9d, 0x19, 0xd4, 0xad, 0x80, 0xd3, 0x01, 0x9e, 0xc5, 0x5b, 0xd8, 0x64, 0x9f, 0x49, 0x91, 0x9d, 0x19, 0xd4, 0xad, 0x80, 0xd3, 0x01, 0x9e, 0xc5, 0x5b, 0xd8, 0x64, 0x9f, 0x30,
0x30, 0x26, 0x74, 0x92, 0x0e, 0x83, 0x63, 0x10, 0x59, 0x50, 0xf8, 0x09, 0x0d, 0x03, 0x4e, 0x82, 0x26, 0x74, 0x92, 0x0e, 0x83, 0x63, 0x10, 0x59, 0x50, 0xf8, 0x19, 0x0d, 0x03, 0x4e, 0x82, 0x8e,
0x8e, 0x74, 0x96, 0x22, 0x1e, 0xc2, 0xe8, 0x39, 0x54, 0xe2, 0xb5, 0x64, 0x68, 0x2e, 0x4a, 0x11, 0x74, 0x96, 0x22, 0x1e, 0xc2, 0xe8, 0x39, 0x54, 0xe2, 0xb5, 0x64, 0x68, 0x2e, 0x4a, 0x11, 0x3f,
0x3f, 0x3e, 0x43, 0xc4, 0x31, 0x1a, 0x25, 0xd8, 0x38, 0x1f, 0xb4, 0x09, 0x0b, 0xdb, 0x6e, 0xfb, 0x3e, 0x43, 0xc4, 0x31, 0x1a, 0x25, 0xd8, 0x38, 0x1f, 0xb4, 0x09, 0x0b, 0xdb, 0x6e, 0xfb, 0x05,
0x05, 0x91, 0x7e, 0x51, 0xaa, 0xaf, 0x4c, 0x33, 0x94, 0xc7, 0x4f, 0xa5, 0x23, 0x30, 0x19, 0xda, 0x91, 0x7e, 0x51, 0xaa, 0xaf, 0x4c, 0x33, 0x94, 0xc7, 0x4f, 0xa5, 0x23, 0x30, 0x19, 0xda, 0x17,
0x17, 0xb0, 0x22, 0x41, 0x3f, 0x87, 0x72, 0x2b, 0xe0, 0x1e, 0xf7, 0x49, 0x4f, 0xde, 0x71, 0x51, 0xb0, 0x22, 0x41, 0xbf, 0x84, 0x72, 0x2b, 0xe0, 0x1e, 0xf7, 0x49, 0x4f, 0xde, 0x71, 0x51, 0xdc,
0xdc, 0x71, 0x63, 0xf3, 0xeb, 0xd7, 0xab, 0x3f, 0x98, 0x99, 0xd1, 0xfa, 0xdc, 0xf3, 0x6b, 0x24, 0x71, 0x63, 0xf3, 0xeb, 0xd7, 0xab, 0x3f, 0x9a, 0x99, 0xd1, 0xfa, 0xdc, 0xf3, 0x6b, 0x24, 0x41,
0x41, 0xe5, 0x24, 0x58, 0xe0, 0x31, 0x7e, 0xe8, 0x0b, 0x58, 0x8a, 0x85, 0xdd, 0x09, 0xa2, 0x3e, 0xe5, 0x24, 0x58, 0xe0, 0x31, 0x7e, 0xe8, 0x0b, 0x58, 0x8a, 0x85, 0xdd, 0x09, 0xa2, 0x3e, 0x67,
0x67, 0x26, 0x48, 0xad, 0xeb, 0x73, 0x6a, 0xad, 0x88, 0x94, 0xda, 0x13, 0x9c, 0x84, 0xb1, 0x77, 0x26, 0x48, 0xad, 0xeb, 0x73, 0x6a, 0xad, 0x88, 0x94, 0xda, 0x13, 0x9c, 0x84, 0xb1, 0x77, 0x02,
0x02, 0x4e, 0x68, 0xe0, 0xfa, 0xda, 0x69, 0x87, 0x30, 0xda, 0x11, 0xbe, 0x29, 0x12, 0xef, 0x9e, 0x4e, 0x68, 0xe0, 0xfa, 0xda, 0x69, 0x87, 0x30, 0xda, 0x11, 0xbe, 0x29, 0x12, 0xef, 0x9e, 0x4c,
0x4c, 0xb7, 0x66, 0x59, 0x9a, 0xe6, 0xc6, 0xf4, 0x57, 0x93, 0xe9, 0xd9, 0x51, 0xc8, 0x78, 0x8c, 0xb7, 0x66, 0x59, 0x9a, 0xe6, 0xc6, 0xf4, 0x57, 0x93, 0xe9, 0xd9, 0x51, 0xc8, 0x78, 0x8c, 0x14,
0x14, 0xdd, 0x83, 0x62, 0xec, 0x08, 0xcc, 0xac, 0x48, 0xe9, 0xad, 0x69, 0x3e, 0x31, 0x0a, 0x1e, 0xdd, 0x83, 0x62, 0xec, 0x08, 0xcc, 0xac, 0x48, 0xe9, 0xad, 0x69, 0x3e, 0x31, 0x0a, 0x1e, 0x21,
0x21, 0x5b, 0x8f, 0xe1, 0xea, 0x69, 0x0e, 0x26, 0x02, 0xe6, 0x98, 0x0c, 0xe2, 0x80, 0x39, 0x26, 0x5b, 0x8f, 0xe1, 0xea, 0x69, 0x0e, 0x26, 0x02, 0xe6, 0x98, 0x0c, 0xe2, 0x80, 0x39, 0x26, 0x03,
0x03, 0x91, 0xb3, 0x4e, 0x5c, 0xbf, 0xaf, 0x72, 0x59, 0x11, 0x2b, 0x60, 0x33, 0x73, 0xcf, 0xb0, 0x91, 0xb3, 0x4e, 0x5c, 0xbf, 0xaf, 0x72, 0x59, 0x11, 0x2b, 0x60, 0x33, 0x73, 0xcf, 0xb0, 0x1e,
0x1e, 0x02, 0x9a, 0xf6, 0x84, 0x73, 0x71, 0xf8, 0x0c, 0x2e, 0xa5, 0x58, 0x35, 0x85, 0xc5, 0xf5, 0x02, 0x9a, 0xf6, 0x84, 0x73, 0x71, 0xf8, 0x0c, 0x2e, 0xa5, 0x58, 0x35, 0x85, 0xc5, 0xf5, 0x24,
0x24, 0x8b, 0xe9, 0x80, 0x1d, 0xb1, 0xb4, 0xff, 0x92, 0x85, 0x72, 0xd2, 0xb7, 0xd0, 0x06, 0x5c, 0x8b, 0xe9, 0x80, 0x1d, 0xb1, 0xb4, 0xff, 0x96, 0x85, 0x72, 0xd2, 0xb7, 0xd0, 0x06, 0x5c, 0x52,
0x52, 0x1a, 0x63, 0x72, 0x94, 0x08, 0x46, 0xc5, 0x3c, 0xed, 0x08, 0xd5, 0xe1, 0xf2, 0x4e, 0x4f, 0x1a, 0x63, 0x72, 0x94, 0x08, 0x46, 0xc5, 0x3c, 0xed, 0x08, 0xd5, 0xe1, 0xf2, 0x4e, 0x4f, 0x6f,
0x6f, 0x27, 0xe3, 0x37, 0x23, 0x93, 0x4d, 0xea, 0x19, 0x0a, 0xe1, 0x03, 0xc5, 0x6a, 0x32, 0xe8, 0x27, 0xe3, 0x37, 0x23, 0x93, 0x4d, 0xea, 0x19, 0x0a, 0xe1, 0x03, 0xc5, 0x6a, 0x32, 0xe8, 0xb3,
0xb3, 0xf2, 0x76, 0x3e, 0x39, 0x3d, 0x00, 0x9c, 0x54, 0x5a, 0xe5, 0x62, 0xe9, 0x7c, 0xd1, 0x8f, 0xf2, 0x76, 0x3e, 0x39, 0x3d, 0x00, 0x9c, 0x54, 0x5a, 0xe5, 0x62, 0xe9, 0x7c, 0xd1, 0x4f, 0x60,
0x60, 0x51, 0x1d, 0x30, 0x9d, 0x57, 0x3e, 0x3a, 0xfd, 0x13, 0x8a, 0x59, 0x4c, 0x23, 0xc8, 0x95, 0x51, 0x1d, 0x30, 0x9d, 0x57, 0x3e, 0x3a, 0xfd, 0x13, 0x8a, 0x59, 0x4c, 0x23, 0xc8, 0x95, 0x1e,
0x1e, 0xcc, 0x5c, 0x38, 0x07, 0xb9, 0xa6, 0xb1, 0x1e, 0x81, 0x35, 0x5b, 0xe4, 0xf3, 0xb8, 0x80, 0xcc, 0x5c, 0x38, 0x07, 0xb9, 0xa6, 0xb1, 0x1e, 0x81, 0x35, 0x5b, 0xe4, 0xf3, 0xb8, 0x80, 0xfd,
0xfd, 0x67, 0x03, 0x96, 0xa7, 0x3e, 0x24, 0x9e, 0x44, 0xf9, 0x28, 0x28, 0x16, 0x72, 0x8d, 0x9a, 0x57, 0x03, 0x96, 0xa7, 0x3e, 0x24, 0x9e, 0x44, 0xf9, 0x28, 0x28, 0x16, 0x72, 0x8d, 0x9a, 0xb0,
0xb0, 0xa0, 0x92, 0x54, 0x46, 0x0a, 0xec, 0xcc, 0x21, 0xb0, 0x93, 0xc8, 0x50, 0x8a, 0xd8, 0xba, 0xa0, 0x92, 0x54, 0x46, 0x0a, 0xec, 0xcc, 0x21, 0xb0, 0x93, 0xc8, 0x50, 0x8a, 0xd8, 0xba, 0x07,
0x07, 0xf0, 0x76, 0xce, 0x6a, 0xff, 0xd5, 0x80, 0x8a, 0x4e, 0x08, 0xba, 0x7e, 0x70, 0xa1, 0x3a, 0xf0, 0x76, 0xce, 0x6a, 0xff, 0xdd, 0x80, 0x8a, 0x4e, 0x08, 0xba, 0x7e, 0x70, 0xa1, 0x3a, 0x8c,
0x8c, 0x31, 0xbd, 0xa7, 0x2b, 0x89, 0x3b, 0x33, 0x73, 0x89, 0x42, 0x73, 0x26, 0xe9, 0x94, 0x8c, 0x31, 0xbd, 0xa7, 0x2b, 0x89, 0x3b, 0x33, 0x73, 0x89, 0x42, 0x73, 0x26, 0xe9, 0x94, 0x8c, 0x53,
0x53, 0xec, 0xac, 0xed, 0xd8, 0xaf, 0x26, 0x50, 0xcf, 0x25, 0xf9, 0x35, 0xa8, 0xec, 0xcb, 0x3a, 0xec, 0xac, 0xed, 0xd8, 0xaf, 0x26, 0x50, 0xcf, 0x25, 0xf9, 0x35, 0xa8, 0xec, 0xcb, 0x3a, 0x75,
0x75, 0xe6, 0xb3, 0x68, 0xff, 0xd7, 0x80, 0xa5, 0x18, 0x47, 0x6b, 0xf7, 0x7d, 0x28, 0x9c, 0x10, 0xe6, 0xb3, 0x68, 0xff, 0xd7, 0x80, 0xa5, 0x18, 0x47, 0x6b, 0xf7, 0x43, 0x28, 0x9c, 0x10, 0xca,
0xca, 0xc9, 0x4b, 0xc2, 0xb4, 0x56, 0xe6, 0xb4, 0x56, 0x9f, 0x4b, 0x0c, 0x3c, 0xc4, 0x44, 0x9b, 0xc9, 0x4b, 0xc2, 0xb4, 0x56, 0xe6, 0xb4, 0x56, 0x9f, 0x4b, 0x0c, 0x3c, 0xc4, 0x44, 0x9b, 0x50,
0x50, 0x50, 0x35, 0x31, 0x89, 0x2f, 0x6a, 0x65, 0x16, 0x95, 0xfe, 0xde, 0x10, 0x1f, 0xd5, 0x20, 0x50, 0x35, 0x31, 0x89, 0x2f, 0x6a, 0x65, 0x16, 0x95, 0xfe, 0xde, 0x10, 0x1f, 0xd5, 0x20, 0xe7,
0xe7, 0x87, 0x5d, 0xa6, 0x63, 0xe6, 0x5b, 0xb3, 0xe8, 0x9e, 0x84, 0x5d, 0x2c, 0x11, 0xd1, 0x7d, 0x87, 0x5d, 0xa6, 0x63, 0xe6, 0x3b, 0xb3, 0xe8, 0x9e, 0x84, 0x5d, 0x2c, 0x11, 0xd1, 0x7d, 0x28,
0x28, 0x7c, 0xe5, 0xd2, 0xc0, 0x0b, 0xba, 0x71, 0x14, 0xac, 0xce, 0x22, 0x7a, 0xae, 0xf0, 0xf0, 0x7c, 0xe5, 0xd2, 0xc0, 0x0b, 0xba, 0x71, 0x14, 0xac, 0xce, 0x22, 0x7a, 0xae, 0xf0, 0xf0, 0x90,
0x90, 0x40, 0x94, 0x71, 0x79, 0x75, 0x86, 0x1e, 0x43, 0xbe, 0xe3, 0x75, 0x09, 0xe3, 0xca, 0x24, 0x40, 0x94, 0x71, 0x79, 0x75, 0x86, 0x1e, 0x43, 0xbe, 0xe3, 0x75, 0x09, 0xe3, 0xca, 0x24, 0x8d,
0x8d, 0xba, 0x78, 0x8f, 0xbe, 0x7e, 0xbd, 0x7a, 0x33, 0xf1, 0xe0, 0x84, 0x11, 0x09, 0x44, 0xd3, 0xba, 0x78, 0x8f, 0xbe, 0x7e, 0xbd, 0x7a, 0x33, 0xf1, 0xe0, 0x84, 0x11, 0x09, 0x44, 0xd3, 0xe0,
0xe0, 0x7a, 0x01, 0xa1, 0xa2, 0x07, 0xb8, 0xad, 0x48, 0x9c, 0xa6, 0xfc, 0xc1, 0x9a, 0x83, 0xe0, 0x7a, 0x01, 0xa1, 0xa2, 0x07, 0xb8, 0xad, 0x48, 0x9c, 0xa6, 0xfc, 0xc1, 0x9a, 0x83, 0xe0, 0xe5,
0xe5, 0xa9, 0x67, 0x45, 0xe6, 0x8b, 0xb7, 0xe3, 0xa5, 0x38, 0x88, 0x30, 0x08, 0xdc, 0x1e, 0xd1, 0xa9, 0x67, 0x45, 0xe6, 0x8b, 0xb7, 0xe3, 0xa5, 0x38, 0x88, 0x30, 0x08, 0xdc, 0x1e, 0xd1, 0xe5,
0xe5, 0x86, 0x5c, 0x8b, 0xaa, 0xa8, 0x2d, 0xfc, 0xbc, 0x23, 0xeb, 0xc5, 0x02, 0xd6, 0x10, 0xda, 0x86, 0x5c, 0x8b, 0xaa, 0xa8, 0x2d, 0xfc, 0xbc, 0x23, 0xeb, 0xc5, 0x02, 0xd6, 0x10, 0xda, 0x84,
0x84, 0x45, 0xc6, 0x5d, 0x2a, 0x72, 0xce, 0xc2, 0x9c, 0xe5, 0x5c, 0x4c, 0x80, 0x1e, 0x40, 0xb1, 0x45, 0xc6, 0x5d, 0x2a, 0x72, 0xce, 0xc2, 0x9c, 0xe5, 0x5c, 0x4c, 0x80, 0x1e, 0x40, 0xb1, 0x1d,
0x1d, 0xf6, 0x22, 0x9f, 0x08, 0xea, 0xfc, 0x9c, 0xd4, 0x23, 0x12, 0xe1, 0x7a, 0x84, 0xd2, 0x90, 0xf6, 0x22, 0x9f, 0x08, 0xea, 0xfc, 0x9c, 0xd4, 0x23, 0x12, 0xe1, 0x7a, 0x84, 0xd2, 0x90, 0xca,
0xca, 0x42, 0xb2, 0x88, 0x15, 0x80, 0xee, 0x42, 0x25, 0xa2, 0x61, 0x97, 0x12, 0xc6, 0x3e, 0xa5, 0x42, 0xb2, 0x88, 0x15, 0x80, 0xee, 0x42, 0x25, 0xa2, 0x61, 0x97, 0x12, 0xc6, 0x3e, 0xa5, 0x61,
0x61, 0x3f, 0xd2, 0xc5, 0xc0, 0xb2, 0x48, 0xde, 0x7b, 0xc9, 0x03, 0x3c, 0x8e, 0x67, 0xff, 0x27, 0x3f, 0xd2, 0xc5, 0xc0, 0xb2, 0x48, 0xde, 0x7b, 0xc9, 0x03, 0x3c, 0x8e, 0x67, 0xff, 0x27, 0x03,
0x03, 0xe5, 0xa4, 0x8b, 0x4c, 0x55, 0xd8, 0x8f, 0x21, 0xaf, 0x1c, 0x4e, 0xf9, 0xfa, 0xdb, 0xd9, 0xe5, 0xa4, 0x8b, 0x4c, 0x55, 0xd8, 0x8f, 0x21, 0xaf, 0x1c, 0x4e, 0xf9, 0xfa, 0xdb, 0xd9, 0x58,
0x58, 0x71, 0x48, 0xb5, 0xb1, 0x09, 0x8b, 0xed, 0x3e, 0x95, 0xe5, 0xb7, 0x2a, 0xca, 0x63, 0x50, 0x71, 0x48, 0xb5, 0xb1, 0x09, 0x8b, 0xed, 0x3e, 0x95, 0xe5, 0xb7, 0x2a, 0xca, 0x63, 0x50, 0x68,
0x68, 0xca, 0x43, 0xee, 0xfa, 0xd2, 0xc6, 0x59, 0xac, 0x00, 0x51, 0x91, 0x0f, 0xbb, 0xa4, 0xf3, 0xca, 0x43, 0xee, 0xfa, 0xd2, 0xc6, 0x59, 0xac, 0x00, 0x51, 0x91, 0x0f, 0xbb, 0xa4, 0xf3, 0x55,
0x55, 0xe4, 0x43, 0xb2, 0xe4, 0xfd, 0x2d, 0xbe, 0xd3, 0xfd, 0x15, 0xce, 0x7d, 0x7f, 0xf6, 0x3f, 0xe4, 0x43, 0xb2, 0xe4, 0xfd, 0x2d, 0xbe, 0xd3, 0xfd, 0x15, 0xce, 0x7d, 0x7f, 0xf6, 0x3f, 0x0d,
0x0c, 0x28, 0x0e, 0x63, 0x2b, 0x61, 0x5d, 0xe3, 0x9d, 0xad, 0x3b, 0x66, 0x99, 0xcc, 0xdb, 0x59, 0x28, 0x0e, 0x63, 0x2b, 0x61, 0x5d, 0xe3, 0x9d, 0xad, 0x3b, 0x66, 0x99, 0xcc, 0xdb, 0x59, 0xe6,
0xe6, 0x0a, 0xe4, 0x19, 0xa7, 0xc4, 0xed, 0xa9, 0x7e, 0x11, 0x6b, 0x48, 0x64, 0xb1, 0x1e, 0xeb, 0x0a, 0xe4, 0x19, 0xa7, 0xc4, 0xed, 0xa9, 0x7e, 0x11, 0x6b, 0x48, 0x64, 0xb1, 0x1e, 0xeb, 0xca,
0xca, 0x1b, 0x2a, 0x63, 0xb1, 0xb4, 0xff, 0x67, 0x40, 0x65, 0x2c, 0xdc, 0xdf, 0xab, 0x2e, 0x97, 0x1b, 0x2a, 0x63, 0xb1, 0xb4, 0xff, 0x67, 0x40, 0x65, 0x2c, 0xdc, 0xdf, 0xab, 0x2e, 0x97, 0x61,
0x61, 0xc1, 0x27, 0x27, 0x44, 0x75, 0xb4, 0x59, 0xac, 0x00, 0xb1, 0xcb, 0x5e, 0x84, 0x94, 0x4b, 0xc1, 0x27, 0x27, 0x44, 0x75, 0xb4, 0x59, 0xac, 0x00, 0xb1, 0xcb, 0x5e, 0x84, 0x94, 0x4b, 0xe1,
0xe1, 0xca, 0x58, 0x01, 0x42, 0xe6, 0x0e, 0xe1, 0xae, 0xe7, 0xcb, 0xbc, 0x54, 0xc6, 0x1a, 0x12, 0xca, 0x58, 0x01, 0x42, 0xe6, 0x0e, 0xe1, 0xae, 0xe7, 0xcb, 0xbc, 0x54, 0xc6, 0x1a, 0x12, 0x32,
0x32, 0xf7, 0xa9, 0xaf, 0x6b, 0x74, 0xb1, 0x44, 0x36, 0xe4, 0xbc, 0xe0, 0x28, 0xd4, 0x6e, 0x23, 0xf7, 0xa9, 0xaf, 0x6b, 0x74, 0xb1, 0x44, 0x36, 0xe4, 0xbc, 0xe0, 0x28, 0xd4, 0x6e, 0x23, 0x2b,
0x2b, 0x1b, 0x55, 0xeb, 0xed, 0x04, 0x47, 0x21, 0x96, 0x67, 0xe8, 0x1a, 0xe4, 0xa9, 0x1b, 0x74, 0x1b, 0x55, 0xeb, 0xed, 0x04, 0x47, 0x21, 0x96, 0x67, 0xe8, 0x1a, 0xe4, 0xa9, 0x1b, 0x74, 0x49,
0x49, 0x5c, 0xa0, 0x17, 0x05, 0x16, 0x16, 0x3b, 0x58, 0x1f, 0xd8, 0x36, 0x94, 0x65, 0x57, 0xbc, 0x5c, 0xa0, 0x17, 0x05, 0x16, 0x16, 0x3b, 0x58, 0x1f, 0xd8, 0x36, 0x94, 0x65, 0x57, 0xbc, 0x4b,
0x4b, 0x98, 0xe8, 0xc1, 0x84, 0x5b, 0x77, 0x5c, 0xee, 0x4a, 0xb5, 0xcb, 0x58, 0xae, 0xed, 0x5b, 0x98, 0xe8, 0xc1, 0x84, 0x5b, 0x77, 0x5c, 0xee, 0x4a, 0xb5, 0xcb, 0x58, 0xae, 0xed, 0x5b, 0x80,
0x80, 0x9e, 0x78, 0x8c, 0x3f, 0x97, 0x33, 0x05, 0x76, 0x56, 0xcb, 0xbc, 0x0f, 0x97, 0xc6, 0xb0, 0x9e, 0x78, 0x8c, 0x3f, 0x97, 0x33, 0x05, 0x76, 0x56, 0xcb, 0xbc, 0x0f, 0x97, 0xc6, 0xb0, 0xf5,
0xf5, 0xb3, 0xf0, 0xc3, 0x89, 0xa6, 0xf9, 0xfa, 0x74, 0xc6, 0x95, 0xa3, 0x0b, 0x47, 0x11, 0x4e, 0xb3, 0xf0, 0xe3, 0x89, 0xa6, 0xf9, 0xfa, 0x74, 0xc6, 0x95, 0xa3, 0x0b, 0x47, 0x11, 0x4e, 0xf4,
0xf4, 0xce, 0x15, 0x28, 0x49, 0xbd, 0xd4, 0xb7, 0x6d, 0x17, 0xca, 0x0a, 0xd4, 0xcc, 0x3f, 0x83, 0xce, 0x15, 0x28, 0x49, 0xbd, 0xd4, 0xb7, 0x6d, 0x17, 0xca, 0x0a, 0xd4, 0xcc, 0x3f, 0x83, 0x8b,
0x8b, 0x31, 0xa3, 0xcf, 0x09, 0x95, 0xed, 0x8c, 0x21, 0xed, 0xf2, 0x9d, 0x59, 0x5f, 0x69, 0x8c, 0x31, 0xa3, 0xcf, 0x09, 0x95, 0xed, 0x8c, 0x21, 0xed, 0xf2, 0xbd, 0x59, 0x5f, 0x69, 0x8c, 0xa3,
0xa3, 0xe3, 0x49, 0x7a, 0x9b, 0xc0, 0x25, 0x89, 0xf3, 0xc8, 0x63, 0x3c, 0xa4, 0x83, 0x58, 0xeb, 0xe3, 0x49, 0x7a, 0x9b, 0xc0, 0x25, 0x89, 0xf3, 0xc8, 0x63, 0x3c, 0xa4, 0x83, 0x58, 0xeb, 0x15,
0x15, 0x80, 0xad, 0x36, 0xf7, 0x4e, 0xc8, 0xd3, 0xc0, 0x57, 0xcf, 0x68, 0x01, 0x27, 0x76, 0xe2, 0x80, 0xad, 0x36, 0xf7, 0x4e, 0xc8, 0xd3, 0xc0, 0x57, 0xcf, 0x68, 0x01, 0x27, 0x76, 0xe2, 0x27,
0x27, 0x32, 0x33, 0xea, 0x1c, 0xaf, 0x42, 0xb1, 0xe5, 0x52, 0x7f, 0xd0, 0x7a, 0xe9, 0x71, 0xdd, 0x32, 0x33, 0xea, 0x1c, 0xaf, 0x42, 0xb1, 0xe5, 0x52, 0x7f, 0xd0, 0x7a, 0xe9, 0x71, 0xdd, 0xc0,
0xc0, 0x8f, 0x36, 0xec, 0xdf, 0x19, 0xb0, 0x9c, 0xfc, 0x4e, 0xeb, 0x44, 0xa4, 0x8b, 0xfb, 0x90, 0x8f, 0x36, 0xec, 0x3f, 0x18, 0xb0, 0x9c, 0xfc, 0x4e, 0xeb, 0x44, 0xa4, 0x8b, 0xfb, 0x90, 0xe3,
0xe3, 0x71, 0x1d, 0xb3, 0x94, 0xa6, 0xc4, 0x14, 0x89, 0x28, 0x75, 0xb0, 0x24, 0x4a, 0x58, 0x5a, 0x71, 0x1d, 0xb3, 0x94, 0xa6, 0xc4, 0x14, 0x89, 0x28, 0x75, 0xb0, 0x24, 0x4a, 0x58, 0x5a, 0x05,
0x05, 0xce, 0xf5, 0xd3, 0xc9, 0x27, 0x2c, 0xfd, 0xb7, 0x22, 0xa0, 0xe9, 0xe3, 0x94, 0x8e, 0x38, 0xce, 0xf5, 0xd3, 0xc9, 0x27, 0x2c, 0xfd, 0x4d, 0x11, 0xd0, 0xf4, 0x71, 0x4a, 0x47, 0x9c, 0x6c,
0xd9, 0x20, 0x66, 0x26, 0x1a, 0xc4, 0x2f, 0x27, 0x1b, 0x44, 0xf5, 0x34, 0xdf, 0x9d, 0x47, 0x92, 0x10, 0x33, 0x13, 0x0d, 0xe2, 0x97, 0x93, 0x0d, 0xa2, 0x7a, 0x9a, 0xef, 0xce, 0x23, 0xc9, 0x1c,
0x39, 0xda, 0xc4, 0xb1, 0x3e, 0x26, 0x77, 0x8e, 0x3e, 0x06, 0xad, 0xc7, 0x2f, 0x8e, 0x7a, 0xeb, 0x6d, 0xe2, 0x58, 0x1f, 0x93, 0x3b, 0x47, 0x1f, 0x83, 0xd6, 0xe3, 0x17, 0x47, 0xbd, 0x75, 0x28,
0x50, 0x9c, 0x53, 0x68, 0xd4, 0x76, 0x74, 0x5d, 0xa1, 0x5f, 0xa1, 0x07, 0xe7, 0x9b, 0x96, 0xe4, 0xce, 0x29, 0x34, 0x6a, 0x3b, 0xba, 0xae, 0xd0, 0xaf, 0xd0, 0x83, 0xf3, 0x4d, 0x4b, 0x72, 0x93,
0x26, 0x27, 0x25, 0x0d, 0x28, 0x6d, 0xc7, 0x89, 0xf2, 0x1c, 0xa3, 0x92, 0x24, 0x11, 0xda, 0xd0, 0x93, 0x92, 0x06, 0x94, 0xb6, 0xe3, 0x44, 0x79, 0x8e, 0x51, 0x49, 0x92, 0x08, 0x6d, 0xe8, 0xc2,
0x85, 0x8d, 0x4a, 0xcd, 0x57, 0xa7, 0x55, 0x8c, 0xc7, 0x22, 0x21, 0xd5, 0x95, 0xcd, 0x51, 0x4a, 0x46, 0xa5, 0xe6, 0xab, 0xd3, 0x2a, 0xc6, 0x63, 0x91, 0x90, 0xea, 0xca, 0xe6, 0x28, 0xa5, 0xb4,
0x69, 0x59, 0x94, 0x06, 0xda, 0x9c, 0xcb, 0xf6, 0x73, 0xd6, 0x97, 0xe8, 0x13, 0xc8, 0x63, 0xc2, 0x2c, 0x4a, 0x03, 0x6d, 0xce, 0x65, 0xfb, 0x39, 0xeb, 0x4b, 0xf4, 0x09, 0xe4, 0x31, 0x61, 0x7d,
0xfa, 0x3e, 0x97, 0xf3, 0x97, 0x52, 0xfd, 0xda, 0x0c, 0xee, 0x0a, 0x49, 0xc6, 0xaa, 0x26, 0x40, 0x9f, 0xcb, 0xf9, 0x4b, 0xa9, 0x7e, 0x6d, 0x06, 0x77, 0x85, 0x24, 0x63, 0x55, 0x13, 0xa0, 0x9f,
0x3f, 0x85, 0x45, 0xb5, 0x62, 0x66, 0x69, 0xd6, 0xd8, 0x20, 0x45, 0x32, 0x4d, 0xa3, 0x1b, 0x0a, 0xc3, 0xa2, 0x5a, 0x31, 0xb3, 0x34, 0x6b, 0x6c, 0x90, 0x22, 0x99, 0xa6, 0xd1, 0x0d, 0x85, 0x86,
0x0d, 0x89, 0x70, 0xfc, 0x94, 0x04, 0x44, 0xcf, 0x05, 0x45, 0x6b, 0xbc, 0x80, 0x13, 0x3b, 0xa8, 0x44, 0x38, 0x7e, 0x4a, 0x02, 0xa2, 0xe7, 0x82, 0xa2, 0x35, 0x5e, 0xc0, 0x89, 0x1d, 0x54, 0x87,
0x0e, 0x0b, 0x9c, 0xba, 0x6d, 0x62, 0x56, 0xe6, 0x30, 0xa1, 0x42, 0x15, 0x89, 0x2d, 0xf2, 0x82, 0x05, 0x4e, 0xdd, 0x36, 0x31, 0x2b, 0x73, 0x98, 0x50, 0xa1, 0x8a, 0xc4, 0x16, 0x79, 0x41, 0x40,
0x80, 0x74, 0xcc, 0x25, 0x55, 0x29, 0x29, 0x08, 0x7d, 0x1b, 0x96, 0x82, 0x7e, 0x4f, 0x36, 0x0b, 0x3a, 0xe6, 0x92, 0xaa, 0x94, 0x14, 0x84, 0xbe, 0x0b, 0x4b, 0x41, 0xbf, 0x27, 0x9b, 0x85, 0xce,
0x9d, 0x7d, 0x4e, 0x22, 0x66, 0x5e, 0x94, 0xdf, 0x9b, 0xd8, 0x45, 0xd7, 0xa1, 0x12, 0xf4, 0x7b, 0x3e, 0x27, 0x11, 0x33, 0x2f, 0xca, 0xef, 0x4d, 0xec, 0xa2, 0xeb, 0x50, 0x09, 0xfa, 0xbd, 0x03,
0x07, 0xe2, 0x85, 0x57, 0x68, 0x55, 0x89, 0x36, 0xbe, 0x89, 0x6e, 0xc1, 0xb2, 0xa0, 0x8b, 0x6f, 0xf1, 0xc2, 0x2b, 0xb4, 0xaa, 0x44, 0x1b, 0xdf, 0x44, 0xb7, 0x60, 0x59, 0xd0, 0xc5, 0xb7, 0xad,
0x5b, 0x61, 0x2e, 0x4b, 0xcc, 0xe9, 0x03, 0xd4, 0x80, 0x0a, 0x79, 0xa9, 0x06, 0x02, 0x2d, 0xe9, 0x30, 0x97, 0x25, 0xe6, 0xf4, 0x01, 0x6a, 0x40, 0x85, 0xbc, 0x54, 0x03, 0x81, 0x96, 0xf4, 0x5f,
0xbf, 0x68, 0x0e, 0x7d, 0xc6, 0x49, 0xde, 0x43, 0xdf, 0xfd, 0x3e, 0xba, 0x0a, 0xeb, 0x4b, 0x28, 0x34, 0x87, 0x3e, 0xe3, 0x24, 0x68, 0x0d, 0x4a, 0x41, 0xbf, 0xf7, 0x3c, 0x2e, 0x7c, 0x2f, 0xc9,
0x27, 0xef, 0x32, 0x85, 0xf6, 0xee, 0x78, 0xd7, 0x3e, 0x87, 0x6f, 0x25, 0x9a, 0x96, 0x01, 0x7c, 0x6f, 0x25, 0xb7, 0xde, 0x43, 0x67, 0xfe, 0x3e, 0xfa, 0x0e, 0xeb, 0x4b, 0x28, 0x27, 0x6f, 0x3b,
0xf3, 0x59, 0xd4, 0x71, 0x39, 0x49, 0xcb, 0xde, 0xd3, 0x59, 0xec, 0x0a, 0xe4, 0xf7, 0xd4, 0x65, 0x85, 0xf6, 0xee, 0x78, 0x5f, 0x3f, 0x87, 0xf7, 0x25, 0xda, 0x9a, 0x01, 0x7c, 0xfb, 0x59, 0xd4,
0xab, 0x99, 0xab, 0x86, 0xc4, 0x7e, 0x93, 0x88, 0x0b, 0xd0, 0x29, 0x5b, 0x43, 0x32, 0xeb, 0x79, 0x71, 0x39, 0x49, 0xcb, 0xef, 0xd3, 0x79, 0xee, 0x0a, 0xe4, 0xf7, 0x94, 0x3b, 0xa8, 0xa9, 0xac,
0x81, 0xeb, 0xc7, 0x83, 0xd7, 0x02, 0x1e, 0xc2, 0xf6, 0x55, 0xb0, 0xd2, 0x3e, 0xad, 0x0c, 0x65, 0x86, 0xc4, 0x7e, 0x93, 0x88, 0x2b, 0xd2, 0x49, 0x5d, 0x43, 0x32, 0x2f, 0x7a, 0x81, 0xeb, 0xc7,
0xff, 0x29, 0x03, 0x30, 0xba, 0x1c, 0xf4, 0x21, 0x40, 0x8f, 0x74, 0x3c, 0xf7, 0x17, 0x7c, 0xd4, 0xa3, 0xd9, 0x02, 0x1e, 0xc2, 0xf6, 0x55, 0xb0, 0xd2, 0x3e, 0xad, 0x0c, 0x65, 0xff, 0x25, 0x03,
0xb0, 0x16, 0xe5, 0x8e, 0xec, 0x5a, 0x47, 0xad, 0x45, 0xe6, 0x9d, 0x5b, 0x0b, 0x04, 0x39, 0x26, 0x30, 0xba, 0x3e, 0xf4, 0x21, 0x40, 0x8f, 0x74, 0x3c, 0xf7, 0x57, 0x7c, 0xd4, 0xd2, 0x16, 0xe5,
0xe4, 0x55, 0x65, 0x90, 0x5c, 0xa3, 0xa7, 0x50, 0x72, 0x83, 0x20, 0xe4, 0x32, 0x4c, 0xe2, 0x66, 0x8e, 0xec, 0x6b, 0x47, 0xcd, 0x47, 0xe6, 0x9d, 0x9b, 0x0f, 0x04, 0x39, 0x26, 0xe4, 0x55, 0x85,
0xfe, 0xf6, 0x69, 0xee, 0xe4, 0x6c, 0x8d, 0xf0, 0x55, 0x14, 0x26, 0x39, 0x58, 0x0f, 0xa0, 0x3a, 0x92, 0x5c, 0xa3, 0xa7, 0x50, 0x72, 0x83, 0x20, 0xe4, 0x32, 0x90, 0xe2, 0x76, 0xff, 0xf6, 0x69,
0x89, 0x70, 0xae, 0x66, 0xf3, 0xef, 0x19, 0xb8, 0x38, 0x71, 0xad, 0xe8, 0x11, 0x54, 0x15, 0x34, 0x0e, 0xe7, 0x6c, 0x8d, 0xf0, 0x55, 0x9c, 0x26, 0x39, 0x58, 0x0f, 0xa0, 0x3a, 0x89, 0x70, 0xae,
0x31, 0x80, 0x39, 0xcb, 0xf1, 0xa7, 0xa8, 0xd0, 0x43, 0x28, 0x6f, 0x71, 0x2e, 0x32, 0xad, 0xd2, 0x76, 0xf4, 0x1f, 0x19, 0xb8, 0x38, 0x71, 0xad, 0xe8, 0x11, 0x54, 0x15, 0x34, 0x31, 0xa2, 0x39,
0x57, 0xb5, 0x98, 0xa7, 0x73, 0x19, 0xa3, 0x40, 0x8f, 0x46, 0x69, 0x2b, 0x3b, 0x6b, 0x90, 0x30, 0x2b, 0x34, 0xa6, 0xa8, 0xd0, 0x43, 0x28, 0x6f, 0x71, 0x2e, 0x72, 0xb1, 0xd2, 0x57, 0x35, 0xa1,
0x21, 0x7f, 0x7a, 0xce, 0xb2, 0x7e, 0x36, 0x3b, 0x00, 0xb2, 0xca, 0x4a, 0xf5, 0xf1, 0x00, 0x38, 0xa7, 0x73, 0x19, 0xa3, 0x40, 0x8f, 0x46, 0x89, 0x2d, 0x3b, 0x6b, 0xd4, 0x30, 0x21, 0x7f, 0x7a,
0x23, 0x6b, 0x8d, 0x6c, 0xf8, 0x07, 0x03, 0x0a, 0x71, 0x80, 0xa6, 0xce, 0x42, 0xee, 0x8f, 0xcf, 0x56, 0xb3, 0x7e, 0x31, 0x3b, 0x00, 0xb2, 0xca, 0x4a, 0xf5, 0xf1, 0x00, 0x38, 0x23, 0xaf, 0x8d,
0x42, 0x6e, 0xcc, 0x7e, 0x34, 0xdf, 0xe7, 0x08, 0xe4, 0xe6, 0x8f, 0xe1, 0x83, 0xd4, 0x82, 0x05, 0x6c, 0xf8, 0x27, 0x03, 0x0a, 0x71, 0x80, 0xa6, 0x4e, 0x4b, 0xee, 0x8f, 0x4f, 0x4b, 0x6e, 0xcc,
0x95, 0x60, 0x71, 0xff, 0x60, 0x0b, 0x1f, 0xb4, 0x9a, 0xd5, 0x0b, 0xa8, 0x0c, 0x85, 0xed, 0xa7, 0x7e, 0x56, 0xdf, 0xe7, 0x90, 0xe4, 0xe6, 0x4f, 0xe1, 0x83, 0xd4, 0x92, 0x06, 0x95, 0x60, 0x71,
0xbb, 0x7b, 0x4f, 0x5a, 0x07, 0xad, 0xaa, 0x21, 0x8e, 0x9a, 0x2d, 0xb1, 0x6e, 0x56, 0x33, 0xf5, 0xff, 0x60, 0x0b, 0x1f, 0xb4, 0x9a, 0xd5, 0x0b, 0xa8, 0x0c, 0x85, 0xed, 0xa7, 0xbb, 0x7b, 0x4f,
0xdf, 0xe6, 0x61, 0x71, 0x5b, 0xfd, 0xf3, 0x86, 0x0e, 0xa0, 0x38, 0xfc, 0x4b, 0x06, 0xd9, 0x29, 0x5a, 0x07, 0xad, 0xaa, 0x21, 0x8e, 0x9a, 0x2d, 0xb1, 0x6e, 0x56, 0x33, 0xf5, 0xdf, 0xe7, 0x61,
0xa6, 0x99, 0xf8, 0x6f, 0xc7, 0xfa, 0xe8, 0x54, 0x1c, 0xfd, 0xa0, 0x3d, 0x82, 0x05, 0xf9, 0xe7, 0x71, 0x5b, 0xfd, 0x37, 0x87, 0x0e, 0xa0, 0x38, 0xfc, 0xd3, 0x06, 0xd9, 0x29, 0xa6, 0x99, 0xf8,
0x14, 0x4a, 0x19, 0x3b, 0x24, 0xff, 0xb5, 0xb2, 0x4e, 0xff, 0xb3, 0x67, 0xc3, 0x10, 0x9c, 0xe4, 0xf7, 0xc7, 0xfa, 0xe8, 0x54, 0x1c, 0xfd, 0xe4, 0x3d, 0x82, 0x05, 0xf9, 0xf7, 0x15, 0x4a, 0x19,
0xcc, 0x26, 0x8d, 0x53, 0x72, 0x30, 0x6c, 0xad, 0x9e, 0x31, 0xec, 0x41, 0xbb, 0x90, 0xd7, 0x8d, 0x4c, 0x24, 0xff, 0xd7, 0xb2, 0x4e, 0xff, 0x3b, 0x68, 0xc3, 0x10, 0x9c, 0xe4, 0x54, 0x27, 0x8d,
0x6c, 0x1a, 0x6a, 0x72, 0x32, 0x63, 0xad, 0xcd, 0x46, 0x50, 0xcc, 0x36, 0x0c, 0xb4, 0x3b, 0x9c, 0x53, 0x72, 0x74, 0x6c, 0xad, 0x9e, 0x31, 0x0e, 0x42, 0xbb, 0x90, 0xd7, 0xad, 0x6e, 0x1a, 0x6a,
0xf5, 0xa7, 0x89, 0x96, 0xec, 0x02, 0xac, 0x33, 0xce, 0xd7, 0x8d, 0x0d, 0x03, 0x7d, 0x01, 0xa5, 0x72, 0x76, 0x63, 0xad, 0xcd, 0x46, 0x50, 0xcc, 0x36, 0x0c, 0xb4, 0x3b, 0xfc, 0x37, 0x20, 0x4d,
0x44, 0x9d, 0x8f, 0x52, 0xaa, 0xcc, 0xe9, 0xa6, 0xc1, 0xba, 0x71, 0x06, 0x96, 0xd6, 0xbc, 0x05, 0xb4, 0x64, 0x9f, 0x60, 0x9d, 0x71, 0xbe, 0x6e, 0x6c, 0x18, 0xe8, 0x0b, 0x28, 0x25, 0x3a, 0x01,
0x39, 0x99, 0x00, 0x52, 0x8c, 0x9d, 0x68, 0x03, 0xd2, 0xc4, 0x1c, 0x6b, 0x0b, 0x0e, 0x55, 0xe3, 0x94, 0x52, 0x87, 0x4e, 0xb7, 0x15, 0xd6, 0x8d, 0x33, 0xb0, 0xb4, 0xe6, 0x2d, 0xc8, 0xc9, 0x04,
0x42, 0x82, 0xa4, 0xf7, 0xa1, 0x1b, 0x67, 0xd5, 0x1b, 0x33, 0xdd, 0x66, 0xca, 0x89, 0x37, 0x0c, 0x90, 0x62, 0xec, 0x44, 0xa3, 0x90, 0x26, 0xe6, 0x58, 0xe3, 0x70, 0xa8, 0x5a, 0x1b, 0x12, 0x24,
0x14, 0x02, 0x9a, 0x4e, 0xfa, 0xe8, 0xbb, 0x29, 0x5e, 0x32, 0xeb, 0x55, 0xb2, 0x6e, 0xcd, 0x87, 0xbd, 0x0f, 0xdd, 0x38, 0xab, 0x22, 0x99, 0xe9, 0x36, 0x53, 0x4e, 0xbc, 0x61, 0xa0, 0x10, 0xd0,
0xac, 0x94, 0x6a, 0x94, 0x5f, 0xbd, 0x59, 0x31, 0xfe, 0xf9, 0x66, 0xc5, 0xf8, 0xf7, 0x9b, 0x15, 0x74, 0xd2, 0x47, 0xdf, 0x4f, 0xf1, 0x92, 0x59, 0xaf, 0x92, 0x75, 0x6b, 0x3e, 0x64, 0xa5, 0x54,
0xe3, 0x30, 0x2f, 0x2b, 0xc9, 0xef, 0xfd, 0x3f, 0x00, 0x00, 0xff, 0xff, 0x12, 0xae, 0x41, 0x43, 0xa3, 0xfc, 0xea, 0xcd, 0x8a, 0xf1, 0xaf, 0x37, 0x2b, 0xc6, 0xbf, 0xdf, 0xac, 0x18, 0x87, 0x79,
0x99, 0x1e, 0x00, 0x00, 0x59, 0x6b, 0xfe, 0xe0, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xaa, 0x06, 0xac, 0x18, 0xbb, 0x1e,
0x00, 0x00,
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
@ -4030,6 +4039,13 @@ func (m *BuildHistoryRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i -= len(m.XXX_unrecognized) i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized)
} }
if m.NumWarnings != 0 {
i = encodeVarintControl(dAtA, i, uint64(m.NumWarnings))
i--
dAtA[i] = 0x1
i--
dAtA[i] = 0x98
}
if m.ExternalError != nil { if m.ExternalError != nil {
{ {
size, err := m.ExternalError.MarshalToSizedBuffer(dAtA[:i]) size, err := m.ExternalError.MarshalToSizedBuffer(dAtA[:i])
@ -5242,6 +5258,9 @@ func (m *BuildHistoryRecord) Size() (n int) {
l = m.ExternalError.Size() l = m.ExternalError.Size()
n += 2 + l + sovControl(uint64(l)) n += 2 + l + sovControl(uint64(l))
} }
if m.NumWarnings != 0 {
n += 2 + sovControl(uint64(m.NumWarnings))
}
if m.XXX_unrecognized != nil { if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized) n += len(m.XXX_unrecognized)
} }
@ -10303,6 +10322,25 @@ func (m *BuildHistoryRecord) Unmarshal(dAtA []byte) error {
return err return err
} }
iNdEx = postIndex iNdEx = postIndex
case 19:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field NumWarnings", wireType)
}
m.NumWarnings = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowControl
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.NumWarnings |= int32(b&0x7F) << shift
if b < 0x80 {
break
}
}
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipControl(dAtA[iNdEx:]) skippy, err := skipControl(dAtA[iNdEx:])

View File

@ -212,6 +212,7 @@ message BuildHistoryRecord {
int32 numTotalSteps = 16; int32 numTotalSteps = 16;
int32 numCompletedSteps = 17; int32 numCompletedSteps = 17;
Descriptor externalError = 18; Descriptor externalError = 18;
int32 numWarnings = 19;
// TODO: tags // TODO: tags
// TODO: unclipped logs // TODO: unclipped logs
} }

View File

@ -7,7 +7,6 @@ import (
"io" "io"
"maps" "maps"
"os" "os"
"path/filepath"
"strings" "strings"
"time" "time"
@ -120,7 +119,7 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
if opt.SessionPreInitialized { if opt.SessionPreInitialized {
return nil, errors.Errorf("no session provided for preinitialized option") return nil, errors.Errorf("no session provided for preinitialized option")
} }
s, err = session.NewSession(statusContext, defaultSessionName(), opt.SharedKey) s, err = session.NewSession(statusContext, opt.SharedKey)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to create session") return nil, errors.Wrap(err, "failed to create session")
} }
@ -419,14 +418,6 @@ func prepareSyncedFiles(def *llb.Definition, localMounts map[string]fsutil.FS) (
return result, nil return result, nil
} }
func defaultSessionName() string {
wd, err := os.Getwd()
if err != nil {
return "unknown"
}
return filepath.Base(wd)
}
type cacheOptions struct { type cacheOptions struct {
options controlapi.CacheOptions options controlapi.CacheOptions
contentStores map[string]content.Store // key: ID of content store ("local:" + csDir) contentStores map[string]content.Store // key: ID of content store ("local:" + csDir)

31
vendor/github.com/moby/buildkit/errdefs/errdefs.go generated vendored Normal file
View File

@ -0,0 +1,31 @@
package errdefs
import "errors"
type internalErr struct {
error
}
func (internalErr) System() {}
func (err internalErr) Unwrap() error {
return err.error
}
type system interface {
System()
}
var _ system = internalErr{}
func Internal(err error) error {
if err == nil {
return nil
}
return internalErr{err}
}
func IsInternal(err error) bool {
var s system
return errors.As(err, &s)
}

View File

@ -125,6 +125,16 @@ func parseSourceDateEpoch(v string) (*time.Time, error) {
return &tm, nil return &tm, nil
} }
func parseLocalSessionIDs(opt map[string]string) map[string]string {
m := map[string]string{}
for k, v := range opt {
if strings.HasPrefix(k, localSessionIDPrefix) {
m[strings.TrimPrefix(k, localSessionIDPrefix)] = v
}
}
return m
}
func filter(opt map[string]string, key string) map[string]string { func filter(opt map[string]string, key string) map[string]string {
m := map[string]string{} m := map[string]string{}
for k, v := range opt { for k, v := range opt {

View File

@ -26,8 +26,9 @@ import (
) )
const ( const (
buildArgPrefix = "build-arg:" buildArgPrefix = "build-arg:"
labelPrefix = "label:" labelPrefix = "label:"
localSessionIDPrefix = "local-sessionid:"
keyTarget = "target" keyTarget = "target"
keyCgroupParent = "cgroup-parent" keyCgroupParent = "cgroup-parent"
@ -79,10 +80,11 @@ type Config struct {
type Client struct { type Client struct {
Config Config
client client.Client client client.Client
ignoreCache []string ignoreCache []string
g flightcontrol.CachedGroup[*buildContext] g flightcontrol.CachedGroup[*buildContext]
bopts client.BuildOpts bopts client.BuildOpts
localsSessionIDs map[string]string
dockerignore []byte dockerignore []byte
dockerignoreName string dockerignoreName string
@ -298,6 +300,9 @@ func (bc *Client) init() error {
return errors.Wrapf(err, "failed to parse %s", keyCopyIgnoredCheckEnabled) return errors.Wrapf(err, "failed to parse %s", keyCopyIgnoredCheckEnabled)
} }
} }
bc.localsSessionIDs = parseLocalSessionIDs(opts)
return nil return nil
} }
@ -331,9 +336,14 @@ func (bc *Client) ReadEntrypoint(ctx context.Context, lang string, opts ...llb.L
filenames = append(filenames, path.Join(path.Dir(bctx.filename), strings.ToLower(DefaultDockerfileName))) filenames = append(filenames, path.Join(path.Dir(bctx.filename), strings.ToLower(DefaultDockerfileName)))
} }
sessionID := bc.bopts.SessionID
if v, ok := bc.localsSessionIDs[bctx.dockerfileLocalName]; ok {
sessionID = v
}
opts = append([]llb.LocalOption{ opts = append([]llb.LocalOption{
llb.FollowPaths(filenames), llb.FollowPaths(filenames),
llb.SessionID(bc.bopts.SessionID), llb.SessionID(sessionID),
llb.SharedKeyHint(bctx.dockerfileLocalName), llb.SharedKeyHint(bctx.dockerfileLocalName),
WithInternalName(name), WithInternalName(name),
llb.Differ(llb.DiffNone, false), llb.Differ(llb.DiffNone, false),
@ -427,8 +437,13 @@ func (bc *Client) MainContext(ctx context.Context, opts ...llb.LocalOption) (*ll
return nil, errors.Wrapf(err, "failed to read dockerignore patterns") return nil, errors.Wrapf(err, "failed to read dockerignore patterns")
} }
sessionID := bc.bopts.SessionID
if v, ok := bc.localsSessionIDs[bctx.contextLocalName]; ok {
sessionID = v
}
opts = append([]llb.LocalOption{ opts = append([]llb.LocalOption{
llb.SessionID(bc.bopts.SessionID), llb.SessionID(sessionID),
llb.ExcludePatterns(excludes), llb.ExcludePatterns(excludes),
llb.SharedKeyHint(bctx.contextLocalName), llb.SharedKeyHint(bctx.contextLocalName),
WithInternalName("load build context"), WithInternalName("load build context"),
@ -500,8 +515,12 @@ func WithInternalName(name string) llb.ConstraintsOpt {
func (bc *Client) dockerIgnorePatterns(ctx context.Context, bctx *buildContext) ([]string, error) { func (bc *Client) dockerIgnorePatterns(ctx context.Context, bctx *buildContext) ([]string, error) {
if bc.dockerignore == nil { if bc.dockerignore == nil {
sessionID := bc.bopts.SessionID
if v, ok := bc.localsSessionIDs[bctx.contextLocalName]; ok {
sessionID = v
}
st := llb.Local(bctx.contextLocalName, st := llb.Local(bctx.contextLocalName,
llb.SessionID(bc.bopts.SessionID), llb.SessionID(sessionID),
llb.FollowPaths([]string{DefaultDockerignoreName}), llb.FollowPaths([]string{DefaultDockerignoreName}),
llb.SharedKeyHint(bctx.contextLocalName+"-"+DefaultDockerignoreName), llb.SharedKeyHint(bctx.contextLocalName+"-"+DefaultDockerignoreName),
WithInternalName("load "+DefaultDockerignoreName), WithInternalName("load "+DefaultDockerignoreName),

View File

@ -187,8 +187,12 @@ func (bc *Client) namedContextRecursive(ctx context.Context, name string, nameWi
} }
return &st, &img, nil return &st, &img, nil
case "local": case "local":
sessionID := bc.bopts.SessionID
if v, ok := bc.localsSessionIDs[vv[1]]; ok {
sessionID = v
}
st := llb.Local(vv[1], st := llb.Local(vv[1],
llb.SessionID(bc.bopts.SessionID), llb.SessionID(sessionID),
llb.FollowPaths([]string{DefaultDockerignoreName}), llb.FollowPaths([]string{DefaultDockerignoreName}),
llb.SharedKeyHint("context:"+nameWithPlatform+"-"+DefaultDockerignoreName), llb.SharedKeyHint("context:"+nameWithPlatform+"-"+DefaultDockerignoreName),
llb.WithCustomName("[context "+nameWithPlatform+"] load "+DefaultDockerignoreName), llb.WithCustomName("[context "+nameWithPlatform+"] load "+DefaultDockerignoreName),
@ -226,7 +230,7 @@ func (bc *Client) namedContextRecursive(ctx context.Context, name string, nameWi
localOutput := &asyncLocalOutput{ localOutput := &asyncLocalOutput{
name: vv[1], name: vv[1],
nameWithPlatform: nameWithPlatform, nameWithPlatform: nameWithPlatform,
sessionID: bc.bopts.SessionID, sessionID: sessionID,
excludes: excludes, excludes: excludes,
extraOpts: opt.AsyncLocalOpts, extraOpts: opt.AsyncLocalOpts,
} }

View File

@ -16,7 +16,6 @@ type Caller interface {
Context() context.Context Context() context.Context
Supports(method string) bool Supports(method string) bool
Conn() *grpc.ClientConn Conn() *grpc.ClientConn
Name() string
SharedKey() string SharedKey() string
} }
@ -106,7 +105,6 @@ func (sm *Manager) handleConn(ctx context.Context, conn net.Conn, opts map[strin
h := http.Header(opts) h := http.Header(opts)
id := h.Get(headerSessionID) id := h.Get(headerSessionID)
name := h.Get(headerSessionName)
sharedKey := h.Get(headerSessionSharedKey) sharedKey := h.Get(headerSessionSharedKey)
ctx, cc, err := grpcClientConn(ctx, conn) ctx, cc, err := grpcClientConn(ctx, conn)
@ -118,7 +116,6 @@ func (sm *Manager) handleConn(ctx context.Context, conn net.Conn, opts map[strin
c := &client{ c := &client{
Session: Session{ Session: Session{
id: id, id: id,
name: name,
sharedKey: sharedKey, sharedKey: sharedKey,
ctx: ctx, ctx: ctx,
cancelCtx: cancel, cancelCtx: cancel,
@ -197,10 +194,6 @@ func (c *client) Context() context.Context {
return c.context() return c.context()
} }
func (c *client) Name() string {
return c.name
}
func (c *client) SharedKey() string { func (c *client) SharedKey() string {
return c.sharedKey return c.sharedKey
} }

View File

@ -38,7 +38,6 @@ type Attachable interface {
type Session struct { type Session struct {
mu sync.Mutex // synchronizes conn run and close mu sync.Mutex // synchronizes conn run and close
id string id string
name string
sharedKey string sharedKey string
ctx context.Context ctx context.Context
cancelCtx func(error) cancelCtx func(error)
@ -49,7 +48,7 @@ type Session struct {
} }
// NewSession returns a new long running session // NewSession returns a new long running session
func NewSession(ctx context.Context, name, sharedKey string) (*Session, error) { func NewSession(ctx context.Context, sharedKey string) (*Session, error) {
id := identity.NewID() id := identity.NewID()
serverOpts := []grpc.ServerOption{ serverOpts := []grpc.ServerOption{
@ -67,7 +66,6 @@ func NewSession(ctx context.Context, name, sharedKey string) (*Session, error) {
s := &Session{ s := &Session{
id: id, id: id,
name: name,
sharedKey: sharedKey, sharedKey: sharedKey,
grpcServer: grpc.NewServer(serverOpts...), grpcServer: grpc.NewServer(serverOpts...),
} }
@ -103,7 +101,6 @@ func (s *Session) Run(ctx context.Context, dialer Dialer) error {
meta := make(map[string][]string) meta := make(map[string][]string)
meta[headerSessionID] = []string{s.id} meta[headerSessionID] = []string{s.id}
meta[headerSessionName] = []string{s.name}
meta[headerSessionSharedKey] = []string{s.sharedKey} meta[headerSessionSharedKey] = []string{s.sharedKey}
for name, svc := range s.grpcServer.GetServiceInfo() { for name, svc := range s.grpcServer.GetServiceInfo() {

View File

@ -10,6 +10,7 @@ import (
gogotypes "github.com/gogo/protobuf/types" gogotypes "github.com/gogo/protobuf/types"
"github.com/golang/protobuf/proto" //nolint:staticcheck "github.com/golang/protobuf/proto" //nolint:staticcheck
"github.com/golang/protobuf/ptypes/any" "github.com/golang/protobuf/ptypes/any"
"github.com/moby/buildkit/errdefs"
"github.com/moby/buildkit/util/bklog" "github.com/moby/buildkit/util/bklog"
"github.com/moby/buildkit/util/stack" "github.com/moby/buildkit/util/stack"
spb "google.golang.org/genproto/googleapis/rpc/status" spb "google.golang.org/genproto/googleapis/rpc/status"
@ -94,6 +95,10 @@ func withDetails(ctx context.Context, s *status.Status, details ...proto.Message
} }
func Code(err error) codes.Code { func Code(err error) codes.Code {
if errdefs.IsInternal(err) {
return codes.Internal
}
if se, ok := err.(interface { if se, ok := err.(interface {
Code() codes.Code Code() codes.Code
}); ok { }); ok {

View File

@ -64,11 +64,7 @@ func retryError(err error) bool {
return true return true
} }
// catches TLS timeout or other network-related temporary errors // catches TLS timeout or other network-related temporary errors
if ne, ok := errors.Cause(err).(net.Error); ok && ne.Temporary() { //nolint:staticcheck // ignoring "SA1019: Temporary is deprecated", continue to propagate net.Error through the "temporary" status if ne := net.Error(nil); errors.As(err, &ne) && ne.Temporary() { //nolint:staticcheck // ignoring "SA1019: Temporary is deprecated", continue to propagate net.Error through the "temporary" status
return true
}
// https://github.com/containerd/containerd/pull/4724
if errors.Cause(err).Error() == "no response" {
return true return true
} }

View File

@ -1,3 +1,6 @@
//go:build !windows
// +build !windows
package integration package integration
var pins = map[string]map[string]string{ var pins = map[string]map[string]string{

View File

@ -189,7 +189,9 @@ func Run(t *testing.T, testCases []Test, opt ...TestOpt) {
t.Skip("rootless") t.Skip("rootless")
} }
ctx := appcontext.Context() ctx := appcontext.Context()
if !strings.HasSuffix(fn, "NoParallel") { // TODO(profnandaa): to revisit this to allow tests run
// in parallel on Windows in a stable way. Is flaky currently.
if !strings.HasSuffix(fn, "NoParallel") && runtime.GOOS != "windows" {
t.Parallel() t.Parallel()
} }
require.NoError(t, sandboxLimiter.Acquire(context.TODO(), 1)) require.NoError(t, sandboxLimiter.Acquire(context.TODO(), 1))
@ -273,23 +275,7 @@ func copyImagesLocal(t *testing.T, host string, images map[string]string) error
} }
func OfficialImages(names ...string) map[string]string { func OfficialImages(names ...string) map[string]string {
ns := runtime.GOARCH return officialImages(names...)
if ns == "arm64" {
ns = "arm64v8"
} else if ns != "amd64" {
ns = "library"
}
m := map[string]string{}
for _, name := range names {
ref := "docker.io/" + ns + "/" + name
if pns, ok := pins[name]; ok {
if dgst, ok := pns[ns]; ok {
ref += "@" + dgst
}
}
m["library/"+name] = ref
}
return m
} }
func withMirrorConfig(mirror string) ConfigUpdater { func withMirrorConfig(mirror string) ConfigUpdater {
@ -439,9 +425,19 @@ func prepareValueMatrix(tc testConf) []matrixValue {
return m return m
} }
// Skips tests on Windows // Skips tests on platform
func SkipOnPlatform(t *testing.T, goos string) { func SkipOnPlatform(t *testing.T, goos string) {
if runtime.GOOS == goos { if runtime.GOOS == goos {
t.Skipf("Skipped on %s", goos) t.Skipf("Skipped on %s", goos)
} }
} }
// Selects between two types, returns second
// argument if on Windows or else first argument.
// Typically used for selecting test cases.
func UnixOrWindows[T any](unix, windows T) T {
if runtime.GOOS == "windows" {
return windows
}
return unix
}

View File

@ -0,0 +1,26 @@
//go:build !windows
// +build !windows
package integration
import "runtime"
func officialImages(names ...string) map[string]string {
ns := runtime.GOARCH
if ns == "arm64" {
ns = "arm64v8"
} else if ns != "amd64" {
ns = "library"
}
m := map[string]string{}
for _, name := range names {
ref := "docker.io/" + ns + "/" + name
if pns, ok := pins[name]; ok {
if dgst, ok := pns[ns]; ok {
ref += "@" + dgst
}
}
m["library/"+name] = ref
}
return m
}

View File

@ -0,0 +1,13 @@
package integration
func officialImages(names ...string) map[string]string {
m := map[string]string{}
for _, name := range names {
// select available refs from the mirror map
// so that we mirror only those needed for the tests
if ref, ok := windowsImagesMirrorMap[name]; ok {
m["library/"+name] = ref
}
}
return m
}

View File

@ -94,8 +94,12 @@ func StartCmd(cmd *exec.Cmd, logs map[string]*bytes.Buffer) (func() error, error
case <-ctx.Done(): case <-ctx.Done():
case <-stopped: case <-stopped:
case <-stop: case <-stop:
// windows processes not responding to SIGTERM
signal := UnixOrWindows(syscall.SIGTERM, syscall.SIGKILL)
signalStr := UnixOrWindows("SIGTERM", "SIGKILL")
fmt.Fprintf(cmd.Stderr, "> sending sigterm %v\n", time.Now()) fmt.Fprintf(cmd.Stderr, "> sending sigterm %v\n", time.Now())
cmd.Process.Signal(syscall.SIGTERM) fmt.Fprintf(cmd.Stderr, "> sending %s %v\n", signalStr, time.Now())
cmd.Process.Signal(signal)
go func() { go func() {
select { select {
case <-stopped: case <-stopped:

View File

@ -11,6 +11,13 @@ import (
var socketScheme = "npipe://" var socketScheme = "npipe://"
var windowsImagesMirrorMap = map[string]string{
// TODO(profnandaa): currently, amd64 only, to revisit for other archs.
"nanoserver:latest": "mcr.microsoft.com/windows/nanoserver:ltsc2022",
"servercore:latest": "mcr.microsoft.com/windows/servercore:ltsc2022",
"busybox:latest": "registry.k8s.io/e2e-test-images/busybox@sha256:6d854ffad9666d2041b879a1c128c9922d77faced7745ad676639b07111ab650",
}
// abstracted function to handle pipe dialing on windows. // abstracted function to handle pipe dialing on windows.
// some simplification has been made to discard timeout param. // some simplification has been made to discard timeout param.
func dialPipe(address string) (net.Conn, error) { func dialPipe(address string) (net.Conn, error) {

View File

@ -197,7 +197,6 @@ func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) {
for { for {
var line []byte var line []byte
line, isPrefix, err = rd.ReadLine() line, isPrefix, err = rd.ReadLine()
if err != nil { if err != nil {
// We should return no error if EOF is reached // We should return no error if EOF is reached
// without a match. // without a match.

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@ -61,7 +61,11 @@ func newClient(ctx context.Context, cfg oconf.Config) (*client, error) {
if c.conn == nil { if c.conn == nil {
// If the caller did not provide a ClientConn when the client was // If the caller did not provide a ClientConn when the client was
// created, create one using the configuration they did provide. // created, create one using the configuration they did provide.
conn, err := grpc.DialContext(ctx, cfg.Metrics.Endpoint, cfg.DialOptions...) userAgent := "OTel Go OTLP over gRPC metrics exporter/" + Version()
dialOpts := []grpc.DialOption{grpc.WithUserAgent(userAgent)}
dialOpts = append(dialOpts, cfg.DialOptions...)
conn, err := grpc.DialContext(ctx, cfg.Metrics.Endpoint, dialOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -172,28 +176,36 @@ func (c *client) exportContext(parent context.Context) (context.Context, context
// duration to wait for if an explicit throttle time is included in err. // duration to wait for if an explicit throttle time is included in err.
func retryable(err error) (bool, time.Duration) { func retryable(err error) (bool, time.Duration) {
s := status.Convert(err) s := status.Convert(err)
return retryableGRPCStatus(s)
}
func retryableGRPCStatus(s *status.Status) (bool, time.Duration) {
switch s.Code() { switch s.Code() {
case codes.Canceled, case codes.Canceled,
codes.DeadlineExceeded, codes.DeadlineExceeded,
codes.ResourceExhausted,
codes.Aborted, codes.Aborted,
codes.OutOfRange, codes.OutOfRange,
codes.Unavailable, codes.Unavailable,
codes.DataLoss: codes.DataLoss:
return true, throttleDelay(s) // Additionally, handle RetryInfo.
_, d := throttleDelay(s)
return true, d
case codes.ResourceExhausted:
// Retry only if the server signals that the recovery from resource exhaustion is possible.
return throttleDelay(s)
} }
// Not a retry-able error. // Not a retry-able error.
return false, 0 return false, 0
} }
// throttleDelay returns a duration to wait for if an explicit throttle time // throttleDelay returns if the status is RetryInfo
// is included in the response status. // and the duration to wait for if an explicit throttle time is included.
func throttleDelay(s *status.Status) time.Duration { func throttleDelay(s *status.Status) (bool, time.Duration) {
for _, detail := range s.Details() { for _, detail := range s.Details() {
if t, ok := detail.(*errdetails.RetryInfo); ok { if t, ok := detail.(*errdetails.RetryInfo); ok {
return t.RetryDelay.AsDuration() return true, t.RetryDelay.AsDuration()
} }
} }
return 0 return false, 0
} }

View File

@ -109,13 +109,7 @@ func compressorToCompression(compressor string) oconf.Compression {
} }
// WithCompressor sets the compressor the gRPC client uses. // WithCompressor sets the compressor the gRPC client uses.
// // Supported compressor values: "gzip".
// It is the responsibility of the caller to ensure that the compressor set
// has been registered with google.golang.org/grpc/encoding (see
// encoding.RegisterCompressor for more information). For example, to register
// the gzip compressor import the package:
//
// import _ "google.golang.org/grpc/encoding/gzip"
// //
// If the OTEL_EXPORTER_OTLP_COMPRESSION or // If the OTEL_EXPORTER_OTLP_COMPRESSION or
// OTEL_EXPORTER_OTLP_METRICS_COMPRESSION environment variable is set, and // OTEL_EXPORTER_OTLP_METRICS_COMPRESSION environment variable is set, and

View File

@ -12,6 +12,85 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// Package otlpmetricgrpc provides an otlpmetric.Exporter that communicates /*
// with an OTLP receiving endpoint using gRPC. Package otlpmetricgrpc provides an OTLP metrics exporter using gRPC.
By default the telemetry is sent to https://localhost:4317.
Exporter should be created using [New] and used with a [metric.PeriodicReader].
The environment variables described below can be used for configuration.
OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT (default: "https://localhost:4317") -
target to which the exporter sends telemetry.
The target syntax is defined in https://github.com/grpc/grpc/blob/master/doc/naming.md.
The value must contain a host.
The value may additionally a port, a scheme, and a path.
The value accepts "http" and "https" scheme.
The value should not contain a query string or fragment.
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT takes precedence over OTEL_EXPORTER_OTLP_ENDPOINT.
The configuration can be overridden by [WithEndpoint], [WithInsecure], [WithGRPCConn] options.
OTEL_EXPORTER_OTLP_INSECURE, OTEL_EXPORTER_OTLP_METRICS_INSECURE (default: "false") -
setting "true" disables client transport security for the exporter's gRPC connection.
You can use this only when an endpoint is provided without the http or https scheme.
OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT setting overrides
the scheme defined via OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT.
OTEL_EXPORTER_OTLP_METRICS_INSECURE takes precedence over OTEL_EXPORTER_OTLP_INSECURE.
The configuration can be overridden by [WithInsecure], [WithGRPCConn] options.
OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_METRICS_HEADERS (default: none) -
key-value pairs used as gRPC metadata associated with gRPC requests.
The value is expected to be represented in a format matching to the [W3C Baggage HTTP Header Content Format],
except that additional semi-colon delimited metadata is not supported.
Example value: "key1=value1,key2=value2".
OTEL_EXPORTER_OTLP_METRICS_HEADERS takes precedence over OTEL_EXPORTER_OTLP_HEADERS.
The configuration can be overridden by [WithHeaders] option.
OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_METRICS_TIMEOUT (default: "10000") -
maximum time in milliseconds the OTLP exporter waits for each batch export.
OTEL_EXPORTER_OTLP_METRICS_TIMEOUT takes precedence over OTEL_EXPORTER_OTLP_TIMEOUT.
The configuration can be overridden by [WithTimeout] option.
OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_METRICS_COMPRESSION (default: none) -
the gRPC compressor the exporter uses.
Supported value: "gzip".
OTEL_EXPORTER_OTLP_METRICS_COMPRESSION takes precedence over OTEL_EXPORTER_OTLP_COMPRESSION.
The configuration can be overridden by [WithCompressor], [WithGRPCConn] options.
OTEL_EXPORTER_OTLP_CERTIFICATE, OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE (default: none) -
the filepath to the trusted certificate to use when verifying a server's TLS credentials.
OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE takes precedence over OTEL_EXPORTER_OTLP_CERTIFICATE.
The configuration can be overridden by [WithTLSCredentials], [WithGRPCConn] options.
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE (default: none) -
the filepath to the client certificate/chain trust for clients private key to use in mTLS communication in PEM format.
OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE takes precedence over OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE.
The configuration can be overridden by [WithTLSCredentials], [WithGRPCConn] options.
OTEL_EXPORTER_OTLP_CLIENT_KEY, OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY (default: none) -
the filepath to the clients private key to use in mTLS communication in PEM format.
OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY takes precedence over OTEL_EXPORTER_OTLP_CLIENT_KEY.
The configuration can be overridden by [WithTLSCredentials], [WithGRPCConn] option.
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE (default: "cumulative") -
aggregation temporality to use on the basis of instrument kind. Supported values:
- "cumulative" - Cumulative aggregation temporality for all instrument kinds,
- "delta" - Delta aggregation temporality for Counter, Asynchronous Counter and Histogram instrument kinds;
Cumulative aggregation for UpDownCounter and Asynchronous UpDownCounter instrument kinds,
- "lowmemory" - Delta aggregation temporality for Synchronous Counter and Histogram instrument kinds;
Cumulative aggregation temporality for Synchronous UpDownCounter, Asynchronous Counter, and Asynchronous UpDownCounter instrument kinds.
The configuration can be overridden by [WithTemporalitySelector] option.
OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION (default: "explicit_bucket_histogram") -
default aggregation to use for histogram instruments. Supported values:
- "explicit_bucket_histogram" - [Explicit Bucket Histogram Aggregation],
- "base2_exponential_bucket_histogram" - [Base2 Exponential Bucket Histogram Aggregation].
The configuration can be overridden by [WithAggregationSelector] option.
[W3C Baggage HTTP Header Content Format]: https://www.w3.org/TR/baggage/#header-content
[Explicit Bucket Histogram Aggregation]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.26.0/specification/metrics/sdk.md#explicit-bucket-histogram-aggregation
[Base2 Exponential Bucket Histogram Aggregation]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.26.0/specification/metrics/sdk.md#base2-exponential-bucket-histogram-aggregation
*/
package otlpmetricgrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" package otlpmetricgrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"

View File

@ -174,13 +174,13 @@ func stringToHeader(value string) map[string]string {
global.Error(errors.New("missing '="), "parse headers", "input", header) global.Error(errors.New("missing '="), "parse headers", "input", header)
continue continue
} }
name, err := url.QueryUnescape(n) name, err := url.PathUnescape(n)
if err != nil { if err != nil {
global.Error(err, "escape header key", "key", n) global.Error(err, "escape header key", "key", n)
continue continue
} }
trimmedName := strings.TrimSpace(name) trimmedName := strings.TrimSpace(name)
value, err := url.QueryUnescape(v) value, err := url.PathUnescape(v)
if err != nil { if err != nil {
global.Error(err, "escape header value", "value", v) global.Error(err, "escape header value", "value", v)
continue continue

View File

@ -30,7 +30,6 @@ import (
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/encoding/gzip" "google.golang.org/grpc/encoding/gzip"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/retry" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/retry"
"go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/metric"
) )
@ -122,7 +121,6 @@ func cleanPath(urlPath string, defaultPath string) string {
// NewGRPCConfig returns a new Config with all settings applied from opts and // NewGRPCConfig returns a new Config with all settings applied from opts and
// any unset setting using the default gRPC config values. // any unset setting using the default gRPC config values.
func NewGRPCConfig(opts ...GRPCOption) Config { func NewGRPCConfig(opts ...GRPCOption) Config {
userAgent := "OTel OTLP Exporter Go/" + otlpmetric.Version()
cfg := Config{ cfg := Config{
Metrics: SignalConfig{ Metrics: SignalConfig{
Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorGRPCPort), Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorGRPCPort),
@ -134,7 +132,6 @@ func NewGRPCConfig(opts ...GRPCOption) Config {
AggregationSelector: metric.DefaultAggregationSelector, AggregationSelector: metric.DefaultAggregationSelector,
}, },
RetryConfig: retry.DefaultConfig, RetryConfig: retry.DefaultConfig,
DialOptions: []grpc.DialOption{grpc.WithUserAgent(userAgent)},
} }
cfg = ApplyGRPCEnvConfigs(cfg) cfg = ApplyGRPCEnvConfigs(cfg)
for _, opt := range opts { for _, opt := range opts {
@ -158,9 +155,6 @@ func NewGRPCConfig(opts ...GRPCOption) Config {
if cfg.Metrics.Compression == GzipCompression { if cfg.Metrics.Compression == GzipCompression {
cfg.DialOptions = append(cfg.DialOptions, grpc.WithDefaultCallOptions(grpc.UseCompressor(gzip.Name))) cfg.DialOptions = append(cfg.DialOptions, grpc.WithDefaultCallOptions(grpc.UseCompressor(gzip.Name)))
} }
if len(cfg.DialOptions) != 0 {
cfg.DialOptions = append(cfg.DialOptions, cfg.DialOptions...)
}
if cfg.ReconnectionPeriod != 0 { if cfg.ReconnectionPeriod != 0 {
p := grpc.ConnectParams{ p := grpc.ConnectParams{
Backoff: backoff.DefaultConfig, Backoff: backoff.DefaultConfig,

View File

@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package otlpmetric // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" package otlpmetricgrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
// Version is the current release version of the OpenTelemetry OTLP metrics exporter in use. // Version is the current release version of the OpenTelemetry OTLP over gRPC metrics exporter in use.
func Version() string { func Version() string {
return "0.42.0" return "0.44.0"
} }

View File

@ -18,6 +18,7 @@ import (
"bytes" "bytes"
"compress/gzip" "compress/gzip"
"context" "context"
"errors"
"fmt" "fmt"
"io" "io"
"net" "net"
@ -30,7 +31,6 @@ import (
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"go.opentelemetry.io/otel" "go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/retry" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/retry"
@ -89,7 +89,7 @@ func newClient(cfg oconf.Config) (*client, error) {
return nil, err return nil, err
} }
userAgent := "OTel OTLP Exporter Go/" + otlpmetric.Version() userAgent := "OTel Go OTLP over HTTP/protobuf metrics exporter/" + Version()
req.Header.Set("User-Agent", userAgent) req.Header.Set("User-Agent", userAgent)
if n := len(cfg.Metrics.Headers); n > 0 { if n := len(cfg.Metrics.Headers); n > 0 {
@ -148,6 +148,10 @@ func (c *client) UploadMetrics(ctx context.Context, protoMetrics *metricpb.Resou
request.reset(iCtx) request.reset(iCtx)
resp, err := c.httpClient.Do(request.Request) resp, err := c.httpClient.Do(request.Request)
var urlErr *url.Error
if errors.As(err, &urlErr) && urlErr.Temporary() {
return newResponseError(http.Header{})
}
if err != nil { if err != nil {
return err return err
} }
@ -162,8 +166,11 @@ func (c *client) UploadMetrics(ctx context.Context, protoMetrics *metricpb.Resou
if _, err := io.Copy(&respData, resp.Body); err != nil { if _, err := io.Copy(&respData, resp.Body); err != nil {
return err return err
} }
if respData.Len() == 0 {
return nil
}
if respData.Len() != 0 { if resp.Header.Get("Content-Type") == "application/x-protobuf" {
var respProto colmetricpb.ExportMetricsServiceResponse var respProto colmetricpb.ExportMetricsServiceResponse
if err := proto.Unmarshal(respData.Bytes(), &respProto); err != nil { if err := proto.Unmarshal(respData.Bytes(), &respProto); err != nil {
return err return err
@ -179,7 +186,10 @@ func (c *client) UploadMetrics(ctx context.Context, protoMetrics *metricpb.Resou
} }
} }
return nil return nil
case sc == http.StatusTooManyRequests, sc == http.StatusServiceUnavailable: case sc == http.StatusTooManyRequests,
sc == http.StatusBadGateway,
sc == http.StatusServiceUnavailable,
sc == http.StatusGatewayTimeout:
// Retry-able failure. // Retry-able failure.
rErr = newResponseError(resp.Header) rErr = newResponseError(resp.Header)

View File

@ -12,7 +12,82 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// Package otlpmetrichttp provides an otlpmetric.Exporter that communicates /*
// with an OTLP receiving endpoint using protobuf encoded metric data over Package otlpmetrichttp provides an OTLP metrics exporter using HTTP with protobuf payloads.
// HTTP. By default the telemetry is sent to https://localhost:4318/v1/metrics.
Exporter should be created using [New] and used with a [metric.PeriodicReader].
The environment variables described below can be used for configuration.
OTEL_EXPORTER_OTLP_ENDPOINT (default: "https://localhost:4318") -
target base URL ("/v1/metrics" is appended) to which the exporter sends telemetry.
The value must contain a scheme ("http" or "https") and host.
The value may additionally contain a port and a path.
The value should not contain a query string or fragment.
The configuration can be overridden by OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
environment variable and by [WithEndpoint], [WithInsecure] options.
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT (default: "https://localhost:4318/v1/metrics") -
target URL to which the exporter sends telemetry.
The value must contain a scheme ("http" or "https") and host.
The value may additionally contain a port and a path.
The value should not contain a query string or fragment.
The configuration can be overridden by [WithEndpoint], [WitnInsecure], [WithURLPath] options.
OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_METRICS_HEADERS (default: none) -
key-value pairs used as headers associated with HTTP requests.
The value is expected to be represented in a format matching to the [W3C Baggage HTTP Header Content Format],
except that additional semi-colon delimited metadata is not supported.
Example value: "key1=value1,key2=value2".
OTEL_EXPORTER_OTLP_METRICS_HEADERS takes precedence over OTEL_EXPORTER_OTLP_HEADERS.
The configuration can be overridden by [WithHeaders] option.
OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_METRICS_TIMEOUT (default: "10000") -
maximum time in milliseconds the OTLP exporter waits for each batch export.
OTEL_EXPORTER_OTLP_METRICS_TIMEOUT takes precedence over OTEL_EXPORTER_OTLP_TIMEOUT.
The configuration can be overridden by [WithTimeout] option.
OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_METRICS_COMPRESSION (default: none) -
compression strategy the exporter uses to compress the HTTP body.
Supported values: "gzip".
OTEL_EXPORTER_OTLP_METRICS_COMPRESSION takes precedence over OTEL_EXPORTER_OTLP_COMPRESSION.
The configuration can be overridden by [WithCompression] option.
OTEL_EXPORTER_OTLP_CERTIFICATE, OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE (default: none) -
filepath to the trusted certificate to use when verifying a server's TLS credentials.
OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE takes precedence over OTEL_EXPORTER_OTLP_CERTIFICATE.
The configuration can be overridden by [WithTLSClientConfig] option.
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE (default: none) -
filepath to the client certificate/chain trust for clients private key to use in mTLS communication in PEM format.
OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE takes precedence over OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE.
The configuration can be overridden by [WithTLSClientConfig] option.
OTEL_EXPORTER_OTLP_CLIENT_KEY, OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY (default: none) -
filepath to the clients private key to use in mTLS communication in PEM format.
OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY takes precedence over OTEL_EXPORTER_OTLP_CLIENT_KEY.
The configuration can be overridden by [WithTLSClientConfig] option.
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE (default: "cumulative") -
aggregation temporality to use on the basis of instrument kind. Supported values:
- "cumulative" - Cumulative aggregation temporality for all instrument kinds,
- "delta" - Delta aggregation temporality for Counter, Asynchronous Counter and Histogram instrument kinds;
Cumulative aggregation for UpDownCounter and Asynchronous UpDownCounter instrument kinds,
- "lowmemory" - Delta aggregation temporality for Synchronous Counter and Histogram instrument kinds;
Cumulative aggregation temporality for Synchronous UpDownCounter, Asynchronous Counter, and Asynchronous UpDownCounter instrument kinds.
The configuration can be overridden by [WithTemporalitySelector] option.
OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION (default: "explicit_bucket_histogram") -
default aggregation to use for histogram instruments. Supported values:
- "explicit_bucket_histogram" - [Explicit Bucket Histogram Aggregation],
- "base2_exponential_bucket_histogram" - [Base2 Exponential Bucket Histogram Aggregation].
The configuration can be overridden by [WithAggregationSelector] option.
[W3C Baggage HTTP Header Content Format]: https://www.w3.org/TR/baggage/#header-content
[Explicit Bucket Histogram Aggregation]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.26.0/specification/metrics/sdk.md#explicit-bucket-histogram-aggregation
[Base2 Exponential Bucket Histogram Aggregation]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.26.0/specification/metrics/sdk.md#base2-exponential-bucket-histogram-aggregation
*/
package otlpmetrichttp // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" package otlpmetrichttp // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"

View File

@ -174,13 +174,13 @@ func stringToHeader(value string) map[string]string {
global.Error(errors.New("missing '="), "parse headers", "input", header) global.Error(errors.New("missing '="), "parse headers", "input", header)
continue continue
} }
name, err := url.QueryUnescape(n) name, err := url.PathUnescape(n)
if err != nil { if err != nil {
global.Error(err, "escape header key", "key", n) global.Error(err, "escape header key", "key", n)
continue continue
} }
trimmedName := strings.TrimSpace(name) trimmedName := strings.TrimSpace(name)
value, err := url.QueryUnescape(v) value, err := url.PathUnescape(v)
if err != nil { if err != nil {
global.Error(err, "escape header value", "value", v) global.Error(err, "escape header value", "value", v)
continue continue

View File

@ -30,7 +30,6 @@ import (
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/encoding/gzip" "google.golang.org/grpc/encoding/gzip"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/retry" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/retry"
"go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/metric"
) )
@ -122,7 +121,6 @@ func cleanPath(urlPath string, defaultPath string) string {
// NewGRPCConfig returns a new Config with all settings applied from opts and // NewGRPCConfig returns a new Config with all settings applied from opts and
// any unset setting using the default gRPC config values. // any unset setting using the default gRPC config values.
func NewGRPCConfig(opts ...GRPCOption) Config { func NewGRPCConfig(opts ...GRPCOption) Config {
userAgent := "OTel OTLP Exporter Go/" + otlpmetric.Version()
cfg := Config{ cfg := Config{
Metrics: SignalConfig{ Metrics: SignalConfig{
Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorGRPCPort), Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorGRPCPort),
@ -134,7 +132,6 @@ func NewGRPCConfig(opts ...GRPCOption) Config {
AggregationSelector: metric.DefaultAggregationSelector, AggregationSelector: metric.DefaultAggregationSelector,
}, },
RetryConfig: retry.DefaultConfig, RetryConfig: retry.DefaultConfig,
DialOptions: []grpc.DialOption{grpc.WithUserAgent(userAgent)},
} }
cfg = ApplyGRPCEnvConfigs(cfg) cfg = ApplyGRPCEnvConfigs(cfg)
for _, opt := range opts { for _, opt := range opts {
@ -158,9 +155,6 @@ func NewGRPCConfig(opts ...GRPCOption) Config {
if cfg.Metrics.Compression == GzipCompression { if cfg.Metrics.Compression == GzipCompression {
cfg.DialOptions = append(cfg.DialOptions, grpc.WithDefaultCallOptions(grpc.UseCompressor(gzip.Name))) cfg.DialOptions = append(cfg.DialOptions, grpc.WithDefaultCallOptions(grpc.UseCompressor(gzip.Name)))
} }
if len(cfg.DialOptions) != 0 {
cfg.DialOptions = append(cfg.DialOptions, cfg.DialOptions...)
}
if cfg.ReconnectionPeriod != 0 { if cfg.ReconnectionPeriod != 0 {
p := grpc.ConnectParams{ p := grpc.ConnectParams{
Backoff: backoff.DefaultConfig, Backoff: backoff.DefaultConfig,

View File

@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// Package otlpmetric provides an OpenTelemetry metric Exporter that can be package otlpmetrichttp // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
// used with PeriodicReader. It transforms metricdata into OTLP and transmits
// the transformed data to OTLP receivers. The Exporter is configurable to use // Version is the current release version of the OpenTelemetry OTLP over HTTP/protobuf metrics exporter in use.
// different Clients, each using a distinct transport protocol to communicate func Version() string {
// to an OTLP receiving endpoint. return "0.44.0"
package otlpmetric // import "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" }

198
vendor/golang.org/x/oauth2/deviceauth.go generated vendored Normal file
View File

@ -0,0 +1,198 @@
package oauth2
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
"golang.org/x/oauth2/internal"
)
// https://datatracker.ietf.org/doc/html/rfc8628#section-3.5
const (
errAuthorizationPending = "authorization_pending"
errSlowDown = "slow_down"
errAccessDenied = "access_denied"
errExpiredToken = "expired_token"
)
// DeviceAuthResponse describes a successful RFC 8628 Device Authorization Response
// https://datatracker.ietf.org/doc/html/rfc8628#section-3.2
type DeviceAuthResponse struct {
// DeviceCode
DeviceCode string `json:"device_code"`
// UserCode is the code the user should enter at the verification uri
UserCode string `json:"user_code"`
// VerificationURI is where user should enter the user code
VerificationURI string `json:"verification_uri"`
// VerificationURIComplete (if populated) includes the user code in the verification URI. This is typically shown to the user in non-textual form, such as a QR code.
VerificationURIComplete string `json:"verification_uri_complete,omitempty"`
// Expiry is when the device code and user code expire
Expiry time.Time `json:"expires_in,omitempty"`
// Interval is the duration in seconds that Poll should wait between requests
Interval int64 `json:"interval,omitempty"`
}
func (d DeviceAuthResponse) MarshalJSON() ([]byte, error) {
type Alias DeviceAuthResponse
var expiresIn int64
if !d.Expiry.IsZero() {
expiresIn = int64(time.Until(d.Expiry).Seconds())
}
return json.Marshal(&struct {
ExpiresIn int64 `json:"expires_in,omitempty"`
*Alias
}{
ExpiresIn: expiresIn,
Alias: (*Alias)(&d),
})
}
func (c *DeviceAuthResponse) UnmarshalJSON(data []byte) error {
type Alias DeviceAuthResponse
aux := &struct {
ExpiresIn int64 `json:"expires_in"`
// workaround misspelling of verification_uri
VerificationURL string `json:"verification_url"`
*Alias
}{
Alias: (*Alias)(c),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
if aux.ExpiresIn != 0 {
c.Expiry = time.Now().UTC().Add(time.Second * time.Duration(aux.ExpiresIn))
}
if c.VerificationURI == "" {
c.VerificationURI = aux.VerificationURL
}
return nil
}
// DeviceAuth returns a device auth struct which contains a device code
// and authorization information provided for users to enter on another device.
func (c *Config) DeviceAuth(ctx context.Context, opts ...AuthCodeOption) (*DeviceAuthResponse, error) {
// https://datatracker.ietf.org/doc/html/rfc8628#section-3.1
v := url.Values{
"client_id": {c.ClientID},
}
if len(c.Scopes) > 0 {
v.Set("scope", strings.Join(c.Scopes, " "))
}
for _, opt := range opts {
opt.setValue(v)
}
return retrieveDeviceAuth(ctx, c, v)
}
func retrieveDeviceAuth(ctx context.Context, c *Config, v url.Values) (*DeviceAuthResponse, error) {
if c.Endpoint.DeviceAuthURL == "" {
return nil, errors.New("endpoint missing DeviceAuthURL")
}
req, err := http.NewRequest("POST", c.Endpoint.DeviceAuthURL, strings.NewReader(v.Encode()))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "application/json")
t := time.Now()
r, err := internal.ContextClient(ctx).Do(req)
if err != nil {
return nil, err
}
body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20))
if err != nil {
return nil, fmt.Errorf("oauth2: cannot auth device: %v", err)
}
if code := r.StatusCode; code < 200 || code > 299 {
return nil, &RetrieveError{
Response: r,
Body: body,
}
}
da := &DeviceAuthResponse{}
err = json.Unmarshal(body, &da)
if err != nil {
return nil, fmt.Errorf("unmarshal %s", err)
}
if !da.Expiry.IsZero() {
// Make a small adjustment to account for time taken by the request
da.Expiry = da.Expiry.Add(-time.Since(t))
}
return da, nil
}
// DeviceAccessToken polls the server to exchange a device code for a token.
func (c *Config) DeviceAccessToken(ctx context.Context, da *DeviceAuthResponse, opts ...AuthCodeOption) (*Token, error) {
if !da.Expiry.IsZero() {
var cancel context.CancelFunc
ctx, cancel = context.WithDeadline(ctx, da.Expiry)
defer cancel()
}
// https://datatracker.ietf.org/doc/html/rfc8628#section-3.4
v := url.Values{
"client_id": {c.ClientID},
"grant_type": {"urn:ietf:params:oauth:grant-type:device_code"},
"device_code": {da.DeviceCode},
}
if len(c.Scopes) > 0 {
v.Set("scope", strings.Join(c.Scopes, " "))
}
for _, opt := range opts {
opt.setValue(v)
}
// "If no value is provided, clients MUST use 5 as the default."
// https://datatracker.ietf.org/doc/html/rfc8628#section-3.2
interval := da.Interval
if interval == 0 {
interval = 5
}
ticker := time.NewTicker(time.Duration(interval) * time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-ticker.C:
tok, err := retrieveToken(ctx, c, v)
if err == nil {
return tok, nil
}
e, ok := err.(*RetrieveError)
if !ok {
return nil, err
}
switch e.ErrorCode {
case errSlowDown:
// https://datatracker.ietf.org/doc/html/rfc8628#section-3.5
// "the interval MUST be increased by 5 seconds for this and all subsequent requests"
interval += 5
ticker.Reset(time.Duration(interval) * time.Second)
case errAuthorizationPending:
// Do nothing.
case errAccessDenied, errExpiredToken:
fallthrough
default:
return tok, err
}
}
}
}

View File

@ -18,6 +18,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time" "time"
) )
@ -115,41 +116,60 @@ const (
AuthStyleInHeader AuthStyle = 2 AuthStyleInHeader AuthStyle = 2
) )
// authStyleCache is the set of tokenURLs we've successfully used via // LazyAuthStyleCache is a backwards compatibility compromise to let Configs
// have a lazily-initialized AuthStyleCache.
//
// The two users of this, oauth2.Config and oauth2/clientcredentials.Config,
// both would ideally just embed an unexported AuthStyleCache but because both
// were historically allowed to be copied by value we can't retroactively add an
// uncopyable Mutex to them.
//
// We could use an atomic.Pointer, but that was added recently enough (in Go
// 1.18) that we'd break Go 1.17 users where the tests as of 2023-08-03
// still pass. By using an atomic.Value, it supports both Go 1.17 and
// copying by value, even if that's not ideal.
type LazyAuthStyleCache struct {
v atomic.Value // of *AuthStyleCache
}
func (lc *LazyAuthStyleCache) Get() *AuthStyleCache {
if c, ok := lc.v.Load().(*AuthStyleCache); ok {
return c
}
c := new(AuthStyleCache)
if !lc.v.CompareAndSwap(nil, c) {
c = lc.v.Load().(*AuthStyleCache)
}
return c
}
// AuthStyleCache is the set of tokenURLs we've successfully used via
// RetrieveToken and which style auth we ended up using. // RetrieveToken and which style auth we ended up using.
// It's called a cache, but it doesn't (yet?) shrink. It's expected that // It's called a cache, but it doesn't (yet?) shrink. It's expected that
// the set of OAuth2 servers a program contacts over time is fixed and // the set of OAuth2 servers a program contacts over time is fixed and
// small. // small.
var authStyleCache struct { type AuthStyleCache struct {
sync.Mutex mu sync.Mutex
m map[string]AuthStyle // keyed by tokenURL m map[string]AuthStyle // keyed by tokenURL
}
// ResetAuthCache resets the global authentication style cache used
// for AuthStyleUnknown token requests.
func ResetAuthCache() {
authStyleCache.Lock()
defer authStyleCache.Unlock()
authStyleCache.m = nil
} }
// lookupAuthStyle reports which auth style we last used with tokenURL // lookupAuthStyle reports which auth style we last used with tokenURL
// when calling RetrieveToken and whether we have ever done so. // when calling RetrieveToken and whether we have ever done so.
func lookupAuthStyle(tokenURL string) (style AuthStyle, ok bool) { func (c *AuthStyleCache) lookupAuthStyle(tokenURL string) (style AuthStyle, ok bool) {
authStyleCache.Lock() c.mu.Lock()
defer authStyleCache.Unlock() defer c.mu.Unlock()
style, ok = authStyleCache.m[tokenURL] style, ok = c.m[tokenURL]
return return
} }
// setAuthStyle adds an entry to authStyleCache, documented above. // setAuthStyle adds an entry to authStyleCache, documented above.
func setAuthStyle(tokenURL string, v AuthStyle) { func (c *AuthStyleCache) setAuthStyle(tokenURL string, v AuthStyle) {
authStyleCache.Lock() c.mu.Lock()
defer authStyleCache.Unlock() defer c.mu.Unlock()
if authStyleCache.m == nil { if c.m == nil {
authStyleCache.m = make(map[string]AuthStyle) c.m = make(map[string]AuthStyle)
} }
authStyleCache.m[tokenURL] = v c.m[tokenURL] = v
} }
// newTokenRequest returns a new *http.Request to retrieve a new token // newTokenRequest returns a new *http.Request to retrieve a new token
@ -189,10 +209,10 @@ func cloneURLValues(v url.Values) url.Values {
return v2 return v2
} }
func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values, authStyle AuthStyle) (*Token, error) { func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values, authStyle AuthStyle, styleCache *AuthStyleCache) (*Token, error) {
needsAuthStyleProbe := authStyle == 0 needsAuthStyleProbe := authStyle == 0
if needsAuthStyleProbe { if needsAuthStyleProbe {
if style, ok := lookupAuthStyle(tokenURL); ok { if style, ok := styleCache.lookupAuthStyle(tokenURL); ok {
authStyle = style authStyle = style
needsAuthStyleProbe = false needsAuthStyleProbe = false
} else { } else {
@ -222,7 +242,7 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string,
token, err = doTokenRoundTrip(ctx, req) token, err = doTokenRoundTrip(ctx, req)
} }
if needsAuthStyleProbe && err == nil { if needsAuthStyleProbe && err == nil {
setAuthStyle(tokenURL, authStyle) styleCache.setAuthStyle(tokenURL, authStyle)
} }
// Don't overwrite `RefreshToken` with an empty value // Don't overwrite `RefreshToken` with an empty value
// if this was a token refreshing request. // if this was a token refreshing request.

33
vendor/golang.org/x/oauth2/oauth2.go generated vendored
View File

@ -58,6 +58,10 @@ type Config struct {
// Scope specifies optional requested permissions. // Scope specifies optional requested permissions.
Scopes []string Scopes []string
// authStyleCache caches which auth style to use when Endpoint.AuthStyle is
// the zero value (AuthStyleAutoDetect).
authStyleCache internal.LazyAuthStyleCache
} }
// A TokenSource is anything that can return a token. // A TokenSource is anything that can return a token.
@ -71,8 +75,9 @@ type TokenSource interface {
// Endpoint represents an OAuth 2.0 provider's authorization and token // Endpoint represents an OAuth 2.0 provider's authorization and token
// endpoint URLs. // endpoint URLs.
type Endpoint struct { type Endpoint struct {
AuthURL string AuthURL string
TokenURL string DeviceAuthURL string
TokenURL string
// AuthStyle optionally specifies how the endpoint wants the // AuthStyle optionally specifies how the endpoint wants the
// client ID & client secret sent. The zero value means to // client ID & client secret sent. The zero value means to
@ -139,15 +144,19 @@ func SetAuthURLParam(key, value string) AuthCodeOption {
// AuthCodeURL returns a URL to OAuth 2.0 provider's consent page // AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
// that asks for permissions for the required scopes explicitly. // that asks for permissions for the required scopes explicitly.
// //
// State is a token to protect the user from CSRF attacks. You must // State is an opaque value used by the client to maintain state between the
// always provide a non-empty string and validate that it matches the // request and callback. The authorization server includes this value when
// state query parameter on your redirect callback. // redirecting the user agent back to the client.
// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
// //
// Opts may include AccessTypeOnline or AccessTypeOffline, as well // Opts may include AccessTypeOnline or AccessTypeOffline, as well
// as ApprovalForce. // as ApprovalForce.
// It can also be used to pass the PKCE challenge. //
// See https://www.oauth.com/oauth2-servers/pkce/ for more info. // To protect against CSRF attacks, opts should include a PKCE challenge
// (S256ChallengeOption). Not all servers support PKCE. An alternative is to
// generate a random state parameter and verify it after exchange.
// See https://datatracker.ietf.org/doc/html/rfc6749#section-10.12 (predating
// PKCE), https://www.oauth.com/oauth2-servers/pkce/ and
// https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-09.html#name-cross-site-request-forgery (describing both approaches)
func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string { func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteString(c.Endpoint.AuthURL) buf.WriteString(c.Endpoint.AuthURL)
@ -162,7 +171,6 @@ func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
v.Set("scope", strings.Join(c.Scopes, " ")) v.Set("scope", strings.Join(c.Scopes, " "))
} }
if state != "" { if state != "" {
// TODO(light): Docs say never to omit state; don't allow empty.
v.Set("state", state) v.Set("state", state)
} }
for _, opt := range opts { for _, opt := range opts {
@ -207,10 +215,11 @@ func (c *Config) PasswordCredentialsToken(ctx context.Context, username, passwor
// The provided context optionally controls which HTTP client is used. See the HTTPClient variable. // The provided context optionally controls which HTTP client is used. See the HTTPClient variable.
// //
// The code will be in the *http.Request.FormValue("code"). Before // The code will be in the *http.Request.FormValue("code"). Before
// calling Exchange, be sure to validate FormValue("state"). // calling Exchange, be sure to validate FormValue("state") if you are
// using it to protect against CSRF attacks.
// //
// Opts may include the PKCE verifier code if previously used in AuthCodeURL. // If using PKCE to protect against CSRF attacks, opts should include a
// See https://www.oauth.com/oauth2-servers/pkce/ for more info. // VerifierOption.
func (c *Config) Exchange(ctx context.Context, code string, opts ...AuthCodeOption) (*Token, error) { func (c *Config) Exchange(ctx context.Context, code string, opts ...AuthCodeOption) (*Token, error) {
v := url.Values{ v := url.Values{
"grant_type": {"authorization_code"}, "grant_type": {"authorization_code"},

68
vendor/golang.org/x/oauth2/pkce.go generated vendored Normal file
View File

@ -0,0 +1,68 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package oauth2
import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"net/url"
)
const (
codeChallengeKey = "code_challenge"
codeChallengeMethodKey = "code_challenge_method"
codeVerifierKey = "code_verifier"
)
// GenerateVerifier generates a PKCE code verifier with 32 octets of randomness.
// This follows recommendations in RFC 7636.
//
// A fresh verifier should be generated for each authorization.
// S256ChallengeOption(verifier) should then be passed to Config.AuthCodeURL
// (or Config.DeviceAccess) and VerifierOption(verifier) to Config.Exchange
// (or Config.DeviceAccessToken).
func GenerateVerifier() string {
// "RECOMMENDED that the output of a suitable random number generator be
// used to create a 32-octet sequence. The octet sequence is then
// base64url-encoded to produce a 43-octet URL-safe string to use as the
// code verifier."
// https://datatracker.ietf.org/doc/html/rfc7636#section-4.1
data := make([]byte, 32)
if _, err := rand.Read(data); err != nil {
panic(err)
}
return base64.RawURLEncoding.EncodeToString(data)
}
// VerifierOption returns a PKCE code verifier AuthCodeOption. It should be
// passed to Config.Exchange or Config.DeviceAccessToken only.
func VerifierOption(verifier string) AuthCodeOption {
return setParam{k: codeVerifierKey, v: verifier}
}
// S256ChallengeFromVerifier returns a PKCE code challenge derived from verifier with method S256.
//
// Prefer to use S256ChallengeOption where possible.
func S256ChallengeFromVerifier(verifier string) string {
sha := sha256.Sum256([]byte(verifier))
return base64.RawURLEncoding.EncodeToString(sha[:])
}
// S256ChallengeOption derives a PKCE code challenge derived from verifier with
// method S256. It should be passed to Config.AuthCodeURL or Config.DeviceAccess
// only.
func S256ChallengeOption(verifier string) AuthCodeOption {
return challengeOption{
challenge_method: "S256",
challenge: S256ChallengeFromVerifier(verifier),
}
}
type challengeOption struct{ challenge_method, challenge string }
func (p challengeOption) setValue(m url.Values) {
m.Set(codeChallengeMethodKey, p.challenge_method)
m.Set(codeChallengeKey, p.challenge)
}

View File

@ -164,7 +164,7 @@ func tokenFromInternal(t *internal.Token) *Token {
// This token is then mapped from *internal.Token into an *oauth2.Token which is returned along // This token is then mapped from *internal.Token into an *oauth2.Token which is returned along
// with an error.. // with an error..
func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) { func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) {
tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v, internal.AuthStyle(c.Endpoint.AuthStyle)) tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v, internal.AuthStyle(c.Endpoint.AuthStyle), c.authStyleCache.Get())
if err != nil { if err != nil {
if rErr, ok := err.(*internal.RetrieveError); ok { if rErr, ok := err.(*internal.RetrieveError); ok {
return nil, (*RetrieveError)(rErr) return nil, (*RetrieveError)(rErr)

View File

@ -32,21 +32,13 @@ import (
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
) )
type ccbMode int
const (
ccbModeActive = iota
ccbModeIdle
ccbModeClosed
ccbModeExitingIdle
)
// ccBalancerWrapper sits between the ClientConn and the Balancer. // ccBalancerWrapper sits between the ClientConn and the Balancer.
// //
// ccBalancerWrapper implements methods corresponding to the ones on the // ccBalancerWrapper implements methods corresponding to the ones on the
// balancer.Balancer interface. The ClientConn is free to call these methods // balancer.Balancer interface. The ClientConn is free to call these methods
// concurrently and the ccBalancerWrapper ensures that calls from the ClientConn // concurrently and the ccBalancerWrapper ensures that calls from the ClientConn
// to the Balancer happen synchronously and in order. // to the Balancer happen in order by performing them in the serializer, without
// any mutexes held.
// //
// ccBalancerWrapper also implements the balancer.ClientConn interface and is // ccBalancerWrapper also implements the balancer.ClientConn interface and is
// passed to the Balancer implementations. It invokes unexported methods on the // passed to the Balancer implementations. It invokes unexported methods on the
@ -57,87 +49,75 @@ const (
type ccBalancerWrapper struct { type ccBalancerWrapper struct {
// The following fields are initialized when the wrapper is created and are // The following fields are initialized when the wrapper is created and are
// read-only afterwards, and therefore can be accessed without a mutex. // read-only afterwards, and therefore can be accessed without a mutex.
cc *ClientConn cc *ClientConn
opts balancer.BuildOptions opts balancer.BuildOptions
serializer *grpcsync.CallbackSerializer
serializerCancel context.CancelFunc
// Outgoing (gRPC --> balancer) calls are guaranteed to execute in a // The following fields are only accessed within the serializer or during
// mutually exclusive manner as they are scheduled in the serializer. Fields // initialization.
// accessed *only* in these serializer callbacks, can therefore be accessed
// without a mutex.
balancer *gracefulswitch.Balancer
curBalancerName string curBalancerName string
balancer *gracefulswitch.Balancer
// mu guards access to the below fields. Access to the serializer and its // The following field is protected by mu. Caller must take cc.mu before
// cancel function needs to be mutex protected because they are overwritten // taking mu.
// when the wrapper exits idle mode. mu sync.Mutex
mu sync.Mutex closed bool
serializer *grpcsync.CallbackSerializer // To serialize all outoing calls.
serializerCancel context.CancelFunc // To close the seralizer at close/enterIdle time.
mode ccbMode // Tracks the current mode of the wrapper.
} }
// newCCBalancerWrapper creates a new balancer wrapper. The underlying balancer // newCCBalancerWrapper creates a new balancer wrapper in idle state. The
// is not created until the switchTo() method is invoked. // underlying balancer is not created until the switchTo() method is invoked.
func newCCBalancerWrapper(cc *ClientConn, bopts balancer.BuildOptions) *ccBalancerWrapper { func newCCBalancerWrapper(cc *ClientConn) *ccBalancerWrapper {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(cc.ctx)
ccb := &ccBalancerWrapper{ ccb := &ccBalancerWrapper{
cc: cc, cc: cc,
opts: bopts, opts: balancer.BuildOptions{
DialCreds: cc.dopts.copts.TransportCredentials,
CredsBundle: cc.dopts.copts.CredsBundle,
Dialer: cc.dopts.copts.Dialer,
Authority: cc.authority,
CustomUserAgent: cc.dopts.copts.UserAgent,
ChannelzParentID: cc.channelzID,
Target: cc.parsedTarget,
},
serializer: grpcsync.NewCallbackSerializer(ctx), serializer: grpcsync.NewCallbackSerializer(ctx),
serializerCancel: cancel, serializerCancel: cancel,
} }
ccb.balancer = gracefulswitch.NewBalancer(ccb, bopts) ccb.balancer = gracefulswitch.NewBalancer(ccb, ccb.opts)
return ccb return ccb
} }
// updateClientConnState is invoked by grpc to push a ClientConnState update to // updateClientConnState is invoked by grpc to push a ClientConnState update to
// the underlying balancer. // the underlying balancer. This is always executed from the serializer, so
// it is safe to call into the balancer here.
func (ccb *ccBalancerWrapper) updateClientConnState(ccs *balancer.ClientConnState) error { func (ccb *ccBalancerWrapper) updateClientConnState(ccs *balancer.ClientConnState) error {
ccb.mu.Lock() errCh := make(chan error)
errCh := make(chan error, 1) ok := ccb.serializer.Schedule(func(ctx context.Context) {
// Here and everywhere else where Schedule() is called, it is done with the defer close(errCh)
// lock held. But the lock guards only the scheduling part. The actual if ctx.Err() != nil || ccb.balancer == nil {
// callback is called asynchronously without the lock being held. return
ok := ccb.serializer.Schedule(func(_ context.Context) { }
errCh <- ccb.balancer.UpdateClientConnState(*ccs) err := ccb.balancer.UpdateClientConnState(*ccs)
if logger.V(2) && err != nil {
logger.Infof("error from balancer.UpdateClientConnState: %v", err)
}
errCh <- err
}) })
if !ok { if !ok {
// If we are unable to schedule a function with the serializer, it return nil
// indicates that it has been closed. A serializer is only closed when
// the wrapper is closed or is in idle.
ccb.mu.Unlock()
return fmt.Errorf("grpc: cannot send state update to a closed or idle balancer")
} }
ccb.mu.Unlock() return <-errCh
// We get here only if the above call to Schedule succeeds, in which case it
// is guaranteed that the scheduled function will run. Therefore it is safe
// to block on this channel.
err := <-errCh
if logger.V(2) && err != nil {
logger.Infof("error from balancer.UpdateClientConnState: %v", err)
}
return err
}
// updateSubConnState is invoked by grpc to push a subConn state update to the
// underlying balancer.
func (ccb *ccBalancerWrapper) updateSubConnState(sc balancer.SubConn, s connectivity.State, err error) {
ccb.mu.Lock()
ccb.serializer.Schedule(func(_ context.Context) {
// Even though it is optional for balancers, gracefulswitch ensures
// opts.StateListener is set, so this cannot ever be nil.
sc.(*acBalancerWrapper).stateListener(balancer.SubConnState{ConnectivityState: s, ConnectionError: err})
})
ccb.mu.Unlock()
} }
// resolverError is invoked by grpc to push a resolver error to the underlying
// balancer. The call to the balancer is executed from the serializer.
func (ccb *ccBalancerWrapper) resolverError(err error) { func (ccb *ccBalancerWrapper) resolverError(err error) {
ccb.mu.Lock() ccb.serializer.Schedule(func(ctx context.Context) {
ccb.serializer.Schedule(func(_ context.Context) { if ctx.Err() != nil || ccb.balancer == nil {
return
}
ccb.balancer.ResolverError(err) ccb.balancer.ResolverError(err)
}) })
ccb.mu.Unlock()
} }
// switchTo is invoked by grpc to instruct the balancer wrapper to switch to the // switchTo is invoked by grpc to instruct the balancer wrapper to switch to the
@ -151,8 +131,10 @@ func (ccb *ccBalancerWrapper) resolverError(err error) {
// the ccBalancerWrapper keeps track of the current LB policy name, and skips // the ccBalancerWrapper keeps track of the current LB policy name, and skips
// the graceful balancer switching process if the name does not change. // the graceful balancer switching process if the name does not change.
func (ccb *ccBalancerWrapper) switchTo(name string) { func (ccb *ccBalancerWrapper) switchTo(name string) {
ccb.mu.Lock() ccb.serializer.Schedule(func(ctx context.Context) {
ccb.serializer.Schedule(func(_ context.Context) { if ctx.Err() != nil || ccb.balancer == nil {
return
}
// TODO: Other languages use case-sensitive balancer registries. We should // TODO: Other languages use case-sensitive balancer registries. We should
// switch as well. See: https://github.com/grpc/grpc-go/issues/5288. // switch as well. See: https://github.com/grpc/grpc-go/issues/5288.
if strings.EqualFold(ccb.curBalancerName, name) { if strings.EqualFold(ccb.curBalancerName, name) {
@ -160,7 +142,6 @@ func (ccb *ccBalancerWrapper) switchTo(name string) {
} }
ccb.buildLoadBalancingPolicy(name) ccb.buildLoadBalancingPolicy(name)
}) })
ccb.mu.Unlock()
} }
// buildLoadBalancingPolicy performs the following: // buildLoadBalancingPolicy performs the following:
@ -187,115 +168,49 @@ func (ccb *ccBalancerWrapper) buildLoadBalancingPolicy(name string) {
ccb.curBalancerName = builder.Name() ccb.curBalancerName = builder.Name()
} }
// close initiates async shutdown of the wrapper. cc.mu must be held when
// calling this function. To determine the wrapper has finished shutting down,
// the channel should block on ccb.serializer.Done() without cc.mu held.
func (ccb *ccBalancerWrapper) close() { func (ccb *ccBalancerWrapper) close() {
channelz.Info(logger, ccb.cc.channelzID, "ccBalancerWrapper: closing")
ccb.closeBalancer(ccbModeClosed)
}
// enterIdleMode is invoked by grpc when the channel enters idle mode upon
// expiry of idle_timeout. This call blocks until the balancer is closed.
func (ccb *ccBalancerWrapper) enterIdleMode() {
channelz.Info(logger, ccb.cc.channelzID, "ccBalancerWrapper: entering idle mode")
ccb.closeBalancer(ccbModeIdle)
}
// closeBalancer is invoked when the channel is being closed or when it enters
// idle mode upon expiry of idle_timeout.
func (ccb *ccBalancerWrapper) closeBalancer(m ccbMode) {
ccb.mu.Lock() ccb.mu.Lock()
if ccb.mode == ccbModeClosed || ccb.mode == ccbModeIdle { ccb.closed = true
ccb.mu.Unlock()
return
}
ccb.mode = m
done := ccb.serializer.Done()
b := ccb.balancer
ok := ccb.serializer.Schedule(func(_ context.Context) {
// Close the serializer to ensure that no more calls from gRPC are sent
// to the balancer.
ccb.serializerCancel()
// Empty the current balancer name because we don't have a balancer
// anymore and also so that we act on the next call to switchTo by
// creating a new balancer specified by the new resolver.
ccb.curBalancerName = ""
})
if !ok {
ccb.mu.Unlock()
return
}
ccb.mu.Unlock() ccb.mu.Unlock()
channelz.Info(logger, ccb.cc.channelzID, "ccBalancerWrapper: closing")
// Give enqueued callbacks a chance to finish before closing the balancer. ccb.serializer.Schedule(func(context.Context) {
<-done if ccb.balancer == nil {
b.Close()
}
// exitIdleMode is invoked by grpc when the channel exits idle mode either
// because of an RPC or because of an invocation of the Connect() API. This
// recreates the balancer that was closed previously when entering idle mode.
//
// If the channel is not in idle mode, we know for a fact that we are here as a
// result of the user calling the Connect() method on the ClientConn. In this
// case, we can simply forward the call to the underlying balancer, instructing
// it to reconnect to the backends.
func (ccb *ccBalancerWrapper) exitIdleMode() {
ccb.mu.Lock()
if ccb.mode == ccbModeClosed {
// Request to exit idle is a no-op when wrapper is already closed.
ccb.mu.Unlock()
return
}
if ccb.mode == ccbModeIdle {
// Recreate the serializer which was closed when we entered idle.
ctx, cancel := context.WithCancel(context.Background())
ccb.serializer = grpcsync.NewCallbackSerializer(ctx)
ccb.serializerCancel = cancel
}
// The ClientConn guarantees that mutual exclusion between close() and
// exitIdleMode(), and since we just created a new serializer, we can be
// sure that the below function will be scheduled.
done := make(chan struct{})
ccb.serializer.Schedule(func(_ context.Context) {
defer close(done)
ccb.mu.Lock()
defer ccb.mu.Unlock()
if ccb.mode != ccbModeIdle {
ccb.balancer.ExitIdle()
return return
} }
ccb.balancer.Close()
// Gracefulswitch balancer does not support a switchTo operation after ccb.balancer = nil
// being closed. Hence we need to create a new one here.
ccb.balancer = gracefulswitch.NewBalancer(ccb, ccb.opts)
ccb.mode = ccbModeActive
channelz.Info(logger, ccb.cc.channelzID, "ccBalancerWrapper: exiting idle mode")
}) })
ccb.mu.Unlock() ccb.serializerCancel()
<-done
} }
func (ccb *ccBalancerWrapper) isIdleOrClosed() bool { // exitIdle invokes the balancer's exitIdle method in the serializer.
ccb.mu.Lock() func (ccb *ccBalancerWrapper) exitIdle() {
defer ccb.mu.Unlock() ccb.serializer.Schedule(func(ctx context.Context) {
return ccb.mode == ccbModeIdle || ccb.mode == ccbModeClosed if ctx.Err() != nil || ccb.balancer == nil {
return
}
ccb.balancer.ExitIdle()
})
} }
func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) { func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
if ccb.isIdleOrClosed() { ccb.cc.mu.Lock()
return nil, fmt.Errorf("grpc: cannot create SubConn when balancer is closed or idle") defer ccb.cc.mu.Unlock()
ccb.mu.Lock()
if ccb.closed {
ccb.mu.Unlock()
return nil, fmt.Errorf("balancer is being closed; no new SubConns allowed")
} }
ccb.mu.Unlock()
if len(addrs) == 0 { if len(addrs) == 0 {
return nil, fmt.Errorf("grpc: cannot create SubConn with empty address list") return nil, fmt.Errorf("grpc: cannot create SubConn with empty address list")
} }
ac, err := ccb.cc.newAddrConn(addrs, opts) ac, err := ccb.cc.newAddrConnLocked(addrs, opts)
if err != nil { if err != nil {
channelz.Warningf(logger, ccb.cc.channelzID, "acBalancerWrapper: NewSubConn: failed to newAddrConn: %v", err) channelz.Warningf(logger, ccb.cc.channelzID, "acBalancerWrapper: NewSubConn: failed to newAddrConn: %v", err)
return nil, err return nil, err
@ -316,10 +231,6 @@ func (ccb *ccBalancerWrapper) RemoveSubConn(sc balancer.SubConn) {
} }
func (ccb *ccBalancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) { func (ccb *ccBalancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) {
if ccb.isIdleOrClosed() {
return
}
acbw, ok := sc.(*acBalancerWrapper) acbw, ok := sc.(*acBalancerWrapper)
if !ok { if !ok {
return return
@ -328,25 +239,39 @@ func (ccb *ccBalancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resol
} }
func (ccb *ccBalancerWrapper) UpdateState(s balancer.State) { func (ccb *ccBalancerWrapper) UpdateState(s balancer.State) {
if ccb.isIdleOrClosed() { ccb.cc.mu.Lock()
defer ccb.cc.mu.Unlock()
ccb.mu.Lock()
if ccb.closed {
ccb.mu.Unlock()
return return
} }
ccb.mu.Unlock()
// Update picker before updating state. Even though the ordering here does // Update picker before updating state. Even though the ordering here does
// not matter, it can lead to multiple calls of Pick in the common start-up // not matter, it can lead to multiple calls of Pick in the common start-up
// case where we wait for ready and then perform an RPC. If the picker is // case where we wait for ready and then perform an RPC. If the picker is
// updated later, we could call the "connecting" picker when the state is // updated later, we could call the "connecting" picker when the state is
// updated, and then call the "ready" picker after the picker gets updated. // updated, and then call the "ready" picker after the picker gets updated.
ccb.cc.blockingpicker.updatePicker(s.Picker)
// Note that there is no need to check if the balancer wrapper was closed,
// as we know the graceful switch LB policy will not call cc if it has been
// closed.
ccb.cc.pickerWrapper.updatePicker(s.Picker)
ccb.cc.csMgr.updateState(s.ConnectivityState) ccb.cc.csMgr.updateState(s.ConnectivityState)
} }
func (ccb *ccBalancerWrapper) ResolveNow(o resolver.ResolveNowOptions) { func (ccb *ccBalancerWrapper) ResolveNow(o resolver.ResolveNowOptions) {
if ccb.isIdleOrClosed() { ccb.cc.mu.RLock()
defer ccb.cc.mu.RUnlock()
ccb.mu.Lock()
if ccb.closed {
ccb.mu.Unlock()
return return
} }
ccb.mu.Unlock()
ccb.cc.resolveNow(o) ccb.cc.resolveNowLocked(o)
} }
func (ccb *ccBalancerWrapper) Target() string { func (ccb *ccBalancerWrapper) Target() string {
@ -364,6 +289,20 @@ type acBalancerWrapper struct {
producers map[balancer.ProducerBuilder]*refCountedProducer producers map[balancer.ProducerBuilder]*refCountedProducer
} }
// updateState is invoked by grpc to push a subConn state update to the
// underlying balancer.
func (acbw *acBalancerWrapper) updateState(s connectivity.State, err error) {
acbw.ccb.serializer.Schedule(func(ctx context.Context) {
if ctx.Err() != nil || acbw.ccb.balancer == nil {
return
}
// Even though it is optional for balancers, gracefulswitch ensures
// opts.StateListener is set, so this cannot ever be nil.
// TODO: delete this comment when UpdateSubConnState is removed.
acbw.stateListener(balancer.SubConnState{ConnectivityState: s, ConnectionError: err})
})
}
func (acbw *acBalancerWrapper) String() string { func (acbw *acBalancerWrapper) String() string {
return fmt.Sprintf("SubConn(id:%d)", acbw.ac.channelzID.Int()) return fmt.Sprintf("SubConn(id:%d)", acbw.ac.channelzID.Int())
} }
@ -377,20 +316,7 @@ func (acbw *acBalancerWrapper) Connect() {
} }
func (acbw *acBalancerWrapper) Shutdown() { func (acbw *acBalancerWrapper) Shutdown() {
ccb := acbw.ccb acbw.ccb.cc.removeAddrConn(acbw.ac, errConnDrain)
if ccb.isIdleOrClosed() {
// It it safe to ignore this call when the balancer is closed or in idle
// because the ClientConn takes care of closing the connections.
//
// Not returning early from here when the balancer is closed or in idle
// leads to a deadlock though, because of the following sequence of
// calls when holding cc.mu:
// cc.exitIdleMode --> ccb.enterIdleMode --> gsw.Close -->
// ccb.RemoveAddrConn --> cc.removeAddrConn
return
}
ccb.cc.removeAddrConn(acbw.ac, errConnDrain)
} }
// NewStream begins a streaming RPC on the addrConn. If the addrConn is not // NewStream begins a streaming RPC on the addrConn. If the addrConn is not

View File

@ -33,9 +33,7 @@ import (
"google.golang.org/grpc/balancer/base" "google.golang.org/grpc/balancer/base"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/connectivity" "google.golang.org/grpc/connectivity"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/internal" "google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/backoff"
"google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/internal/grpcsync" "google.golang.org/grpc/internal/grpcsync"
"google.golang.org/grpc/internal/idle" "google.golang.org/grpc/internal/idle"
@ -48,9 +46,9 @@ import (
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
_ "google.golang.org/grpc/balancer/roundrobin" // To register roundrobin. _ "google.golang.org/grpc/balancer/roundrobin" // To register roundrobin.
_ "google.golang.org/grpc/internal/resolver/dns" // To register dns resolver.
_ "google.golang.org/grpc/internal/resolver/passthrough" // To register passthrough resolver. _ "google.golang.org/grpc/internal/resolver/passthrough" // To register passthrough resolver.
_ "google.golang.org/grpc/internal/resolver/unix" // To register unix resolver. _ "google.golang.org/grpc/internal/resolver/unix" // To register unix resolver.
_ "google.golang.org/grpc/resolver/dns" // To register dns resolver.
) )
const ( const (
@ -119,23 +117,8 @@ func (dcs *defaultConfigSelector) SelectConfig(rpcInfo iresolver.RPCInfo) (*ires
}, nil }, nil
} }
// DialContext creates a client connection to the given target. By default, it's // newClient returns a new client in idle mode.
// a non-blocking dial (the function won't wait for connections to be func newClient(target string, opts ...DialOption) (conn *ClientConn, err error) {
// established, and connecting happens in the background). To make it a blocking
// dial, use WithBlock() dial option.
//
// In the non-blocking case, the ctx does not act against the connection. It
// only controls the setup steps.
//
// In the blocking case, ctx can be used to cancel or expire the pending
// connection. Once this function returns, the cancellation and expiration of
// ctx will be noop. Users should call ClientConn.Close to terminate all the
// pending operations after this function returns.
//
// The target name syntax is defined in
// https://github.com/grpc/grpc/blob/master/doc/naming.md.
// e.g. to use dns resolver, a "dns:///" prefix should be applied to the target.
func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) {
cc := &ClientConn{ cc := &ClientConn{
target: target, target: target,
conns: make(map[*addrConn]struct{}), conns: make(map[*addrConn]struct{}),
@ -143,23 +126,11 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
czData: new(channelzData), czData: new(channelzData),
} }
// We start the channel off in idle mode, but kick it out of idle at the end
// of this method, instead of waiting for the first RPC. Other gRPC
// implementations do wait for the first RPC to kick the channel out of
// idle. But doing so would be a major behavior change for our users who are
// used to seeing the channel active after Dial.
//
// Taking this approach of kicking it out of idle at the end of this method
// allows us to share the code between channel creation and exiting idle
// mode. This will also make it easy for us to switch to starting the
// channel off in idle, if at all we ever get to do that.
cc.idlenessState = ccIdlenessStateIdle
cc.retryThrottler.Store((*retryThrottler)(nil)) cc.retryThrottler.Store((*retryThrottler)(nil))
cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{nil}) cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{nil})
cc.ctx, cc.cancel = context.WithCancel(context.Background()) cc.ctx, cc.cancel = context.WithCancel(context.Background())
cc.exitIdleCond = sync.NewCond(&cc.mu)
// Apply dial options.
disableGlobalOpts := false disableGlobalOpts := false
for _, opt := range opts { for _, opt := range opts {
if _, ok := opt.(*disableGlobalDialOptions); ok { if _, ok := opt.(*disableGlobalDialOptions); ok {
@ -177,21 +148,9 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
for _, opt := range opts { for _, opt := range opts {
opt.apply(&cc.dopts) opt.apply(&cc.dopts)
} }
chainUnaryClientInterceptors(cc) chainUnaryClientInterceptors(cc)
chainStreamClientInterceptors(cc) chainStreamClientInterceptors(cc)
defer func() {
if err != nil {
cc.Close()
}
}()
// Register ClientConn with channelz.
cc.channelzRegistration(target)
cc.csMgr = newConnectivityStateManager(cc.ctx, cc.channelzID)
if err := cc.validateTransportCredentials(); err != nil { if err := cc.validateTransportCredentials(); err != nil {
return nil, err return nil, err
} }
@ -205,10 +164,80 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
} }
cc.mkp = cc.dopts.copts.KeepaliveParams cc.mkp = cc.dopts.copts.KeepaliveParams
if cc.dopts.copts.UserAgent != "" { // Register ClientConn with channelz.
cc.dopts.copts.UserAgent += " " + grpcUA cc.channelzRegistration(target)
} else {
cc.dopts.copts.UserAgent = grpcUA // TODO: Ideally it should be impossible to error from this function after
// channelz registration. This will require removing some channelz logs
// from the following functions that can error. Errors can be returned to
// the user, and successful logs can be emitted here, after the checks have
// passed and channelz is subsequently registered.
// Determine the resolver to use.
if err := cc.parseTargetAndFindResolver(); err != nil {
channelz.RemoveEntry(cc.channelzID)
return nil, err
}
if err = cc.determineAuthority(); err != nil {
channelz.RemoveEntry(cc.channelzID)
return nil, err
}
cc.csMgr = newConnectivityStateManager(cc.ctx, cc.channelzID)
cc.pickerWrapper = newPickerWrapper(cc.dopts.copts.StatsHandlers)
cc.initIdleStateLocked() // Safe to call without the lock, since nothing else has a reference to cc.
cc.idlenessMgr = idle.NewManager((*idler)(cc), cc.dopts.idleTimeout)
return cc, nil
}
// DialContext creates a client connection to the given target. By default, it's
// a non-blocking dial (the function won't wait for connections to be
// established, and connecting happens in the background). To make it a blocking
// dial, use WithBlock() dial option.
//
// In the non-blocking case, the ctx does not act against the connection. It
// only controls the setup steps.
//
// In the blocking case, ctx can be used to cancel or expire the pending
// connection. Once this function returns, the cancellation and expiration of
// ctx will be noop. Users should call ClientConn.Close to terminate all the
// pending operations after this function returns.
//
// The target name syntax is defined in
// https://github.com/grpc/grpc/blob/master/doc/naming.md.
// e.g. to use dns resolver, a "dns:///" prefix should be applied to the target.
func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) {
cc, err := newClient(target, opts...)
if err != nil {
return nil, err
}
// We start the channel off in idle mode, but kick it out of idle now,
// instead of waiting for the first RPC. Other gRPC implementations do wait
// for the first RPC to kick the channel out of idle. But doing so would be
// a major behavior change for our users who are used to seeing the channel
// active after Dial.
//
// Taking this approach of kicking it out of idle at the end of this method
// allows us to share the code between channel creation and exiting idle
// mode. This will also make it easy for us to switch to starting the
// channel off in idle, i.e. by making newClient exported.
defer func() {
if err != nil {
cc.Close()
}
}()
// This creates the name resolver, load balancer, etc.
if err := cc.idlenessMgr.ExitIdleMode(); err != nil {
return nil, err
}
// Return now for non-blocking dials.
if !cc.dopts.block {
return cc, nil
} }
if cc.dopts.timeout > 0 { if cc.dopts.timeout > 0 {
@ -231,49 +260,6 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
} }
}() }()
if cc.dopts.bs == nil {
cc.dopts.bs = backoff.DefaultExponential
}
// Determine the resolver to use.
if err := cc.parseTargetAndFindResolver(); err != nil {
return nil, err
}
if err = cc.determineAuthority(); err != nil {
return nil, err
}
if cc.dopts.scChan != nil {
// Blocking wait for the initial service config.
select {
case sc, ok := <-cc.dopts.scChan:
if ok {
cc.sc = &sc
cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{&sc})
}
case <-ctx.Done():
return nil, ctx.Err()
}
}
if cc.dopts.scChan != nil {
go cc.scWatcher()
}
// This creates the name resolver, load balancer, blocking picker etc.
if err := cc.exitIdleMode(); err != nil {
return nil, err
}
// Configure idleness support with configured idle timeout or default idle
// timeout duration. Idleness can be explicitly disabled by the user, by
// setting the dial option to 0.
cc.idlenessMgr = idle.NewManager(idle.ManagerOptions{Enforcer: (*idler)(cc), Timeout: cc.dopts.idleTimeout, Logger: logger})
// Return early for non-blocking dials.
if !cc.dopts.block {
return cc, nil
}
// A blocking dial blocks until the clientConn is ready. // A blocking dial blocks until the clientConn is ready.
for { for {
s := cc.GetState() s := cc.GetState()
@ -320,8 +306,8 @@ func (cc *ClientConn) addTraceEvent(msg string) {
type idler ClientConn type idler ClientConn
func (i *idler) EnterIdleMode() error { func (i *idler) EnterIdleMode() {
return (*ClientConn)(i).enterIdleMode() (*ClientConn)(i).enterIdleMode()
} }
func (i *idler) ExitIdleMode() error { func (i *idler) ExitIdleMode() error {
@ -329,117 +315,71 @@ func (i *idler) ExitIdleMode() error {
} }
// exitIdleMode moves the channel out of idle mode by recreating the name // exitIdleMode moves the channel out of idle mode by recreating the name
// resolver and load balancer. // resolver and load balancer. This should never be called directly; use
func (cc *ClientConn) exitIdleMode() error { // cc.idlenessMgr.ExitIdleMode instead.
func (cc *ClientConn) exitIdleMode() (err error) {
cc.mu.Lock() cc.mu.Lock()
if cc.conns == nil { if cc.conns == nil {
cc.mu.Unlock() cc.mu.Unlock()
return errConnClosing return errConnClosing
} }
if cc.idlenessState != ccIdlenessStateIdle {
channelz.Infof(logger, cc.channelzID, "ClientConn asked to exit idle mode, current mode is %v", cc.idlenessState)
cc.mu.Unlock()
return nil
}
defer func() {
// When Close() and exitIdleMode() race against each other, one of the
// following two can happen:
// - Close() wins the race and runs first. exitIdleMode() runs after, and
// sees that the ClientConn is already closed and hence returns early.
// - exitIdleMode() wins the race and runs first and recreates the balancer
// and releases the lock before recreating the resolver. If Close() runs
// in this window, it will wait for exitIdleMode to complete.
//
// We achieve this synchronization using the below condition variable.
cc.mu.Lock()
cc.idlenessState = ccIdlenessStateActive
cc.exitIdleCond.Signal()
cc.mu.Unlock()
}()
cc.idlenessState = ccIdlenessStateExitingIdle
exitedIdle := false
if cc.blockingpicker == nil {
cc.blockingpicker = newPickerWrapper(cc.dopts.copts.StatsHandlers)
} else {
cc.blockingpicker.exitIdleMode()
exitedIdle = true
}
var credsClone credentials.TransportCredentials
if creds := cc.dopts.copts.TransportCredentials; creds != nil {
credsClone = creds.Clone()
}
if cc.balancerWrapper == nil {
cc.balancerWrapper = newCCBalancerWrapper(cc, balancer.BuildOptions{
DialCreds: credsClone,
CredsBundle: cc.dopts.copts.CredsBundle,
Dialer: cc.dopts.copts.Dialer,
Authority: cc.authority,
CustomUserAgent: cc.dopts.copts.UserAgent,
ChannelzParentID: cc.channelzID,
Target: cc.parsedTarget,
})
} else {
cc.balancerWrapper.exitIdleMode()
}
cc.firstResolveEvent = grpcsync.NewEvent()
cc.mu.Unlock() cc.mu.Unlock()
// This needs to be called without cc.mu because this builds a new resolver // This needs to be called without cc.mu because this builds a new resolver
// which might update state or report error inline which needs to be handled // which might update state or report error inline, which would then need to
// by cc.updateResolverState() which also grabs cc.mu. // acquire cc.mu.
if err := cc.initResolverWrapper(credsClone); err != nil { if err := cc.resolverWrapper.start(); err != nil {
return err return err
} }
if exitedIdle { cc.addTraceEvent("exiting idle mode")
cc.addTraceEvent("exiting idle mode")
}
return nil return nil
} }
// enterIdleMode puts the channel in idle mode, and as part of it shuts down the // initIdleStateLocked initializes common state to how it should be while idle.
// name resolver, load balancer and any subchannels. func (cc *ClientConn) initIdleStateLocked() {
func (cc *ClientConn) enterIdleMode() error { cc.resolverWrapper = newCCResolverWrapper(cc)
cc.mu.Lock() cc.balancerWrapper = newCCBalancerWrapper(cc)
defer cc.mu.Unlock() cc.firstResolveEvent = grpcsync.NewEvent()
if cc.conns == nil {
return ErrClientConnClosing
}
if cc.idlenessState != ccIdlenessStateActive {
channelz.Warningf(logger, cc.channelzID, "ClientConn asked to enter idle mode, current mode is %v", cc.idlenessState)
return nil
}
// cc.conns == nil is a proxy for the ClientConn being closed. So, instead // cc.conns == nil is a proxy for the ClientConn being closed. So, instead
// of setting it to nil here, we recreate the map. This also means that we // of setting it to nil here, we recreate the map. This also means that we
// don't have to do this when exiting idle mode. // don't have to do this when exiting idle mode.
conns := cc.conns
cc.conns = make(map[*addrConn]struct{}) cc.conns = make(map[*addrConn]struct{})
}
// TODO: Currently, we close the resolver wrapper upon entering idle mode // enterIdleMode puts the channel in idle mode, and as part of it shuts down the
// and create a new one upon exiting idle mode. This means that the // name resolver, load balancer, and any subchannels. This should never be
// `cc.resolverWrapper` field would be overwritten everytime we exit idle // called directly; use cc.idlenessMgr.EnterIdleMode instead.
// mode. While this means that we need to hold `cc.mu` when accessing func (cc *ClientConn) enterIdleMode() {
// `cc.resolverWrapper`, it makes the code simpler in the wrapper. We should cc.mu.Lock()
// try to do the same for the balancer and picker wrappers too.
cc.resolverWrapper.close() if cc.conns == nil {
cc.blockingpicker.enterIdleMode() cc.mu.Unlock()
cc.balancerWrapper.enterIdleMode() return
}
conns := cc.conns
rWrapper := cc.resolverWrapper
rWrapper.close()
cc.pickerWrapper.reset()
bWrapper := cc.balancerWrapper
bWrapper.close()
cc.csMgr.updateState(connectivity.Idle) cc.csMgr.updateState(connectivity.Idle)
cc.idlenessState = ccIdlenessStateIdle
cc.addTraceEvent("entering idle mode") cc.addTraceEvent("entering idle mode")
go func() { cc.initIdleStateLocked()
for ac := range conns {
ac.tearDown(errConnIdling)
}
}()
return nil cc.mu.Unlock()
// Block until the name resolver and LB policy are closed.
<-rWrapper.serializer.Done()
<-bWrapper.serializer.Done()
// Close all subchannels after the LB policy is closed.
for ac := range conns {
ac.tearDown(errConnIdling)
}
} }
// validateTransportCredentials performs a series of checks on the configured // validateTransportCredentials performs a series of checks on the configured
@ -649,66 +589,35 @@ type ClientConn struct {
dopts dialOptions // Default and user specified dial options. dopts dialOptions // Default and user specified dial options.
channelzID *channelz.Identifier // Channelz identifier for the channel. channelzID *channelz.Identifier // Channelz identifier for the channel.
resolverBuilder resolver.Builder // See parseTargetAndFindResolver(). resolverBuilder resolver.Builder // See parseTargetAndFindResolver().
balancerWrapper *ccBalancerWrapper // Uses gracefulswitch.balancer underneath. idlenessMgr *idle.Manager
idlenessMgr idle.Manager
// The following provide their own synchronization, and therefore don't // The following provide their own synchronization, and therefore don't
// require cc.mu to be held to access them. // require cc.mu to be held to access them.
csMgr *connectivityStateManager csMgr *connectivityStateManager
blockingpicker *pickerWrapper pickerWrapper *pickerWrapper
safeConfigSelector iresolver.SafeConfigSelector safeConfigSelector iresolver.SafeConfigSelector
czData *channelzData czData *channelzData
retryThrottler atomic.Value // Updated from service config. retryThrottler atomic.Value // Updated from service config.
// firstResolveEvent is used to track whether the name resolver sent us at
// least one update. RPCs block on this event.
firstResolveEvent *grpcsync.Event
// mu protects the following fields. // mu protects the following fields.
// TODO: split mu so the same mutex isn't used for everything. // TODO: split mu so the same mutex isn't used for everything.
mu sync.RWMutex mu sync.RWMutex
resolverWrapper *ccResolverWrapper // Initialized in Dial; cleared in Close. resolverWrapper *ccResolverWrapper // Always recreated whenever entering idle to simplify Close.
balancerWrapper *ccBalancerWrapper // Always recreated whenever entering idle to simplify Close.
sc *ServiceConfig // Latest service config received from the resolver. sc *ServiceConfig // Latest service config received from the resolver.
conns map[*addrConn]struct{} // Set to nil on close. conns map[*addrConn]struct{} // Set to nil on close.
mkp keepalive.ClientParameters // May be updated upon receipt of a GoAway. mkp keepalive.ClientParameters // May be updated upon receipt of a GoAway.
idlenessState ccIdlenessState // Tracks idleness state of the channel. // firstResolveEvent is used to track whether the name resolver sent us at
exitIdleCond *sync.Cond // Signalled when channel exits idle. // least one update. RPCs block on this event. May be accessed without mu
// if we know we cannot be asked to enter idle mode while accessing it (e.g.
// when the idle manager has already been closed, or if we are already
// entering idle mode).
firstResolveEvent *grpcsync.Event
lceMu sync.Mutex // protects lastConnectionError lceMu sync.Mutex // protects lastConnectionError
lastConnectionError error lastConnectionError error
} }
// ccIdlenessState tracks the idleness state of the channel.
//
// Channels start off in `active` and move to `idle` after a period of
// inactivity. When moving back to `active` upon an incoming RPC, they
// transition through `exiting_idle`. This state is useful for synchronization
// with Close().
//
// This state tracking is mostly for self-protection. The idlenessManager is
// expected to keep track of the state as well, and is expected not to call into
// the ClientConn unnecessarily.
type ccIdlenessState int8
const (
ccIdlenessStateActive ccIdlenessState = iota
ccIdlenessStateIdle
ccIdlenessStateExitingIdle
)
func (s ccIdlenessState) String() string {
switch s {
case ccIdlenessStateActive:
return "active"
case ccIdlenessStateIdle:
return "idle"
case ccIdlenessStateExitingIdle:
return "exitingIdle"
default:
return "unknown"
}
}
// WaitForStateChange waits until the connectivity.State of ClientConn changes from sourceState or // WaitForStateChange waits until the connectivity.State of ClientConn changes from sourceState or
// ctx expires. A true value is returned in former case and false in latter. // ctx expires. A true value is returned in former case and false in latter.
// //
@ -748,29 +657,15 @@ func (cc *ClientConn) GetState() connectivity.State {
// Notice: This API is EXPERIMENTAL and may be changed or removed in a later // Notice: This API is EXPERIMENTAL and may be changed or removed in a later
// release. // release.
func (cc *ClientConn) Connect() { func (cc *ClientConn) Connect() {
cc.exitIdleMode() if err := cc.idlenessMgr.ExitIdleMode(); err != nil {
cc.addTraceEvent(err.Error())
return
}
// If the ClientConn was not in idle mode, we need to call ExitIdle on the // If the ClientConn was not in idle mode, we need to call ExitIdle on the
// LB policy so that connections can be created. // LB policy so that connections can be created.
cc.balancerWrapper.exitIdleMode() cc.mu.Lock()
} cc.balancerWrapper.exitIdle()
cc.mu.Unlock()
func (cc *ClientConn) scWatcher() {
for {
select {
case sc, ok := <-cc.dopts.scChan:
if !ok {
return
}
cc.mu.Lock()
// TODO: load balance policy runtime change is ignored.
// We may revisit this decision in the future.
cc.sc = &sc
cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{&sc})
cc.mu.Unlock()
case <-cc.ctx.Done():
return
}
}
} }
// waitForResolvedAddrs blocks until the resolver has provided addresses or the // waitForResolvedAddrs blocks until the resolver has provided addresses or the
@ -804,11 +699,11 @@ func init() {
internal.SubscribeToConnectivityStateChanges = func(cc *ClientConn, s grpcsync.Subscriber) func() { internal.SubscribeToConnectivityStateChanges = func(cc *ClientConn, s grpcsync.Subscriber) func() {
return cc.csMgr.pubSub.Subscribe(s) return cc.csMgr.pubSub.Subscribe(s)
} }
internal.EnterIdleModeForTesting = func(cc *ClientConn) error { internal.EnterIdleModeForTesting = func(cc *ClientConn) {
return cc.enterIdleMode() cc.idlenessMgr.EnterIdleModeForTesting()
} }
internal.ExitIdleModeForTesting = func(cc *ClientConn) error { internal.ExitIdleModeForTesting = func(cc *ClientConn) error {
return cc.exitIdleMode() return cc.idlenessMgr.ExitIdleMode()
} }
} }
@ -824,9 +719,8 @@ func (cc *ClientConn) maybeApplyDefaultServiceConfig(addrs []resolver.Address) {
} }
} }
func (cc *ClientConn) updateResolverState(s resolver.State, err error) error { func (cc *ClientConn) updateResolverStateAndUnlock(s resolver.State, err error) error {
defer cc.firstResolveEvent.Fire() defer cc.firstResolveEvent.Fire()
cc.mu.Lock()
// Check if the ClientConn is already closed. Some fields (e.g. // Check if the ClientConn is already closed. Some fields (e.g.
// balancerWrapper) are set to nil when closing the ClientConn, and could // balancerWrapper) are set to nil when closing the ClientConn, and could
// cause nil pointer panic if we don't have this check. // cause nil pointer panic if we don't have this check.
@ -872,7 +766,7 @@ func (cc *ClientConn) updateResolverState(s resolver.State, err error) error {
if cc.sc == nil { if cc.sc == nil {
// Apply the failing LB only if we haven't received valid service config // Apply the failing LB only if we haven't received valid service config
// from the name resolver in the past. // from the name resolver in the past.
cc.applyFailingLB(s.ServiceConfig) cc.applyFailingLBLocked(s.ServiceConfig)
cc.mu.Unlock() cc.mu.Unlock()
return ret return ret
} }
@ -894,15 +788,13 @@ func (cc *ClientConn) updateResolverState(s resolver.State, err error) error {
return ret return ret
} }
// applyFailingLB is akin to configuring an LB policy on the channel which // applyFailingLBLocked is akin to configuring an LB policy on the channel which
// always fails RPCs. Here, an actual LB policy is not configured, but an always // always fails RPCs. Here, an actual LB policy is not configured, but an always
// erroring picker is configured, which returns errors with information about // erroring picker is configured, which returns errors with information about
// what was invalid in the received service config. A config selector with no // what was invalid in the received service config. A config selector with no
// service config is configured, and the connectivity state of the channel is // service config is configured, and the connectivity state of the channel is
// set to TransientFailure. // set to TransientFailure.
// func (cc *ClientConn) applyFailingLBLocked(sc *serviceconfig.ParseResult) {
// Caller must hold cc.mu.
func (cc *ClientConn) applyFailingLB(sc *serviceconfig.ParseResult) {
var err error var err error
if sc.Err != nil { if sc.Err != nil {
err = status.Errorf(codes.Unavailable, "error parsing service config: %v", sc.Err) err = status.Errorf(codes.Unavailable, "error parsing service config: %v", sc.Err)
@ -910,14 +802,10 @@ func (cc *ClientConn) applyFailingLB(sc *serviceconfig.ParseResult) {
err = status.Errorf(codes.Unavailable, "illegal service config type: %T", sc.Config) err = status.Errorf(codes.Unavailable, "illegal service config type: %T", sc.Config)
} }
cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{nil}) cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{nil})
cc.blockingpicker.updatePicker(base.NewErrPicker(err)) cc.pickerWrapper.updatePicker(base.NewErrPicker(err))
cc.csMgr.updateState(connectivity.TransientFailure) cc.csMgr.updateState(connectivity.TransientFailure)
} }
func (cc *ClientConn) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State, err error) {
cc.balancerWrapper.updateSubConnState(sc, s, err)
}
// Makes a copy of the input addresses slice and clears out the balancer // Makes a copy of the input addresses slice and clears out the balancer
// attributes field. Addresses are passed during subconn creation and address // attributes field. Addresses are passed during subconn creation and address
// update operations. In both cases, we will clear the balancer attributes by // update operations. In both cases, we will clear the balancer attributes by
@ -932,10 +820,14 @@ func copyAddressesWithoutBalancerAttributes(in []resolver.Address) []resolver.Ad
return out return out
} }
// newAddrConn creates an addrConn for addrs and adds it to cc.conns. // newAddrConnLocked creates an addrConn for addrs and adds it to cc.conns.
// //
// Caller needs to make sure len(addrs) > 0. // Caller needs to make sure len(addrs) > 0.
func (cc *ClientConn) newAddrConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (*addrConn, error) { func (cc *ClientConn) newAddrConnLocked(addrs []resolver.Address, opts balancer.NewSubConnOptions) (*addrConn, error) {
if cc.conns == nil {
return nil, ErrClientConnClosing
}
ac := &addrConn{ ac := &addrConn{
state: connectivity.Idle, state: connectivity.Idle,
cc: cc, cc: cc,
@ -947,12 +839,6 @@ func (cc *ClientConn) newAddrConn(addrs []resolver.Address, opts balancer.NewSub
stateChan: make(chan struct{}), stateChan: make(chan struct{}),
} }
ac.ctx, ac.cancel = context.WithCancel(cc.ctx) ac.ctx, ac.cancel = context.WithCancel(cc.ctx)
// Track ac in cc. This needs to be done before any getTransport(...) is called.
cc.mu.Lock()
defer cc.mu.Unlock()
if cc.conns == nil {
return nil, ErrClientConnClosing
}
var err error var err error
ac.channelzID, err = channelz.RegisterSubChannel(ac, cc.channelzID, "") ac.channelzID, err = channelz.RegisterSubChannel(ac, cc.channelzID, "")
@ -968,6 +854,7 @@ func (cc *ClientConn) newAddrConn(addrs []resolver.Address, opts balancer.NewSub
}, },
}) })
// Track ac in cc. This needs to be done before any getTransport(...) is called.
cc.conns[ac] = struct{}{} cc.conns[ac] = struct{}{}
return ac, nil return ac, nil
} }
@ -1174,7 +1061,7 @@ func (cc *ClientConn) healthCheckConfig() *healthCheckConfig {
} }
func (cc *ClientConn) getTransport(ctx context.Context, failfast bool, method string) (transport.ClientTransport, balancer.PickResult, error) { func (cc *ClientConn) getTransport(ctx context.Context, failfast bool, method string) (transport.ClientTransport, balancer.PickResult, error) {
return cc.blockingpicker.pick(ctx, failfast, balancer.PickInfo{ return cc.pickerWrapper.pick(ctx, failfast, balancer.PickInfo{
Ctx: ctx, Ctx: ctx,
FullMethodName: method, FullMethodName: method,
}) })
@ -1216,12 +1103,12 @@ func (cc *ClientConn) applyServiceConfigAndBalancer(sc *ServiceConfig, configSel
func (cc *ClientConn) resolveNow(o resolver.ResolveNowOptions) { func (cc *ClientConn) resolveNow(o resolver.ResolveNowOptions) {
cc.mu.RLock() cc.mu.RLock()
r := cc.resolverWrapper cc.resolverWrapper.resolveNow(o)
cc.mu.RUnlock() cc.mu.RUnlock()
if r == nil { }
return
} func (cc *ClientConn) resolveNowLocked(o resolver.ResolveNowOptions) {
go r.resolveNow(o) cc.resolverWrapper.resolveNow(o)
} }
// ResetConnectBackoff wakes up all subchannels in transient failure and causes // ResetConnectBackoff wakes up all subchannels in transient failure and causes
@ -1253,40 +1140,32 @@ func (cc *ClientConn) Close() error {
<-cc.csMgr.pubSub.Done() <-cc.csMgr.pubSub.Done()
}() }()
// Prevent calls to enter/exit idle immediately, and ensure we are not
// currently entering/exiting idle mode.
cc.idlenessMgr.Close()
cc.mu.Lock() cc.mu.Lock()
if cc.conns == nil { if cc.conns == nil {
cc.mu.Unlock() cc.mu.Unlock()
return ErrClientConnClosing return ErrClientConnClosing
} }
for cc.idlenessState == ccIdlenessStateExitingIdle {
cc.exitIdleCond.Wait()
}
conns := cc.conns conns := cc.conns
cc.conns = nil cc.conns = nil
cc.csMgr.updateState(connectivity.Shutdown) cc.csMgr.updateState(connectivity.Shutdown)
pWrapper := cc.blockingpicker // We can safely unlock and continue to access all fields now as
rWrapper := cc.resolverWrapper // cc.conns==nil, preventing any further operations on cc.
bWrapper := cc.balancerWrapper
idlenessMgr := cc.idlenessMgr
cc.mu.Unlock() cc.mu.Unlock()
cc.resolverWrapper.close()
// The order of closing matters here since the balancer wrapper assumes the // The order of closing matters here since the balancer wrapper assumes the
// picker is closed before it is closed. // picker is closed before it is closed.
if pWrapper != nil { cc.pickerWrapper.close()
pWrapper.close() cc.balancerWrapper.close()
}
if bWrapper != nil { <-cc.resolverWrapper.serializer.Done()
bWrapper.close() <-cc.balancerWrapper.serializer.Done()
}
if rWrapper != nil {
rWrapper.close()
}
if idlenessMgr != nil {
idlenessMgr.Close()
}
for ac := range conns { for ac := range conns {
ac.tearDown(ErrClientConnClosing) ac.tearDown(ErrClientConnClosing)
@ -1307,7 +1186,7 @@ type addrConn struct {
cc *ClientConn cc *ClientConn
dopts dialOptions dopts dialOptions
acbw balancer.SubConn acbw *acBalancerWrapper
scopts balancer.NewSubConnOptions scopts balancer.NewSubConnOptions
// transport is set when there's a viable transport (note: ac state may not be READY as LB channel // transport is set when there's a viable transport (note: ac state may not be READY as LB channel
@ -1345,7 +1224,7 @@ func (ac *addrConn) updateConnectivityState(s connectivity.State, lastErr error)
} else { } else {
channelz.Infof(logger, ac.channelzID, "Subchannel Connectivity change to %v, last error: %s", s, lastErr) channelz.Infof(logger, ac.channelzID, "Subchannel Connectivity change to %v, last error: %s", s, lastErr)
} }
ac.cc.handleSubConnStateChange(ac.acbw, s, lastErr) ac.acbw.updateState(s, lastErr)
} }
// adjustParams updates parameters used to create transports upon // adjustParams updates parameters used to create transports upon
@ -1849,7 +1728,7 @@ func (cc *ClientConn) parseTargetAndFindResolver() error {
if err != nil { if err != nil {
channelz.Infof(logger, cc.channelzID, "dial target %q parse failed: %v", cc.target, err) channelz.Infof(logger, cc.channelzID, "dial target %q parse failed: %v", cc.target, err)
} else { } else {
channelz.Infof(logger, cc.channelzID, "parsed dial target is: %+v", parsedTarget) channelz.Infof(logger, cc.channelzID, "parsed dial target is: %#v", parsedTarget)
rb = cc.getResolver(parsedTarget.URL.Scheme) rb = cc.getResolver(parsedTarget.URL.Scheme)
if rb != nil { if rb != nil {
cc.parsedTarget = parsedTarget cc.parsedTarget = parsedTarget
@ -2007,32 +1886,3 @@ func (cc *ClientConn) determineAuthority() error {
channelz.Infof(logger, cc.channelzID, "Channel authority set to %q", cc.authority) channelz.Infof(logger, cc.channelzID, "Channel authority set to %q", cc.authority)
return nil return nil
} }
// initResolverWrapper creates a ccResolverWrapper, which builds the name
// resolver. This method grabs the lock to assign the newly built resolver
// wrapper to the cc.resolverWrapper field.
func (cc *ClientConn) initResolverWrapper(creds credentials.TransportCredentials) error {
rw, err := newCCResolverWrapper(cc, ccResolverWrapperOpts{
target: cc.parsedTarget,
builder: cc.resolverBuilder,
bOpts: resolver.BuildOptions{
DisableServiceConfig: cc.dopts.disableServiceConfig,
DialCreds: creds,
CredsBundle: cc.dopts.copts.CredsBundle,
Dialer: cc.dopts.copts.Dialer,
},
channelzID: cc.channelzID,
})
if err != nil {
return fmt.Errorf("failed to build resolver: %v", err)
}
// Resolver implementations may report state update or error inline when
// built (or right after), and this is handled in cc.updateResolverState.
// Also, an error from the resolver might lead to a re-resolution request
// from the balancer, which is handled in resolveNow() where
// `cc.resolverWrapper` is accessed. Hence, we need to hold the lock here.
cc.mu.Lock()
cc.resolverWrapper = rw
cc.mu.Unlock()
return nil
}

View File

@ -25,7 +25,13 @@ import (
"strconv" "strconv"
) )
// A Code is an unsigned 32-bit error code as defined in the gRPC spec. // A Code is a status code defined according to the [gRPC documentation].
//
// Only the codes defined as consts in this package are valid codes. Do not use
// other code values. Behavior of other codes is implementation-specific and
// interoperability between implementations is not guaranteed.
//
// [gRPC documentation]: https://github.com/grpc/grpc/blob/master/doc/statuscodes.md
type Code uint32 type Code uint32
const ( const (

View File

@ -44,10 +44,25 @@ func (t TLSInfo) AuthType() string {
return "tls" return "tls"
} }
// cipherSuiteLookup returns the string version of a TLS cipher suite ID.
func cipherSuiteLookup(cipherSuiteID uint16) string {
for _, s := range tls.CipherSuites() {
if s.ID == cipherSuiteID {
return s.Name
}
}
for _, s := range tls.InsecureCipherSuites() {
if s.ID == cipherSuiteID {
return s.Name
}
}
return fmt.Sprintf("unknown ID: %v", cipherSuiteID)
}
// GetSecurityValue returns security info requested by channelz. // GetSecurityValue returns security info requested by channelz.
func (t TLSInfo) GetSecurityValue() ChannelzSecurityValue { func (t TLSInfo) GetSecurityValue() ChannelzSecurityValue {
v := &TLSChannelzSecurityValue{ v := &TLSChannelzSecurityValue{
StandardName: cipherSuiteLookup[t.State.CipherSuite], StandardName: cipherSuiteLookup(t.State.CipherSuite),
} }
// Currently there's no way to get LocalCertificate info from tls package. // Currently there's no way to get LocalCertificate info from tls package.
if len(t.State.PeerCertificates) > 0 { if len(t.State.PeerCertificates) > 0 {
@ -138,10 +153,39 @@ func (c *tlsCreds) OverrideServerName(serverNameOverride string) error {
return nil return nil
} }
// The following cipher suites are forbidden for use with HTTP/2 by
// https://datatracker.ietf.org/doc/html/rfc7540#appendix-A
var tls12ForbiddenCipherSuites = map[uint16]struct{}{
tls.TLS_RSA_WITH_AES_128_CBC_SHA: {},
tls.TLS_RSA_WITH_AES_256_CBC_SHA: {},
tls.TLS_RSA_WITH_AES_128_GCM_SHA256: {},
tls.TLS_RSA_WITH_AES_256_GCM_SHA384: {},
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: {},
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: {},
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: {},
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: {},
}
// NewTLS uses c to construct a TransportCredentials based on TLS. // NewTLS uses c to construct a TransportCredentials based on TLS.
func NewTLS(c *tls.Config) TransportCredentials { func NewTLS(c *tls.Config) TransportCredentials {
tc := &tlsCreds{credinternal.CloneTLSConfig(c)} tc := &tlsCreds{credinternal.CloneTLSConfig(c)}
tc.config.NextProtos = credinternal.AppendH2ToNextProtos(tc.config.NextProtos) tc.config.NextProtos = credinternal.AppendH2ToNextProtos(tc.config.NextProtos)
// If the user did not configure a MinVersion and did not configure a
// MaxVersion < 1.2, use MinVersion=1.2, which is required by
// https://datatracker.ietf.org/doc/html/rfc7540#section-9.2
if tc.config.MinVersion == 0 && (tc.config.MaxVersion == 0 || tc.config.MaxVersion >= tls.VersionTLS12) {
tc.config.MinVersion = tls.VersionTLS12
}
// If the user did not configure CipherSuites, use all "secure" cipher
// suites reported by the TLS package, but remove some explicitly forbidden
// by https://datatracker.ietf.org/doc/html/rfc7540#appendix-A
if tc.config.CipherSuites == nil {
for _, cs := range tls.CipherSuites() {
if _, ok := tls12ForbiddenCipherSuites[cs.ID]; !ok {
tc.config.CipherSuites = append(tc.config.CipherSuites, cs.ID)
}
}
}
return tc return tc
} }
@ -205,32 +249,3 @@ type TLSChannelzSecurityValue struct {
LocalCertificate []byte LocalCertificate []byte
RemoteCertificate []byte RemoteCertificate []byte
} }
var cipherSuiteLookup = map[uint16]string{
tls.TLS_RSA_WITH_RC4_128_SHA: "TLS_RSA_WITH_RC4_128_SHA",
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
tls.TLS_RSA_WITH_AES_128_CBC_SHA: "TLS_RSA_WITH_AES_128_CBC_SHA",
tls.TLS_RSA_WITH_AES_256_CBC_SHA: "TLS_RSA_WITH_AES_256_CBC_SHA",
tls.TLS_RSA_WITH_AES_128_GCM_SHA256: "TLS_RSA_WITH_AES_128_GCM_SHA256",
tls.TLS_RSA_WITH_AES_256_GCM_SHA384: "TLS_RSA_WITH_AES_256_GCM_SHA384",
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA: "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
tls.TLS_FALLBACK_SCSV: "TLS_FALLBACK_SCSV",
tls.TLS_RSA_WITH_AES_128_CBC_SHA256: "TLS_RSA_WITH_AES_128_CBC_SHA256",
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
tls.TLS_AES_128_GCM_SHA256: "TLS_AES_128_GCM_SHA256",
tls.TLS_AES_256_GCM_SHA384: "TLS_AES_256_GCM_SHA384",
tls.TLS_CHACHA20_POLY1305_SHA256: "TLS_CHACHA20_POLY1305_SHA256",
}

View File

@ -46,6 +46,7 @@ func init() {
internal.WithBinaryLogger = withBinaryLogger internal.WithBinaryLogger = withBinaryLogger
internal.JoinDialOptions = newJoinDialOption internal.JoinDialOptions = newJoinDialOption
internal.DisableGlobalDialOptions = newDisableGlobalDialOptions internal.DisableGlobalDialOptions = newDisableGlobalDialOptions
internal.WithRecvBufferPool = withRecvBufferPool
} }
// dialOptions configure a Dial call. dialOptions are set by the DialOption // dialOptions configure a Dial call. dialOptions are set by the DialOption
@ -63,7 +64,6 @@ type dialOptions struct {
block bool block bool
returnLastError bool returnLastError bool
timeout time.Duration timeout time.Duration
scChan <-chan ServiceConfig
authority string authority string
binaryLogger binarylog.Logger binaryLogger binarylog.Logger
copts transport.ConnectOptions copts transport.ConnectOptions
@ -250,19 +250,6 @@ func WithDecompressor(dc Decompressor) DialOption {
}) })
} }
// WithServiceConfig returns a DialOption which has a channel to read the
// service configuration.
//
// Deprecated: service config should be received through name resolver or via
// WithDefaultServiceConfig, as specified at
// https://github.com/grpc/grpc/blob/master/doc/service_config.md. Will be
// removed in a future 1.x release.
func WithServiceConfig(c <-chan ServiceConfig) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.scChan = c
})
}
// WithConnectParams configures the ClientConn to use the provided ConnectParams // WithConnectParams configures the ClientConn to use the provided ConnectParams
// for creating and maintaining connections to servers. // for creating and maintaining connections to servers.
// //
@ -413,6 +400,17 @@ func WithTimeout(d time.Duration) DialOption {
// connections. If FailOnNonTempDialError() is set to true, and an error is // connections. If FailOnNonTempDialError() is set to true, and an error is
// returned by f, gRPC checks the error's Temporary() method to decide if it // returned by f, gRPC checks the error's Temporary() method to decide if it
// should try to reconnect to the network address. // should try to reconnect to the network address.
//
// Note: All supported releases of Go (as of December 2023) override the OS
// defaults for TCP keepalive time and interval to 15s. To enable TCP keepalive
// with OS defaults for keepalive time and interval, use a net.Dialer that sets
// the KeepAlive field to a negative value, and sets the SO_KEEPALIVE socket
// option to true from the Control field. For a concrete example of how to do
// this, see internal.NetDialerWithTCPKeepalive().
//
// For more information, please see [issue 23459] in the Go github repo.
//
// [issue 23459]: https://github.com/golang/go/issues/23459
func WithContextDialer(f func(context.Context, string) (net.Conn, error)) DialOption { func WithContextDialer(f func(context.Context, string) (net.Conn, error)) DialOption {
return newFuncDialOption(func(o *dialOptions) { return newFuncDialOption(func(o *dialOptions) {
o.copts.Dialer = f o.copts.Dialer = f
@ -487,7 +485,7 @@ func FailOnNonTempDialError(f bool) DialOption {
// the RPCs. // the RPCs.
func WithUserAgent(s string) DialOption { func WithUserAgent(s string) DialOption {
return newFuncDialOption(func(o *dialOptions) { return newFuncDialOption(func(o *dialOptions) {
o.copts.UserAgent = s o.copts.UserAgent = s + " " + grpcUA
}) })
} }
@ -637,14 +635,16 @@ func withHealthCheckFunc(f internal.HealthChecker) DialOption {
func defaultDialOptions() dialOptions { func defaultDialOptions() dialOptions {
return dialOptions{ return dialOptions{
healthCheckFunc: internal.HealthCheckFunc,
copts: transport.ConnectOptions{ copts: transport.ConnectOptions{
WriteBufferSize: defaultWriteBufSize,
ReadBufferSize: defaultReadBufSize, ReadBufferSize: defaultReadBufSize,
WriteBufferSize: defaultWriteBufSize,
UseProxy: true, UseProxy: true,
UserAgent: grpcUA,
}, },
recvBufferPool: nopBufferPool{}, bs: internalbackoff.DefaultExponential,
idleTimeout: 30 * time.Minute, healthCheckFunc: internal.HealthCheckFunc,
idleTimeout: 30 * time.Minute,
recvBufferPool: nopBufferPool{},
} }
} }
@ -705,11 +705,13 @@ func WithIdleTimeout(d time.Duration) DialOption {
// options are used: WithStatsHandler, EnableTracing, or binary logging. In such // options are used: WithStatsHandler, EnableTracing, or binary logging. In such
// cases, the shared buffer pool will be ignored. // cases, the shared buffer pool will be ignored.
// //
// # Experimental // Deprecated: use experimental.WithRecvBufferPool instead. Will be deleted in
// // v1.60.0 or later.
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func WithRecvBufferPool(bufferPool SharedBufferPool) DialOption { func WithRecvBufferPool(bufferPool SharedBufferPool) DialOption {
return withRecvBufferPool(bufferPool)
}
func withRecvBufferPool(bufferPool SharedBufferPool) DialOption {
return newFuncDialOption(func(o *dialOptions) { return newFuncDialOption(func(o *dialOptions) {
o.recvBufferPool = bufferPool o.recvBufferPool = bufferPool
}) })

View File

@ -18,7 +18,10 @@
// Package buffer provides an implementation of an unbounded buffer. // Package buffer provides an implementation of an unbounded buffer.
package buffer package buffer
import "sync" import (
"errors"
"sync"
)
// Unbounded is an implementation of an unbounded buffer which does not use // Unbounded is an implementation of an unbounded buffer which does not use
// extra goroutines. This is typically used for passing updates from one entity // extra goroutines. This is typically used for passing updates from one entity
@ -36,6 +39,7 @@ import "sync"
type Unbounded struct { type Unbounded struct {
c chan any c chan any
closed bool closed bool
closing bool
mu sync.Mutex mu sync.Mutex
backlog []any backlog []any
} }
@ -45,32 +49,32 @@ func NewUnbounded() *Unbounded {
return &Unbounded{c: make(chan any, 1)} return &Unbounded{c: make(chan any, 1)}
} }
var errBufferClosed = errors.New("Put called on closed buffer.Unbounded")
// Put adds t to the unbounded buffer. // Put adds t to the unbounded buffer.
func (b *Unbounded) Put(t any) { func (b *Unbounded) Put(t any) error {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
if b.closed { if b.closing {
return return errBufferClosed
} }
if len(b.backlog) == 0 { if len(b.backlog) == 0 {
select { select {
case b.c <- t: case b.c <- t:
return return nil
default: default:
} }
} }
b.backlog = append(b.backlog, t) b.backlog = append(b.backlog, t)
return nil
} }
// Load sends the earliest buffered data, if any, onto the read channel // Load sends the earliest buffered data, if any, onto the read channel returned
// returned by Get(). Users are expected to call this every time they read a // by Get(). Users are expected to call this every time they successfully read a
// value from the read channel. // value from the read channel.
func (b *Unbounded) Load() { func (b *Unbounded) Load() {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
if b.closed {
return
}
if len(b.backlog) > 0 { if len(b.backlog) > 0 {
select { select {
case b.c <- b.backlog[0]: case b.c <- b.backlog[0]:
@ -78,6 +82,8 @@ func (b *Unbounded) Load() {
b.backlog = b.backlog[1:] b.backlog = b.backlog[1:]
default: default:
} }
} else if b.closing && !b.closed {
close(b.c)
} }
} }
@ -88,18 +94,23 @@ func (b *Unbounded) Load() {
// send the next buffered value onto the channel if there is any. // send the next buffered value onto the channel if there is any.
// //
// If the unbounded buffer is closed, the read channel returned by this method // If the unbounded buffer is closed, the read channel returned by this method
// is closed. // is closed after all data is drained.
func (b *Unbounded) Get() <-chan any { func (b *Unbounded) Get() <-chan any {
return b.c return b.c
} }
// Close closes the unbounded buffer. // Close closes the unbounded buffer. No subsequent data may be Put(), and the
// channel returned from Get() will be closed after all the data is read and
// Load() is called for the final time.
func (b *Unbounded) Close() { func (b *Unbounded) Close() {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
if b.closed { if b.closing {
return return
} }
b.closed = true b.closing = true
close(b.c) if len(b.backlog) == 0 {
b.closed = true
close(b.c)
}
} }

View File

@ -31,6 +31,7 @@ import (
"time" "time"
"google.golang.org/grpc/grpclog" "google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal"
) )
const ( const (
@ -58,6 +59,12 @@ func TurnOn() {
} }
} }
func init() {
internal.ChannelzTurnOffForTesting = func() {
atomic.StoreInt32(&curState, 0)
}
}
// IsOn returns whether channelz data collection is on. // IsOn returns whether channelz data collection is on.
func IsOn() bool { func IsOn() bool {
return atomic.LoadInt32(&curState) == 1 return atomic.LoadInt32(&curState) == 1

View File

@ -36,9 +36,6 @@ var (
// "GRPC_RING_HASH_CAP". This does not override the default bounds // "GRPC_RING_HASH_CAP". This does not override the default bounds
// checking which NACKs configs specifying ring sizes > 8*1024*1024 (~8M). // checking which NACKs configs specifying ring sizes > 8*1024*1024 (~8M).
RingHashCap = uint64FromEnv("GRPC_RING_HASH_CAP", 4096, 1, 8*1024*1024) RingHashCap = uint64FromEnv("GRPC_RING_HASH_CAP", 4096, 1, 8*1024*1024)
// PickFirstLBConfig is set if we should support configuration of the
// pick_first LB policy.
PickFirstLBConfig = boolFromEnv("GRPC_EXPERIMENTAL_PICKFIRST_LB_CONFIG", true)
// LeastRequestLB is set if we should support the least_request_experimental // LeastRequestLB is set if we should support the least_request_experimental
// LB policy, which can be enabled by setting the environment variable // LB policy, which can be enabled by setting the environment variable
// "GRPC_EXPERIMENTAL_ENABLE_LEAST_REQUEST" to "true". // "GRPC_EXPERIMENTAL_ENABLE_LEAST_REQUEST" to "true".

View File

@ -50,46 +50,7 @@ var (
// //
// When both bootstrap FileName and FileContent are set, FileName is used. // When both bootstrap FileName and FileContent are set, FileName is used.
XDSBootstrapFileContent = os.Getenv(XDSBootstrapFileContentEnv) XDSBootstrapFileContent = os.Getenv(XDSBootstrapFileContentEnv)
// XDSRingHash indicates whether ring hash support is enabled, which can be
// disabled by setting the environment variable
// "GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH" to "false".
XDSRingHash = boolFromEnv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH", true)
// XDSClientSideSecurity is used to control processing of security
// configuration on the client-side.
//
// Note that there is no env var protection for the server-side because we
// have a brand new API on the server-side and users explicitly need to use
// the new API to get security integration on the server.
XDSClientSideSecurity = boolFromEnv("GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT", true)
// XDSAggregateAndDNS indicates whether processing of aggregated cluster and
// DNS cluster is enabled, which can be disabled by setting the environment
// variable "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER"
// to "false".
XDSAggregateAndDNS = boolFromEnv("GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER", true)
// XDSRBAC indicates whether xDS configured RBAC HTTP Filter is enabled,
// which can be disabled by setting the environment variable
// "GRPC_XDS_EXPERIMENTAL_RBAC" to "false".
XDSRBAC = boolFromEnv("GRPC_XDS_EXPERIMENTAL_RBAC", true)
// XDSOutlierDetection indicates whether outlier detection support is
// enabled, which can be disabled by setting the environment variable
// "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION" to "false".
XDSOutlierDetection = boolFromEnv("GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION", true)
// XDSFederation indicates whether federation support is enabled, which can
// be enabled by setting the environment variable
// "GRPC_EXPERIMENTAL_XDS_FEDERATION" to "true".
XDSFederation = boolFromEnv("GRPC_EXPERIMENTAL_XDS_FEDERATION", true)
// XDSRLS indicates whether processing of Cluster Specifier plugins and
// support for the RLS CLuster Specifier is enabled, which can be disabled by
// setting the environment variable "GRPC_EXPERIMENTAL_XDS_RLS_LB" to
// "false".
XDSRLS = boolFromEnv("GRPC_EXPERIMENTAL_XDS_RLS_LB", true)
// C2PResolverTestOnlyTrafficDirectorURI is the TD URI for testing. // C2PResolverTestOnlyTrafficDirectorURI is the TD URI for testing.
C2PResolverTestOnlyTrafficDirectorURI = os.Getenv("GRPC_TEST_ONLY_GOOGLE_C2P_RESOLVER_TRAFFIC_DIRECTOR_URI") C2PResolverTestOnlyTrafficDirectorURI = os.Getenv("GRPC_TEST_ONLY_GOOGLE_C2P_RESOLVER_TRAFFIC_DIRECTOR_URI")
// XDSCustomLBPolicy indicates whether Custom LB Policies are enabled, which
// can be disabled by setting the environment variable
// "GRPC_EXPERIMENTAL_XDS_CUSTOM_LB_CONFIG" to "false".
XDSCustomLBPolicy = boolFromEnv("GRPC_EXPERIMENTAL_XDS_CUSTOM_LB_CONFIG", true)
) )

28
vendor/google.golang.org/grpc/internal/experimental.go generated vendored Normal file
View File

@ -0,0 +1,28 @@
/*
* Copyright 2023 gRPC 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 internal
var (
// WithRecvBufferPool is implemented by the grpc package and returns a dial
// option to configure a shared buffer pool for a grpc.ClientConn.
WithRecvBufferPool any // func (grpc.SharedBufferPool) grpc.DialOption
// RecvBufferPool is implemented by the grpc package and returns a server
// option to configure a shared buffer pool for a grpc.Server.
RecvBufferPool any // func (grpc.SharedBufferPool) grpc.ServerOption
)

View File

@ -20,7 +20,6 @@ package grpcsync
import ( import (
"context" "context"
"sync"
"google.golang.org/grpc/internal/buffer" "google.golang.org/grpc/internal/buffer"
) )
@ -38,8 +37,6 @@ type CallbackSerializer struct {
done chan struct{} done chan struct{}
callbacks *buffer.Unbounded callbacks *buffer.Unbounded
closedMu sync.Mutex
closed bool
} }
// NewCallbackSerializer returns a new CallbackSerializer instance. The provided // NewCallbackSerializer returns a new CallbackSerializer instance. The provided
@ -65,56 +62,34 @@ func NewCallbackSerializer(ctx context.Context) *CallbackSerializer {
// callbacks to be executed by the serializer. It is not possible to add // callbacks to be executed by the serializer. It is not possible to add
// callbacks once the context passed to NewCallbackSerializer is cancelled. // callbacks once the context passed to NewCallbackSerializer is cancelled.
func (cs *CallbackSerializer) Schedule(f func(ctx context.Context)) bool { func (cs *CallbackSerializer) Schedule(f func(ctx context.Context)) bool {
cs.closedMu.Lock() return cs.callbacks.Put(f) == nil
defer cs.closedMu.Unlock()
if cs.closed {
return false
}
cs.callbacks.Put(f)
return true
} }
func (cs *CallbackSerializer) run(ctx context.Context) { func (cs *CallbackSerializer) run(ctx context.Context) {
var backlog []func(context.Context)
defer close(cs.done) defer close(cs.done)
// TODO: when Go 1.21 is the oldest supported version, this loop and Close
// can be replaced with:
//
// context.AfterFunc(ctx, cs.callbacks.Close)
for ctx.Err() == nil { for ctx.Err() == nil {
select { select {
case <-ctx.Done(): case <-ctx.Done():
// Do nothing here. Next iteration of the for loop will not happen, // Do nothing here. Next iteration of the for loop will not happen,
// since ctx.Err() would be non-nil. // since ctx.Err() would be non-nil.
case callback, ok := <-cs.callbacks.Get(): case cb := <-cs.callbacks.Get():
if !ok {
return
}
cs.callbacks.Load() cs.callbacks.Load()
callback.(func(ctx context.Context))(ctx) cb.(func(context.Context))(ctx)
} }
} }
// Fetch pending callbacks if any, and execute them before returning from // Close the buffer to prevent new callbacks from being added.
// this method and closing cs.done.
cs.closedMu.Lock()
cs.closed = true
backlog = cs.fetchPendingCallbacks()
cs.callbacks.Close() cs.callbacks.Close()
cs.closedMu.Unlock()
for _, b := range backlog {
b(ctx)
}
}
func (cs *CallbackSerializer) fetchPendingCallbacks() []func(context.Context) { // Run all pending callbacks.
var backlog []func(context.Context) for cb := range cs.callbacks.Get() {
for { cs.callbacks.Load()
select { cb.(func(context.Context))(ctx)
case b := <-cs.callbacks.Get():
backlog = append(backlog, b.(func(context.Context)))
cs.callbacks.Load()
default:
return backlog
}
} }
} }

View File

@ -26,8 +26,6 @@ import (
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"google.golang.org/grpc/grpclog"
) )
// For overriding in unit tests. // For overriding in unit tests.
@ -39,27 +37,12 @@ var timeAfterFunc = func(d time.Duration, f func()) *time.Timer {
// and exit from idle mode. // and exit from idle mode.
type Enforcer interface { type Enforcer interface {
ExitIdleMode() error ExitIdleMode() error
EnterIdleMode() error EnterIdleMode()
} }
// Manager defines the functionality required to track RPC activity on a // Manager implements idleness detection and calls the configured Enforcer to
// channel. // enter/exit idle mode when appropriate. Must be created by NewManager.
type Manager interface { type Manager struct {
OnCallBegin() error
OnCallEnd()
Close()
}
type noopManager struct{}
func (noopManager) OnCallBegin() error { return nil }
func (noopManager) OnCallEnd() {}
func (noopManager) Close() {}
// manager implements the Manager interface. It uses atomic operations to
// synchronize access to shared state and a mutex to guarantee mutual exclusion
// in a critical section.
type manager struct {
// State accessed atomically. // State accessed atomically.
lastCallEndTime int64 // Unix timestamp in nanos; time when the most recent RPC completed. lastCallEndTime int64 // Unix timestamp in nanos; time when the most recent RPC completed.
activeCallsCount int32 // Count of active RPCs; -math.MaxInt32 means channel is idle or is trying to get there. activeCallsCount int32 // Count of active RPCs; -math.MaxInt32 means channel is idle or is trying to get there.
@ -69,8 +52,7 @@ type manager struct {
// Can be accessed without atomics or mutex since these are set at creation // Can be accessed without atomics or mutex since these are set at creation
// time and read-only after that. // time and read-only after that.
enforcer Enforcer // Functionality provided by grpc.ClientConn. enforcer Enforcer // Functionality provided by grpc.ClientConn.
timeout int64 // Idle timeout duration nanos stored as an int64. timeout time.Duration
logger grpclog.LoggerV2
// idleMu is used to guarantee mutual exclusion in two scenarios: // idleMu is used to guarantee mutual exclusion in two scenarios:
// - Opposing intentions: // - Opposing intentions:
@ -88,57 +70,48 @@ type manager struct {
timer *time.Timer timer *time.Timer
} }
// ManagerOptions is a collection of options used by
// NewManager.
type ManagerOptions struct {
Enforcer Enforcer
Timeout time.Duration
Logger grpclog.LoggerV2
}
// NewManager creates a new idleness manager implementation for the // NewManager creates a new idleness manager implementation for the
// given idle timeout. // given idle timeout. It begins in idle mode.
func NewManager(opts ManagerOptions) Manager { func NewManager(enforcer Enforcer, timeout time.Duration) *Manager {
if opts.Timeout == 0 { return &Manager{
return noopManager{} enforcer: enforcer,
timeout: timeout,
actuallyIdle: true,
activeCallsCount: -math.MaxInt32,
} }
m := &manager{
enforcer: opts.Enforcer,
timeout: int64(opts.Timeout),
logger: opts.Logger,
}
m.timer = timeAfterFunc(opts.Timeout, m.handleIdleTimeout)
return m
} }
// resetIdleTimer resets the idle timer to the given duration. This method // resetIdleTimerLocked resets the idle timer to the given duration. Called
// should only be called from the timer callback. // when exiting idle mode or when the timer fires and we need to reset it.
func (m *manager) resetIdleTimer(d time.Duration) { func (m *Manager) resetIdleTimerLocked(d time.Duration) {
m.idleMu.Lock() if m.isClosed() || m.timeout == 0 || m.actuallyIdle {
defer m.idleMu.Unlock()
if m.timer == nil {
// Only close sets timer to nil. We are done.
return return
} }
// It is safe to ignore the return value from Reset() because this method is // It is safe to ignore the return value from Reset() because this method is
// only ever called from the timer callback, which means the timer has // only ever called from the timer callback or when exiting idle mode.
// already fired. if m.timer != nil {
m.timer.Reset(d) m.timer.Stop()
}
m.timer = timeAfterFunc(d, m.handleIdleTimeout)
}
func (m *Manager) resetIdleTimer(d time.Duration) {
m.idleMu.Lock()
defer m.idleMu.Unlock()
m.resetIdleTimerLocked(d)
} }
// handleIdleTimeout is the timer callback that is invoked upon expiry of the // handleIdleTimeout is the timer callback that is invoked upon expiry of the
// configured idle timeout. The channel is considered inactive if there are no // configured idle timeout. The channel is considered inactive if there are no
// ongoing calls and no RPC activity since the last time the timer fired. // ongoing calls and no RPC activity since the last time the timer fired.
func (m *manager) handleIdleTimeout() { func (m *Manager) handleIdleTimeout() {
if m.isClosed() { if m.isClosed() {
return return
} }
if atomic.LoadInt32(&m.activeCallsCount) > 0 { if atomic.LoadInt32(&m.activeCallsCount) > 0 {
m.resetIdleTimer(time.Duration(m.timeout)) m.resetIdleTimer(m.timeout)
return return
} }
@ -148,24 +121,12 @@ func (m *manager) handleIdleTimeout() {
// Set the timer to fire after a duration of idle timeout, calculated // Set the timer to fire after a duration of idle timeout, calculated
// from the time the most recent RPC completed. // from the time the most recent RPC completed.
atomic.StoreInt32(&m.activeSinceLastTimerCheck, 0) atomic.StoreInt32(&m.activeSinceLastTimerCheck, 0)
m.resetIdleTimer(time.Duration(atomic.LoadInt64(&m.lastCallEndTime) + m.timeout - time.Now().UnixNano())) m.resetIdleTimer(time.Duration(atomic.LoadInt64(&m.lastCallEndTime)-time.Now().UnixNano()) + m.timeout)
return return
} }
// This CAS operation is extremely likely to succeed given that there has // Now that we've checked that there has been no activity, attempt to enter
// been no activity since the last time we were here. Setting the // idle mode, which is very likely to succeed.
// activeCallsCount to -math.MaxInt32 indicates to OnCallBegin() that the
// channel is either in idle mode or is trying to get there.
if !atomic.CompareAndSwapInt32(&m.activeCallsCount, 0, -math.MaxInt32) {
// This CAS operation can fail if an RPC started after we checked for
// activity at the top of this method, or one was ongoing from before
// the last time we were here. In both case, reset the timer and return.
m.resetIdleTimer(time.Duration(m.timeout))
return
}
// Now that we've set the active calls count to -math.MaxInt32, it's time to
// actually move to idle mode.
if m.tryEnterIdleMode() { if m.tryEnterIdleMode() {
// Successfully entered idle mode. No timer needed until we exit idle. // Successfully entered idle mode. No timer needed until we exit idle.
return return
@ -174,8 +135,7 @@ func (m *manager) handleIdleTimeout() {
// Failed to enter idle mode due to a concurrent RPC that kept the channel // Failed to enter idle mode due to a concurrent RPC that kept the channel
// active, or because of an error from the channel. Undo the attempt to // active, or because of an error from the channel. Undo the attempt to
// enter idle, and reset the timer to try again later. // enter idle, and reset the timer to try again later.
atomic.AddInt32(&m.activeCallsCount, math.MaxInt32) m.resetIdleTimer(m.timeout)
m.resetIdleTimer(time.Duration(m.timeout))
} }
// tryEnterIdleMode instructs the channel to enter idle mode. But before // tryEnterIdleMode instructs the channel to enter idle mode. But before
@ -185,36 +145,49 @@ func (m *manager) handleIdleTimeout() {
// Return value indicates whether or not the channel moved to idle mode. // Return value indicates whether or not the channel moved to idle mode.
// //
// Holds idleMu which ensures mutual exclusion with exitIdleMode. // Holds idleMu which ensures mutual exclusion with exitIdleMode.
func (m *manager) tryEnterIdleMode() bool { func (m *Manager) tryEnterIdleMode() bool {
// Setting the activeCallsCount to -math.MaxInt32 indicates to OnCallBegin()
// that the channel is either in idle mode or is trying to get there.
if !atomic.CompareAndSwapInt32(&m.activeCallsCount, 0, -math.MaxInt32) {
// This CAS operation can fail if an RPC started after we checked for
// activity in the timer handler, or one was ongoing from before the
// last time the timer fired, or if a test is attempting to enter idle
// mode without checking. In all cases, abort going into idle mode.
return false
}
// N.B. if we fail to enter idle mode after this, we must re-add
// math.MaxInt32 to m.activeCallsCount.
m.idleMu.Lock() m.idleMu.Lock()
defer m.idleMu.Unlock() defer m.idleMu.Unlock()
if atomic.LoadInt32(&m.activeCallsCount) != -math.MaxInt32 { if atomic.LoadInt32(&m.activeCallsCount) != -math.MaxInt32 {
// We raced and lost to a new RPC. Very rare, but stop entering idle. // We raced and lost to a new RPC. Very rare, but stop entering idle.
atomic.AddInt32(&m.activeCallsCount, math.MaxInt32)
return false return false
} }
if atomic.LoadInt32(&m.activeSinceLastTimerCheck) == 1 { if atomic.LoadInt32(&m.activeSinceLastTimerCheck) == 1 {
// An very short RPC could have come in (and also finished) after we // A very short RPC could have come in (and also finished) after we
// checked for calls count and activity in handleIdleTimeout(), but // checked for calls count and activity in handleIdleTimeout(), but
// before the CAS operation. So, we need to check for activity again. // before the CAS operation. So, we need to check for activity again.
atomic.AddInt32(&m.activeCallsCount, math.MaxInt32)
return false return false
} }
// No new RPCs have come in since we last set the active calls count value // No new RPCs have come in since we set the active calls count value to
// -math.MaxInt32 in the timer callback. And since we have the lock, it is // -math.MaxInt32. And since we have the lock, it is safe to enter idle mode
// safe to enter idle mode now. // unconditionally now.
if err := m.enforcer.EnterIdleMode(); err != nil { m.enforcer.EnterIdleMode()
m.logger.Errorf("Failed to enter idle mode: %v", err)
return false
}
// Successfully entered idle mode.
m.actuallyIdle = true m.actuallyIdle = true
return true return true
} }
func (m *Manager) EnterIdleModeForTesting() {
m.tryEnterIdleMode()
}
// OnCallBegin is invoked at the start of every RPC. // OnCallBegin is invoked at the start of every RPC.
func (m *manager) OnCallBegin() error { func (m *Manager) OnCallBegin() error {
if m.isClosed() { if m.isClosed() {
return nil return nil
} }
@ -227,7 +200,7 @@ func (m *manager) OnCallBegin() error {
// Channel is either in idle mode or is in the process of moving to idle // Channel is either in idle mode or is in the process of moving to idle
// mode. Attempt to exit idle mode to allow this RPC. // mode. Attempt to exit idle mode to allow this RPC.
if err := m.exitIdleMode(); err != nil { if err := m.ExitIdleMode(); err != nil {
// Undo the increment to calls count, and return an error causing the // Undo the increment to calls count, and return an error causing the
// RPC to fail. // RPC to fail.
atomic.AddInt32(&m.activeCallsCount, -1) atomic.AddInt32(&m.activeCallsCount, -1)
@ -238,28 +211,30 @@ func (m *manager) OnCallBegin() error {
return nil return nil
} }
// exitIdleMode instructs the channel to exit idle mode. // ExitIdleMode instructs m to call the enforcer's ExitIdleMode and update m's
// // internal state.
// Holds idleMu which ensures mutual exclusion with tryEnterIdleMode. func (m *Manager) ExitIdleMode() error {
func (m *manager) exitIdleMode() error { // Holds idleMu which ensures mutual exclusion with tryEnterIdleMode.
m.idleMu.Lock() m.idleMu.Lock()
defer m.idleMu.Unlock() defer m.idleMu.Unlock()
if !m.actuallyIdle { if m.isClosed() || !m.actuallyIdle {
// This can happen in two scenarios: // This can happen in three scenarios:
// - handleIdleTimeout() set the calls count to -math.MaxInt32 and called // - handleIdleTimeout() set the calls count to -math.MaxInt32 and called
// tryEnterIdleMode(). But before the latter could grab the lock, an RPC // tryEnterIdleMode(). But before the latter could grab the lock, an RPC
// came in and OnCallBegin() noticed that the calls count is negative. // came in and OnCallBegin() noticed that the calls count is negative.
// - Channel is in idle mode, and multiple new RPCs come in at the same // - Channel is in idle mode, and multiple new RPCs come in at the same
// time, all of them notice a negative calls count in OnCallBegin and get // time, all of them notice a negative calls count in OnCallBegin and get
// here. The first one to get the lock would got the channel to exit idle. // here. The first one to get the lock would got the channel to exit idle.
// - Channel is not in idle mode, and the user calls Connect which calls
// m.ExitIdleMode.
// //
// Either way, nothing to do here. // In any case, there is nothing to do here.
return nil return nil
} }
if err := m.enforcer.ExitIdleMode(); err != nil { if err := m.enforcer.ExitIdleMode(); err != nil {
return fmt.Errorf("channel failed to exit idle mode: %v", err) return fmt.Errorf("failed to exit idle mode: %w", err)
} }
// Undo the idle entry process. This also respects any new RPC attempts. // Undo the idle entry process. This also respects any new RPC attempts.
@ -267,12 +242,12 @@ func (m *manager) exitIdleMode() error {
m.actuallyIdle = false m.actuallyIdle = false
// Start a new timer to fire after the configured idle timeout. // Start a new timer to fire after the configured idle timeout.
m.timer = timeAfterFunc(time.Duration(m.timeout), m.handleIdleTimeout) m.resetIdleTimerLocked(m.timeout)
return nil return nil
} }
// OnCallEnd is invoked at the end of every RPC. // OnCallEnd is invoked at the end of every RPC.
func (m *manager) OnCallEnd() { func (m *Manager) OnCallEnd() {
if m.isClosed() { if m.isClosed() {
return return
} }
@ -287,15 +262,17 @@ func (m *manager) OnCallEnd() {
atomic.AddInt32(&m.activeCallsCount, -1) atomic.AddInt32(&m.activeCallsCount, -1)
} }
func (m *manager) isClosed() bool { func (m *Manager) isClosed() bool {
return atomic.LoadInt32(&m.closed) == 1 return atomic.LoadInt32(&m.closed) == 1
} }
func (m *manager) Close() { func (m *Manager) Close() {
atomic.StoreInt32(&m.closed, 1) atomic.StoreInt32(&m.closed, 1)
m.idleMu.Lock() m.idleMu.Lock()
m.timer.Stop() if m.timer != nil {
m.timer = nil m.timer.Stop()
m.timer = nil
}
m.idleMu.Unlock() m.idleMu.Unlock()
} }

View File

@ -73,6 +73,11 @@ var (
// xDS-enabled server invokes this method on a grpc.Server when a particular // xDS-enabled server invokes this method on a grpc.Server when a particular
// listener moves to "not-serving" mode. // listener moves to "not-serving" mode.
DrainServerTransports any // func(*grpc.Server, string) DrainServerTransports any // func(*grpc.Server, string)
// IsRegisteredMethod returns whether the passed in method is registered as
// a method on the server.
IsRegisteredMethod any // func(*grpc.Server, string) bool
// ServerFromContext returns the server from the context.
ServerFromContext any // func(context.Context) *grpc.Server
// AddGlobalServerOptions adds an array of ServerOption that will be // AddGlobalServerOptions adds an array of ServerOption that will be
// effective globally for newly created servers. The priority will be: 1. // effective globally for newly created servers. The priority will be: 1.
// user-provided; 2. this method; 3. default values. // user-provided; 2. this method; 3. default values.
@ -177,10 +182,12 @@ var (
GRPCResolverSchemeExtraMetadata string = "xds" GRPCResolverSchemeExtraMetadata string = "xds"
// EnterIdleModeForTesting gets the ClientConn to enter IDLE mode. // EnterIdleModeForTesting gets the ClientConn to enter IDLE mode.
EnterIdleModeForTesting any // func(*grpc.ClientConn) error EnterIdleModeForTesting any // func(*grpc.ClientConn)
// ExitIdleModeForTesting gets the ClientConn to exit IDLE mode. // ExitIdleModeForTesting gets the ClientConn to exit IDLE mode.
ExitIdleModeForTesting any // func(*grpc.ClientConn) error ExitIdleModeForTesting any // func(*grpc.ClientConn) error
ChannelzTurnOffForTesting func()
) )
// HealthChecker defines the signature of the client-side LB channel health checking function. // HealthChecker defines the signature of the client-side LB channel health checking function.

View File

@ -23,7 +23,6 @@ package dns
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"net" "net"
"os" "os"
@ -37,6 +36,7 @@ import (
"google.golang.org/grpc/internal/backoff" "google.golang.org/grpc/internal/backoff"
"google.golang.org/grpc/internal/envconfig" "google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/grpcrand" "google.golang.org/grpc/internal/grpcrand"
"google.golang.org/grpc/internal/resolver/dns/internal"
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
"google.golang.org/grpc/serviceconfig" "google.golang.org/grpc/serviceconfig"
) )
@ -47,15 +47,11 @@ var EnableSRVLookups = false
var logger = grpclog.Component("dns") var logger = grpclog.Component("dns")
// Globals to stub out in tests. TODO: Perhaps these two can be combined into a
// single variable for testing the resolver?
var (
newTimer = time.NewTimer
newTimerDNSResRate = time.NewTimer
)
func init() { func init() {
resolver.Register(NewBuilder()) resolver.Register(NewBuilder())
internal.TimeAfterFunc = time.After
internal.NewNetResolver = newNetResolver
internal.AddressDialer = addressDialer
} }
const ( const (
@ -70,23 +66,6 @@ const (
txtAttribute = "grpc_config=" txtAttribute = "grpc_config="
) )
var (
errMissingAddr = errors.New("dns resolver: missing address")
// Addresses ending with a colon that is supposed to be the separator
// between host and port is not allowed. E.g. "::" is a valid address as
// it is an IPv6 address (host only) and "[::]:" is invalid as it ends with
// a colon as the host and port separator
errEndsWithColon = errors.New("dns resolver: missing port after port-separator colon")
)
var (
defaultResolver netResolver = net.DefaultResolver
// To prevent excessive re-resolution, we enforce a rate limit on DNS
// resolution requests.
minDNSResRate = 30 * time.Second
)
var addressDialer = func(address string) func(context.Context, string, string) (net.Conn, error) { var addressDialer = func(address string) func(context.Context, string, string) (net.Conn, error) {
return func(ctx context.Context, network, _ string) (net.Conn, error) { return func(ctx context.Context, network, _ string) (net.Conn, error) {
var dialer net.Dialer var dialer net.Dialer
@ -94,7 +73,11 @@ var addressDialer = func(address string) func(context.Context, string, string) (
} }
} }
var newNetResolver = func(authority string) (netResolver, error) { var newNetResolver = func(authority string) (internal.NetResolver, error) {
if authority == "" {
return net.DefaultResolver, nil
}
host, port, err := parseTarget(authority, defaultDNSSvrPort) host, port, err := parseTarget(authority, defaultDNSSvrPort)
if err != nil { if err != nil {
return nil, err return nil, err
@ -104,7 +87,7 @@ var newNetResolver = func(authority string) (netResolver, error) {
return &net.Resolver{ return &net.Resolver{
PreferGo: true, PreferGo: true,
Dial: addressDialer(authorityWithPort), Dial: internal.AddressDialer(authorityWithPort),
}, nil }, nil
} }
@ -142,13 +125,9 @@ func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts
disableServiceConfig: opts.DisableServiceConfig, disableServiceConfig: opts.DisableServiceConfig,
} }
if target.URL.Host == "" { d.resolver, err = internal.NewNetResolver(target.URL.Host)
d.resolver = defaultResolver if err != nil {
} else { return nil, err
d.resolver, err = newNetResolver(target.URL.Host)
if err != nil {
return nil, err
}
} }
d.wg.Add(1) d.wg.Add(1)
@ -161,12 +140,6 @@ func (b *dnsBuilder) Scheme() string {
return "dns" return "dns"
} }
type netResolver interface {
LookupHost(ctx context.Context, host string) (addrs []string, err error)
LookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*net.SRV, err error)
LookupTXT(ctx context.Context, name string) (txts []string, err error)
}
// deadResolver is a resolver that does nothing. // deadResolver is a resolver that does nothing.
type deadResolver struct{} type deadResolver struct{}
@ -178,7 +151,7 @@ func (deadResolver) Close() {}
type dnsResolver struct { type dnsResolver struct {
host string host string
port string port string
resolver netResolver resolver internal.NetResolver
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
cc resolver.ClientConn cc resolver.ClientConn
@ -223,29 +196,27 @@ func (d *dnsResolver) watcher() {
err = d.cc.UpdateState(*state) err = d.cc.UpdateState(*state)
} }
var timer *time.Timer var waitTime time.Duration
if err == nil { if err == nil {
// Success resolving, wait for the next ResolveNow. However, also wait 30 // Success resolving, wait for the next ResolveNow. However, also wait 30
// seconds at the very least to prevent constantly re-resolving. // seconds at the very least to prevent constantly re-resolving.
backoffIndex = 1 backoffIndex = 1
timer = newTimerDNSResRate(minDNSResRate) waitTime = internal.MinResolutionRate
select { select {
case <-d.ctx.Done(): case <-d.ctx.Done():
timer.Stop()
return return
case <-d.rn: case <-d.rn:
} }
} else { } else {
// Poll on an error found in DNS Resolver or an error received from // Poll on an error found in DNS Resolver or an error received from
// ClientConn. // ClientConn.
timer = newTimer(backoff.DefaultExponential.Backoff(backoffIndex)) waitTime = backoff.DefaultExponential.Backoff(backoffIndex)
backoffIndex++ backoffIndex++
} }
select { select {
case <-d.ctx.Done(): case <-d.ctx.Done():
timer.Stop()
return return
case <-timer.C: case <-internal.TimeAfterFunc(waitTime):
} }
} }
} }
@ -387,7 +358,7 @@ func formatIP(addr string) (addrIP string, ok bool) {
// target: ":80" defaultPort: "443" returns host: "localhost", port: "80" // target: ":80" defaultPort: "443" returns host: "localhost", port: "80"
func parseTarget(target, defaultPort string) (host, port string, err error) { func parseTarget(target, defaultPort string) (host, port string, err error) {
if target == "" { if target == "" {
return "", "", errMissingAddr return "", "", internal.ErrMissingAddr
} }
if ip := net.ParseIP(target); ip != nil { if ip := net.ParseIP(target); ip != nil {
// target is an IPv4 or IPv6(without brackets) address // target is an IPv4 or IPv6(without brackets) address
@ -397,7 +368,7 @@ func parseTarget(target, defaultPort string) (host, port string, err error) {
if port == "" { if port == "" {
// If the port field is empty (target ends with colon), e.g. "[::1]:", // If the port field is empty (target ends with colon), e.g. "[::1]:",
// this is an error. // this is an error.
return "", "", errEndsWithColon return "", "", internal.ErrEndsWithColon
} }
// target has port, i.e ipv4-host:port, [ipv6-host]:port, host-name:port // target has port, i.e ipv4-host:port, [ipv6-host]:port, host-name:port
if host == "" { if host == "" {

View File

@ -0,0 +1,70 @@
/*
*
* Copyright 2023 gRPC 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 internal contains functionality internal to the dns resolver package.
package internal
import (
"context"
"errors"
"net"
"time"
)
// NetResolver groups the methods on net.Resolver that are used by the DNS
// resolver implementation. This allows the default net.Resolver instance to be
// overidden from tests.
type NetResolver interface {
LookupHost(ctx context.Context, host string) (addrs []string, err error)
LookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*net.SRV, err error)
LookupTXT(ctx context.Context, name string) (txts []string, err error)
}
var (
// ErrMissingAddr is the error returned when building a DNS resolver when
// the provided target name is empty.
ErrMissingAddr = errors.New("dns resolver: missing address")
// ErrEndsWithColon is the error returned when building a DNS resolver when
// the provided target name ends with a colon that is supposed to be the
// separator between host and port. E.g. "::" is a valid address as it is
// an IPv6 address (host only) and "[::]:" is invalid as it ends with a
// colon as the host and port separator
ErrEndsWithColon = errors.New("dns resolver: missing port after port-separator colon")
)
// The following vars are overridden from tests.
var (
// MinResolutionRate is the minimum rate at which re-resolutions are
// allowed. This helps to prevent excessive re-resolution.
MinResolutionRate = 30 * time.Second
// TimeAfterFunc is used by the DNS resolver to wait for the given duration
// to elapse. In non-test code, this is implemented by time.After. In test
// code, this can be used to control the amount of time the resolver is
// blocked waiting for the duration to elapse.
TimeAfterFunc func(time.Duration) <-chan time.Time
// NewNetResolver returns the net.Resolver instance for the given target.
NewNetResolver func(string) (NetResolver, error)
// AddressDialer is the dialer used to dial the DNS server. It accepts the
// Host portion of the URL corresponding to the user's dial target and
// returns a dial function.
AddressDialer func(address string) func(context.Context, string, string) (net.Conn, error)
)

View File

@ -0,0 +1,29 @@
//go:build !unix
/*
* Copyright 2023 gRPC 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 internal
import (
"net"
)
// NetDialerWithTCPKeepalive returns a vanilla net.Dialer on non-unix platforms.
func NetDialerWithTCPKeepalive() *net.Dialer {
return &net.Dialer{}
}

View File

@ -0,0 +1,54 @@
//go:build unix
/*
* Copyright 2023 gRPC 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 internal
import (
"net"
"syscall"
"time"
"golang.org/x/sys/unix"
)
// NetDialerWithTCPKeepalive returns a net.Dialer that enables TCP keepalives on
// the underlying connection with OS default values for keepalive parameters.
//
// TODO: Once https://github.com/golang/go/issues/62254 lands, and the
// appropriate Go version becomes less than our least supported Go version, we
// should look into using the new API to make things more straightforward.
func NetDialerWithTCPKeepalive() *net.Dialer {
return &net.Dialer{
// Setting a negative value here prevents the Go stdlib from overriding
// the values of TCP keepalive time and interval. It also prevents the
// Go stdlib from enabling TCP keepalives by default.
KeepAlive: time.Duration(-1),
// This method is called after the underlying network socket is created,
// but before dialing the socket (or calling its connect() method). The
// combination of unconditionally enabling TCP keepalives here, and
// disabling the overriding of TCP keepalive parameters by setting the
// KeepAlive field to a negative value above, results in OS defaults for
// the TCP keealive interval and time parameters.
Control: func(_, _ string, c syscall.RawConn) error {
return c.Control(func(fd uintptr) {
unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 1)
})
},
}
}

View File

@ -75,11 +75,25 @@ func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request, stats []s
return nil, errors.New(msg) return nil, errors.New(msg)
} }
var localAddr net.Addr
if la := r.Context().Value(http.LocalAddrContextKey); la != nil {
localAddr, _ = la.(net.Addr)
}
var authInfo credentials.AuthInfo
if r.TLS != nil {
authInfo = credentials.TLSInfo{State: *r.TLS, CommonAuthInfo: credentials.CommonAuthInfo{SecurityLevel: credentials.PrivacyAndIntegrity}}
}
p := peer.Peer{
Addr: strAddr(r.RemoteAddr),
LocalAddr: localAddr,
AuthInfo: authInfo,
}
st := &serverHandlerTransport{ st := &serverHandlerTransport{
rw: w, rw: w,
req: r, req: r,
closedCh: make(chan struct{}), closedCh: make(chan struct{}),
writes: make(chan func()), writes: make(chan func()),
peer: p,
contentType: contentType, contentType: contentType,
contentSubtype: contentSubtype, contentSubtype: contentSubtype,
stats: stats, stats: stats,
@ -134,6 +148,8 @@ type serverHandlerTransport struct {
headerMD metadata.MD headerMD metadata.MD
peer peer.Peer
closeOnce sync.Once closeOnce sync.Once
closedCh chan struct{} // closed on Close closedCh chan struct{} // closed on Close
@ -165,7 +181,13 @@ func (ht *serverHandlerTransport) Close(err error) {
}) })
} }
func (ht *serverHandlerTransport) RemoteAddr() net.Addr { return strAddr(ht.req.RemoteAddr) } func (ht *serverHandlerTransport) Peer() *peer.Peer {
return &peer.Peer{
Addr: ht.peer.Addr,
LocalAddr: ht.peer.LocalAddr,
AuthInfo: ht.peer.AuthInfo,
}
}
// strAddr is a net.Addr backed by either a TCP "ip:port" string, or // strAddr is a net.Addr backed by either a TCP "ip:port" string, or
// the empty string if unknown. // the empty string if unknown.
@ -347,10 +369,8 @@ func (ht *serverHandlerTransport) WriteHeader(s *Stream, md metadata.MD) error {
return err return err
} }
func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream)) { func (ht *serverHandlerTransport) HandleStreams(ctx context.Context, startStream func(*Stream)) {
// With this transport type there will be exactly 1 stream: this HTTP request. // With this transport type there will be exactly 1 stream: this HTTP request.
ctx := ht.req.Context()
var cancel context.CancelFunc var cancel context.CancelFunc
if ht.timeoutSet { if ht.timeoutSet {
ctx, cancel = context.WithTimeout(ctx, ht.timeout) ctx, cancel = context.WithTimeout(ctx, ht.timeout)
@ -370,34 +390,19 @@ func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream)) {
ht.Close(errors.New("request is done processing")) ht.Close(errors.New("request is done processing"))
}() }()
req := ht.req
s := &Stream{
id: 0, // irrelevant
requestRead: func(int) {},
cancel: cancel,
buf: newRecvBuffer(),
st: ht,
method: req.URL.Path,
recvCompress: req.Header.Get("grpc-encoding"),
contentSubtype: ht.contentSubtype,
}
pr := &peer.Peer{
Addr: ht.RemoteAddr(),
}
if req.TLS != nil {
pr.AuthInfo = credentials.TLSInfo{State: *req.TLS, CommonAuthInfo: credentials.CommonAuthInfo{SecurityLevel: credentials.PrivacyAndIntegrity}}
}
ctx = metadata.NewIncomingContext(ctx, ht.headerMD) ctx = metadata.NewIncomingContext(ctx, ht.headerMD)
s.ctx = peer.NewContext(ctx, pr) req := ht.req
for _, sh := range ht.stats { s := &Stream{
s.ctx = sh.TagRPC(s.ctx, &stats.RPCTagInfo{FullMethodName: s.method}) id: 0, // irrelevant
inHeader := &stats.InHeader{ ctx: ctx,
FullMethod: s.method, requestRead: func(int) {},
RemoteAddr: ht.RemoteAddr(), cancel: cancel,
Compression: s.recvCompress, buf: newRecvBuffer(),
} st: ht,
sh.HandleRPC(s.ctx, inHeader) method: req.URL.Path,
recvCompress: req.Header.Get("grpc-encoding"),
contentSubtype: ht.contentSubtype,
headerWireLength: 0, // won't have access to header wire length until golang/go#18997.
} }
s.trReader = &transportReader{ s.trReader = &transportReader{
reader: &recvBufferReader{ctx: s.ctx, ctxDone: s.ctx.Done(), recv: s.buf, freeBuffer: func(*bytes.Buffer) {}}, reader: &recvBufferReader{ctx: s.ctx, ctxDone: s.ctx.Done(), recv: s.buf, freeBuffer: func(*bytes.Buffer) {}},

View File

@ -36,6 +36,7 @@ import (
"golang.org/x/net/http2/hpack" "golang.org/x/net/http2/hpack"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/internal/channelz"
icredentials "google.golang.org/grpc/internal/credentials" icredentials "google.golang.org/grpc/internal/credentials"
"google.golang.org/grpc/internal/grpclog" "google.golang.org/grpc/internal/grpclog"
@ -43,7 +44,7 @@ import (
"google.golang.org/grpc/internal/grpcutil" "google.golang.org/grpc/internal/grpcutil"
imetadata "google.golang.org/grpc/internal/metadata" imetadata "google.golang.org/grpc/internal/metadata"
istatus "google.golang.org/grpc/internal/status" istatus "google.golang.org/grpc/internal/status"
"google.golang.org/grpc/internal/syscall" isyscall "google.golang.org/grpc/internal/syscall"
"google.golang.org/grpc/internal/transport/networktype" "google.golang.org/grpc/internal/transport/networktype"
"google.golang.org/grpc/keepalive" "google.golang.org/grpc/keepalive"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
@ -176,7 +177,7 @@ func dial(ctx context.Context, fn func(context.Context, string) (net.Conn, error
if networkType == "tcp" && useProxy { if networkType == "tcp" && useProxy {
return proxyDial(ctx, address, grpcUA) return proxyDial(ctx, address, grpcUA)
} }
return (&net.Dialer{}).DialContext(ctx, networkType, address) return internal.NetDialerWithTCPKeepalive().DialContext(ctx, networkType, address)
} }
func isTemporary(err error) bool { func isTemporary(err error) bool {
@ -262,7 +263,7 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
} }
keepaliveEnabled := false keepaliveEnabled := false
if kp.Time != infinity { if kp.Time != infinity {
if err = syscall.SetTCPUserTimeout(conn, kp.Timeout); err != nil { if err = isyscall.SetTCPUserTimeout(conn, kp.Timeout); err != nil {
return nil, connectionErrorf(false, err, "transport: failed to set TCP_USER_TIMEOUT: %v", err) return nil, connectionErrorf(false, err, "transport: failed to set TCP_USER_TIMEOUT: %v", err)
} }
keepaliveEnabled = true keepaliveEnabled = true
@ -493,8 +494,9 @@ func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream {
func (t *http2Client) getPeer() *peer.Peer { func (t *http2Client) getPeer() *peer.Peer {
return &peer.Peer{ return &peer.Peer{
Addr: t.remoteAddr, Addr: t.remoteAddr,
AuthInfo: t.authInfo, // Can be nil AuthInfo: t.authInfo, // Can be nil
LocalAddr: t.localAddr,
} }
} }

View File

@ -68,18 +68,15 @@ var serverConnectionCounter uint64
// http2Server implements the ServerTransport interface with HTTP2. // http2Server implements the ServerTransport interface with HTTP2.
type http2Server struct { type http2Server struct {
lastRead int64 // Keep this field 64-bit aligned. Accessed atomically. lastRead int64 // Keep this field 64-bit aligned. Accessed atomically.
ctx context.Context done chan struct{}
done chan struct{} conn net.Conn
conn net.Conn loopy *loopyWriter
loopy *loopyWriter readerDone chan struct{} // sync point to enable testing.
readerDone chan struct{} // sync point to enable testing. loopyWriterDone chan struct{}
writerDone chan struct{} // sync point to enable testing. peer peer.Peer
remoteAddr net.Addr inTapHandle tap.ServerInHandle
localAddr net.Addr framer *framer
authInfo credentials.AuthInfo // auth info about the connection
inTapHandle tap.ServerInHandle
framer *framer
// The max number of concurrent streams. // The max number of concurrent streams.
maxStreams uint32 maxStreams uint32
// controlBuf delivers all the control related tasks (e.g., window // controlBuf delivers all the control related tasks (e.g., window
@ -243,16 +240,18 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
} }
done := make(chan struct{}) done := make(chan struct{})
peer := peer.Peer{
Addr: conn.RemoteAddr(),
LocalAddr: conn.LocalAddr(),
AuthInfo: authInfo,
}
t := &http2Server{ t := &http2Server{
ctx: setConnection(context.Background(), rawConn),
done: done, done: done,
conn: conn, conn: conn,
remoteAddr: conn.RemoteAddr(), peer: peer,
localAddr: conn.LocalAddr(),
authInfo: authInfo,
framer: framer, framer: framer,
readerDone: make(chan struct{}), readerDone: make(chan struct{}),
writerDone: make(chan struct{}), loopyWriterDone: make(chan struct{}),
maxStreams: config.MaxStreams, maxStreams: config.MaxStreams,
inTapHandle: config.InTapHandle, inTapHandle: config.InTapHandle,
fc: &trInFlow{limit: uint32(icwz)}, fc: &trInFlow{limit: uint32(icwz)},
@ -267,8 +266,6 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
bufferPool: newBufferPool(), bufferPool: newBufferPool(),
} }
t.logger = prefixLoggerForServerTransport(t) t.logger = prefixLoggerForServerTransport(t)
// Add peer information to the http2server context.
t.ctx = peer.NewContext(t.ctx, t.getPeer())
t.controlBuf = newControlBuffer(t.done) t.controlBuf = newControlBuffer(t.done)
if dynamicWindow { if dynamicWindow {
@ -277,15 +274,7 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
updateFlowControl: t.updateFlowControl, updateFlowControl: t.updateFlowControl,
} }
} }
for _, sh := range t.stats { t.channelzID, err = channelz.RegisterNormalSocket(t, config.ChannelzParentID, fmt.Sprintf("%s -> %s", t.peer.Addr, t.peer.LocalAddr))
t.ctx = sh.TagConn(t.ctx, &stats.ConnTagInfo{
RemoteAddr: t.remoteAddr,
LocalAddr: t.localAddr,
})
connBegin := &stats.ConnBegin{}
sh.HandleConn(t.ctx, connBegin)
}
t.channelzID, err = channelz.RegisterNormalSocket(t, config.ChannelzParentID, fmt.Sprintf("%s -> %s", t.remoteAddr, t.localAddr))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -334,7 +323,7 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
t.loopy = newLoopyWriter(serverSide, t.framer, t.controlBuf, t.bdpEst, t.conn, t.logger) t.loopy = newLoopyWriter(serverSide, t.framer, t.controlBuf, t.bdpEst, t.conn, t.logger)
t.loopy.ssGoAwayHandler = t.outgoingGoAwayHandler t.loopy.ssGoAwayHandler = t.outgoingGoAwayHandler
t.loopy.run() t.loopy.run()
close(t.writerDone) close(t.loopyWriterDone)
}() }()
go t.keepalive() go t.keepalive()
return t, nil return t, nil
@ -342,7 +331,7 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
// operateHeaders takes action on the decoded headers. Returns an error if fatal // operateHeaders takes action on the decoded headers. Returns an error if fatal
// error encountered and transport needs to close, otherwise returns nil. // error encountered and transport needs to close, otherwise returns nil.
func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream)) error { func (t *http2Server) operateHeaders(ctx context.Context, frame *http2.MetaHeadersFrame, handle func(*Stream)) error {
// Acquire max stream ID lock for entire duration // Acquire max stream ID lock for entire duration
t.maxStreamMu.Lock() t.maxStreamMu.Lock()
defer t.maxStreamMu.Unlock() defer t.maxStreamMu.Unlock()
@ -369,10 +358,11 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
buf := newRecvBuffer() buf := newRecvBuffer()
s := &Stream{ s := &Stream{
id: streamID, id: streamID,
st: t, st: t,
buf: buf, buf: buf,
fc: &inFlow{limit: uint32(t.initialWindowSize)}, fc: &inFlow{limit: uint32(t.initialWindowSize)},
headerWireLength: int(frame.Header().Length),
} }
var ( var (
// if false, content-type was missing or invalid // if false, content-type was missing or invalid
@ -511,9 +501,9 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
s.state = streamReadDone s.state = streamReadDone
} }
if timeoutSet { if timeoutSet {
s.ctx, s.cancel = context.WithTimeout(t.ctx, timeout) s.ctx, s.cancel = context.WithTimeout(ctx, timeout)
} else { } else {
s.ctx, s.cancel = context.WithCancel(t.ctx) s.ctx, s.cancel = context.WithCancel(ctx)
} }
// Attach the received metadata to the context. // Attach the received metadata to the context.
@ -592,18 +582,6 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
s.requestRead = func(n int) { s.requestRead = func(n int) {
t.adjustWindow(s, uint32(n)) t.adjustWindow(s, uint32(n))
} }
for _, sh := range t.stats {
s.ctx = sh.TagRPC(s.ctx, &stats.RPCTagInfo{FullMethodName: s.method})
inHeader := &stats.InHeader{
FullMethod: s.method,
RemoteAddr: t.remoteAddr,
LocalAddr: t.localAddr,
Compression: s.recvCompress,
WireLength: int(frame.Header().Length),
Header: mdata.Copy(),
}
sh.HandleRPC(s.ctx, inHeader)
}
s.ctxDone = s.ctx.Done() s.ctxDone = s.ctx.Done()
s.wq = newWriteQuota(defaultWriteQuota, s.ctxDone) s.wq = newWriteQuota(defaultWriteQuota, s.ctxDone)
s.trReader = &transportReader{ s.trReader = &transportReader{
@ -629,8 +607,11 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
// HandleStreams receives incoming streams using the given handler. This is // HandleStreams receives incoming streams using the given handler. This is
// typically run in a separate goroutine. // typically run in a separate goroutine.
// traceCtx attaches trace to ctx and returns the new context. // traceCtx attaches trace to ctx and returns the new context.
func (t *http2Server) HandleStreams(handle func(*Stream)) { func (t *http2Server) HandleStreams(ctx context.Context, handle func(*Stream)) {
defer close(t.readerDone) defer func() {
<-t.loopyWriterDone
close(t.readerDone)
}()
for { for {
t.controlBuf.throttle() t.controlBuf.throttle()
frame, err := t.framer.fr.ReadFrame() frame, err := t.framer.fr.ReadFrame()
@ -664,7 +645,7 @@ func (t *http2Server) HandleStreams(handle func(*Stream)) {
} }
switch frame := frame.(type) { switch frame := frame.(type) {
case *http2.MetaHeadersFrame: case *http2.MetaHeadersFrame:
if err := t.operateHeaders(frame, handle); err != nil { if err := t.operateHeaders(ctx, frame, handle); err != nil {
t.Close(err) t.Close(err)
break break
} }
@ -1242,10 +1223,6 @@ func (t *http2Server) Close(err error) {
for _, s := range streams { for _, s := range streams {
s.cancel() s.cancel()
} }
for _, sh := range t.stats {
connEnd := &stats.ConnEnd{}
sh.HandleConn(t.ctx, connEnd)
}
} }
// deleteStream deletes the stream s from transport's active streams. // deleteStream deletes the stream s from transport's active streams.
@ -1311,10 +1288,6 @@ func (t *http2Server) closeStream(s *Stream, rst bool, rstCode http2.ErrCode, eo
}) })
} }
func (t *http2Server) RemoteAddr() net.Addr {
return t.remoteAddr
}
func (t *http2Server) Drain(debugData string) { func (t *http2Server) Drain(debugData string) {
t.mu.Lock() t.mu.Lock()
defer t.mu.Unlock() defer t.mu.Unlock()
@ -1397,11 +1370,11 @@ func (t *http2Server) ChannelzMetric() *channelz.SocketInternalMetric {
LastMessageReceivedTimestamp: time.Unix(0, atomic.LoadInt64(&t.czData.lastMsgRecvTime)), LastMessageReceivedTimestamp: time.Unix(0, atomic.LoadInt64(&t.czData.lastMsgRecvTime)),
LocalFlowControlWindow: int64(t.fc.getSize()), LocalFlowControlWindow: int64(t.fc.getSize()),
SocketOptions: channelz.GetSocketOption(t.conn), SocketOptions: channelz.GetSocketOption(t.conn),
LocalAddr: t.localAddr, LocalAddr: t.peer.LocalAddr,
RemoteAddr: t.remoteAddr, RemoteAddr: t.peer.Addr,
// RemoteName : // RemoteName :
} }
if au, ok := t.authInfo.(credentials.ChannelzSecurityInfo); ok { if au, ok := t.peer.AuthInfo.(credentials.ChannelzSecurityInfo); ok {
s.Security = au.GetSecurityValue() s.Security = au.GetSecurityValue()
} }
s.RemoteFlowControlWindow = t.getOutFlowWindow() s.RemoteFlowControlWindow = t.getOutFlowWindow()
@ -1433,10 +1406,12 @@ func (t *http2Server) getOutFlowWindow() int64 {
} }
} }
func (t *http2Server) getPeer() *peer.Peer { // Peer returns the peer of the transport.
func (t *http2Server) Peer() *peer.Peer {
return &peer.Peer{ return &peer.Peer{
Addr: t.remoteAddr, Addr: t.peer.Addr,
AuthInfo: t.authInfo, // Can be nil LocalAddr: t.peer.LocalAddr,
AuthInfo: t.peer.AuthInfo, // Can be nil
} }
} }
@ -1461,6 +1436,6 @@ func GetConnection(ctx context.Context) net.Conn {
// SetConnection adds the connection to the context to be able to get // SetConnection adds the connection to the context to be able to get
// information about the destination ip and port for an incoming RPC. This also // information about the destination ip and port for an incoming RPC. This also
// allows any unary or streaming interceptors to see the connection. // allows any unary or streaming interceptors to see the connection.
func setConnection(ctx context.Context, conn net.Conn) context.Context { func SetConnection(ctx context.Context, conn net.Conn) context.Context {
return context.WithValue(ctx, connectionKey{}, conn) return context.WithValue(ctx, connectionKey{}, conn)
} }

View File

@ -28,6 +28,8 @@ import (
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
"net/url" "net/url"
"google.golang.org/grpc/internal"
) )
const proxyAuthHeaderKey = "Proxy-Authorization" const proxyAuthHeaderKey = "Proxy-Authorization"
@ -112,7 +114,7 @@ func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, backendAddr stri
// proxyDial dials, connecting to a proxy first if necessary. Checks if a proxy // proxyDial dials, connecting to a proxy first if necessary. Checks if a proxy
// is necessary, dials, does the HTTP CONNECT handshake, and returns the // is necessary, dials, does the HTTP CONNECT handshake, and returns the
// connection. // connection.
func proxyDial(ctx context.Context, addr string, grpcUA string) (conn net.Conn, err error) { func proxyDial(ctx context.Context, addr string, grpcUA string) (net.Conn, error) {
newAddr := addr newAddr := addr
proxyURL, err := mapAddress(addr) proxyURL, err := mapAddress(addr)
if err != nil { if err != nil {
@ -122,15 +124,15 @@ func proxyDial(ctx context.Context, addr string, grpcUA string) (conn net.Conn,
newAddr = proxyURL.Host newAddr = proxyURL.Host
} }
conn, err = (&net.Dialer{}).DialContext(ctx, "tcp", newAddr) conn, err := internal.NetDialerWithTCPKeepalive().DialContext(ctx, "tcp", newAddr)
if err != nil { if err != nil {
return return nil, err
} }
if proxyURL != nil { if proxyURL == nil {
// proxy is disabled if proxyURL is nil. // proxy is disabled if proxyURL is nil.
conn, err = doHTTPConnectHandshake(ctx, conn, addr, proxyURL, grpcUA) return conn, err
} }
return return doHTTPConnectHandshake(ctx, conn, addr, proxyURL, grpcUA)
} }
func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error { func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error {

View File

@ -37,6 +37,7 @@ import (
"google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/keepalive" "google.golang.org/grpc/keepalive"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
"google.golang.org/grpc/stats" "google.golang.org/grpc/stats"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
@ -265,7 +266,8 @@ type Stream struct {
// headerValid indicates whether a valid header was received. Only // headerValid indicates whether a valid header was received. Only
// meaningful after headerChan is closed (always call waitOnHeader() before // meaningful after headerChan is closed (always call waitOnHeader() before
// reading its value). Not valid on server side. // reading its value). Not valid on server side.
headerValid bool headerValid bool
headerWireLength int // Only set on server side.
// hdrMu protects header and trailer metadata on the server-side. // hdrMu protects header and trailer metadata on the server-side.
hdrMu sync.Mutex hdrMu sync.Mutex
@ -425,6 +427,12 @@ func (s *Stream) Context() context.Context {
return s.ctx return s.ctx
} }
// SetContext sets the context of the stream. This will be deleted once the
// stats handler callouts all move to gRPC layer.
func (s *Stream) SetContext(ctx context.Context) {
s.ctx = ctx
}
// Method returns the method for the stream. // Method returns the method for the stream.
func (s *Stream) Method() string { func (s *Stream) Method() string {
return s.method return s.method
@ -437,6 +445,12 @@ func (s *Stream) Status() *status.Status {
return s.status return s.status
} }
// HeaderWireLength returns the size of the headers of the stream as received
// from the wire. Valid only on the server.
func (s *Stream) HeaderWireLength() int {
return s.headerWireLength
}
// SetHeader sets the header metadata. This can be called multiple times. // SetHeader sets the header metadata. This can be called multiple times.
// Server side only. // Server side only.
// This should not be called in parallel to other data writes. // This should not be called in parallel to other data writes.
@ -698,7 +712,7 @@ type ClientTransport interface {
// Write methods for a given Stream will be called serially. // Write methods for a given Stream will be called serially.
type ServerTransport interface { type ServerTransport interface {
// HandleStreams receives incoming streams using the given handler. // HandleStreams receives incoming streams using the given handler.
HandleStreams(func(*Stream)) HandleStreams(context.Context, func(*Stream))
// WriteHeader sends the header metadata for the given stream. // WriteHeader sends the header metadata for the given stream.
// WriteHeader may not be called on all streams. // WriteHeader may not be called on all streams.
@ -717,8 +731,8 @@ type ServerTransport interface {
// handlers will be terminated asynchronously. // handlers will be terminated asynchronously.
Close(err error) Close(err error)
// RemoteAddr returns the remote network address. // Peer returns the peer of the server transport.
RemoteAddr() net.Addr Peer() *peer.Peer
// Drain notifies the client this ServerTransport stops accepting new RPCs. // Drain notifies the client this ServerTransport stops accepting new RPCs.
Drain(debugData string) Drain(debugData string)

View File

@ -153,14 +153,16 @@ func Join(mds ...MD) MD {
type mdIncomingKey struct{} type mdIncomingKey struct{}
type mdOutgoingKey struct{} type mdOutgoingKey struct{}
// NewIncomingContext creates a new context with incoming md attached. // NewIncomingContext creates a new context with incoming md attached. md must
// not be modified after calling this function.
func NewIncomingContext(ctx context.Context, md MD) context.Context { func NewIncomingContext(ctx context.Context, md MD) context.Context {
return context.WithValue(ctx, mdIncomingKey{}, md) return context.WithValue(ctx, mdIncomingKey{}, md)
} }
// NewOutgoingContext creates a new context with outgoing md attached. If used // NewOutgoingContext creates a new context with outgoing md attached. If used
// in conjunction with AppendToOutgoingContext, NewOutgoingContext will // in conjunction with AppendToOutgoingContext, NewOutgoingContext will
// overwrite any previously-appended metadata. // overwrite any previously-appended metadata. md must not be modified after
// calling this function.
func NewOutgoingContext(ctx context.Context, md MD) context.Context { func NewOutgoingContext(ctx context.Context, md MD) context.Context {
return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md}) return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md})
} }
@ -203,7 +205,8 @@ func FromIncomingContext(ctx context.Context) (MD, bool) {
} }
// ValueFromIncomingContext returns the metadata value corresponding to the metadata // ValueFromIncomingContext returns the metadata value corresponding to the metadata
// key from the incoming metadata if it exists. Key must be lower-case. // key from the incoming metadata if it exists. Keys are matched in a case insensitive
// manner.
// //
// # Experimental // # Experimental
// //
@ -219,17 +222,16 @@ func ValueFromIncomingContext(ctx context.Context, key string) []string {
return copyOf(v) return copyOf(v)
} }
for k, v := range md { for k, v := range md {
// We need to manually convert all keys to lower case, because MD is a // Case insenitive comparison: MD is a map, and there's no guarantee
// map, and there's no guarantee that the MD attached to the context is // that the MD attached to the context is created using our helper
// created using our helper functions. // functions.
if strings.ToLower(k) == key { if strings.EqualFold(k, key) {
return copyOf(v) return copyOf(v)
} }
} }
return nil return nil
} }
// the returned slice must not be modified in place
func copyOf(v []string) []string { func copyOf(v []string) []string {
vals := make([]string, len(v)) vals := make([]string, len(v))
copy(vals, v) copy(vals, v)

View File

@ -32,6 +32,8 @@ import (
type Peer struct { type Peer struct {
// Addr is the peer address. // Addr is the peer address.
Addr net.Addr Addr net.Addr
// LocalAddr is the local address.
LocalAddr net.Addr
// AuthInfo is the authentication information of the transport. // AuthInfo is the authentication information of the transport.
// It is nil if there is no transport security being used. // It is nil if there is no transport security being used.
AuthInfo credentials.AuthInfo AuthInfo credentials.AuthInfo

View File

@ -37,7 +37,6 @@ import (
type pickerWrapper struct { type pickerWrapper struct {
mu sync.Mutex mu sync.Mutex
done bool done bool
idle bool
blockingCh chan struct{} blockingCh chan struct{}
picker balancer.Picker picker balancer.Picker
statsHandlers []stats.Handler // to record blocking picker calls statsHandlers []stats.Handler // to record blocking picker calls
@ -53,11 +52,7 @@ func newPickerWrapper(statsHandlers []stats.Handler) *pickerWrapper {
// updatePicker is called by UpdateBalancerState. It unblocks all blocked pick. // updatePicker is called by UpdateBalancerState. It unblocks all blocked pick.
func (pw *pickerWrapper) updatePicker(p balancer.Picker) { func (pw *pickerWrapper) updatePicker(p balancer.Picker) {
pw.mu.Lock() pw.mu.Lock()
if pw.done || pw.idle { if pw.done {
// There is a small window where a picker update from the LB policy can
// race with the channel going to idle mode. If the picker is idle here,
// it is because the channel asked it to do so, and therefore it is sage
// to ignore the update from the LB policy.
pw.mu.Unlock() pw.mu.Unlock()
return return
} }
@ -210,23 +205,15 @@ func (pw *pickerWrapper) close() {
close(pw.blockingCh) close(pw.blockingCh)
} }
func (pw *pickerWrapper) enterIdleMode() { // reset clears the pickerWrapper and prepares it for being used again when idle
pw.mu.Lock() // mode is exited.
defer pw.mu.Unlock() func (pw *pickerWrapper) reset() {
if pw.done {
return
}
pw.idle = true
}
func (pw *pickerWrapper) exitIdleMode() {
pw.mu.Lock() pw.mu.Lock()
defer pw.mu.Unlock() defer pw.mu.Unlock()
if pw.done { if pw.done {
return return
} }
pw.blockingCh = make(chan struct{}) pw.blockingCh = make(chan struct{})
pw.idle = false
} }
// dropError is a wrapper error that indicates the LB policy wishes to drop the // dropError is a wrapper error that indicates the LB policy wishes to drop the

View File

@ -25,7 +25,6 @@ import (
"google.golang.org/grpc/balancer" "google.golang.org/grpc/balancer"
"google.golang.org/grpc/connectivity" "google.golang.org/grpc/connectivity"
"google.golang.org/grpc/internal/envconfig"
internalgrpclog "google.golang.org/grpc/internal/grpclog" internalgrpclog "google.golang.org/grpc/internal/grpclog"
"google.golang.org/grpc/internal/grpcrand" "google.golang.org/grpc/internal/grpcrand"
"google.golang.org/grpc/internal/pretty" "google.golang.org/grpc/internal/pretty"
@ -65,19 +64,6 @@ type pfConfig struct {
} }
func (*pickfirstBuilder) ParseConfig(js json.RawMessage) (serviceconfig.LoadBalancingConfig, error) { func (*pickfirstBuilder) ParseConfig(js json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
if !envconfig.PickFirstLBConfig {
// Prior to supporting loadbalancing configuration, the pick_first LB
// policy did not implement the balancer.ConfigParser interface. This
// meant that if a non-empty configuration was passed to it, the service
// config unmarshaling code would throw a warning log, but would
// continue using the pick_first LB policy. The code below ensures the
// same behavior is retained if the env var is not set.
if string(js) != "{}" {
logger.Warningf("Ignoring non-empty balancer configuration %q for the pick_first LB policy", string(js))
}
return nil, nil
}
var cfg pfConfig var cfg pfConfig
if err := json.Unmarshal(js, &cfg); err != nil { if err := json.Unmarshal(js, &cfg); err != nil {
return nil, fmt.Errorf("pickfirst: unable to unmarshal LB policy config: %s, error: %v", string(js), err) return nil, fmt.Errorf("pickfirst: unable to unmarshal LB policy config: %s, error: %v", string(js), err)

View File

@ -0,0 +1,36 @@
/*
*
* Copyright 2018 gRPC 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 dns implements a dns resolver to be installed as the default resolver
// in grpc.
//
// Deprecated: this package is imported by grpc and should not need to be
// imported directly by users.
package dns
import (
"google.golang.org/grpc/internal/resolver/dns"
"google.golang.org/grpc/resolver"
)
// NewBuilder creates a dnsBuilder which is used to factory DNS resolvers.
//
// Deprecated: import grpc and use resolver.Get("dns") instead.
func NewBuilder() resolver.Builder {
return dns.NewBuilder()
}

View File

@ -136,3 +136,116 @@ func (a *AddressMap) Values() []any {
} }
return ret return ret
} }
type endpointNode struct {
addrs map[string]struct{}
}
// Equal returns whether the unordered set of addrs are the same between the
// endpoint nodes.
func (en *endpointNode) Equal(en2 *endpointNode) bool {
if len(en.addrs) != len(en2.addrs) {
return false
}
for addr := range en.addrs {
if _, ok := en2.addrs[addr]; !ok {
return false
}
}
return true
}
func toEndpointNode(endpoint Endpoint) endpointNode {
en := make(map[string]struct{})
for _, addr := range endpoint.Addresses {
en[addr.Addr] = struct{}{}
}
return endpointNode{
addrs: en,
}
}
// EndpointMap is a map of endpoints to arbitrary values keyed on only the
// unordered set of address strings within an endpoint. This map is not thread
// safe, thus it is unsafe to access concurrently. Must be created via
// NewEndpointMap; do not construct directly.
type EndpointMap struct {
endpoints map[*endpointNode]any
}
// NewEndpointMap creates a new EndpointMap.
func NewEndpointMap() *EndpointMap {
return &EndpointMap{
endpoints: make(map[*endpointNode]any),
}
}
// Get returns the value for the address in the map, if present.
func (em *EndpointMap) Get(e Endpoint) (value any, ok bool) {
en := toEndpointNode(e)
if endpoint := em.find(en); endpoint != nil {
return em.endpoints[endpoint], true
}
return nil, false
}
// Set updates or adds the value to the address in the map.
func (em *EndpointMap) Set(e Endpoint, value any) {
en := toEndpointNode(e)
if endpoint := em.find(en); endpoint != nil {
em.endpoints[endpoint] = value
return
}
em.endpoints[&en] = value
}
// Len returns the number of entries in the map.
func (em *EndpointMap) Len() int {
return len(em.endpoints)
}
// Keys returns a slice of all current map keys, as endpoints specifying the
// addresses present in the endpoint keys, in which uniqueness is determined by
// the unordered set of addresses. Thus, endpoint information returned is not
// the full endpoint data (drops duplicated addresses and attributes) but can be
// used for EndpointMap accesses.
func (em *EndpointMap) Keys() []Endpoint {
ret := make([]Endpoint, 0, len(em.endpoints))
for en := range em.endpoints {
var endpoint Endpoint
for addr := range en.addrs {
endpoint.Addresses = append(endpoint.Addresses, Address{Addr: addr})
}
ret = append(ret, endpoint)
}
return ret
}
// Values returns a slice of all current map values.
func (em *EndpointMap) Values() []any {
ret := make([]any, 0, len(em.endpoints))
for _, val := range em.endpoints {
ret = append(ret, val)
}
return ret
}
// find returns a pointer to the endpoint node in em if the endpoint node is
// already present. If not found, nil is returned. The comparisons are done on
// the unordered set of addresses within an endpoint.
func (em EndpointMap) find(e endpointNode) *endpointNode {
for endpoint := range em.endpoints {
if e.Equal(endpoint) {
return endpoint
}
}
return nil
}
// Delete removes the specified endpoint from the map.
func (em *EndpointMap) Delete(e Endpoint) {
en := toEndpointNode(e)
if entry := em.find(en); entry != nil {
delete(em.endpoints, entry)
}
}

View File

@ -240,11 +240,6 @@ type ClientConn interface {
// //
// Deprecated: Use UpdateState instead. // Deprecated: Use UpdateState instead.
NewAddress(addresses []Address) NewAddress(addresses []Address)
// NewServiceConfig is called by resolver to notify ClientConn a new
// service config. The service config should be provided as a json string.
//
// Deprecated: Use UpdateState instead.
NewServiceConfig(serviceConfig string)
// ParseServiceConfig parses the provided service config and returns an // ParseServiceConfig parses the provided service config and returns an
// object that provides the parsed config. // object that provides the parsed config.
ParseServiceConfig(serviceConfigJSON string) *serviceconfig.ParseResult ParseServiceConfig(serviceConfigJSON string) *serviceconfig.ParseResult
@ -286,6 +281,11 @@ func (t Target) Endpoint() string {
return strings.TrimPrefix(endpoint, "/") return strings.TrimPrefix(endpoint, "/")
} }
// String returns a string representation of Target.
func (t Target) String() string {
return t.URL.String()
}
// Builder creates a resolver that will be used to watch name resolution updates. // Builder creates a resolver that will be used to watch name resolution updates.
type Builder interface { type Builder interface {
// Build creates a new resolver for the given target. // Build creates a new resolver for the given target.

View File

@ -1,247 +0,0 @@
/*
*
* Copyright 2017 gRPC 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 grpc
import (
"context"
"strings"
"sync"
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/internal/grpcsync"
"google.golang.org/grpc/internal/pretty"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/serviceconfig"
)
// resolverStateUpdater wraps the single method used by ccResolverWrapper to
// report a state update from the actual resolver implementation.
type resolverStateUpdater interface {
updateResolverState(s resolver.State, err error) error
}
// ccResolverWrapper is a wrapper on top of cc for resolvers.
// It implements resolver.ClientConn interface.
type ccResolverWrapper struct {
// The following fields are initialized when the wrapper is created and are
// read-only afterwards, and therefore can be accessed without a mutex.
cc resolverStateUpdater
channelzID *channelz.Identifier
ignoreServiceConfig bool
opts ccResolverWrapperOpts
serializer *grpcsync.CallbackSerializer // To serialize all incoming calls.
serializerCancel context.CancelFunc // To close the serializer, accessed only from close().
// All incoming (resolver --> gRPC) calls are guaranteed to execute in a
// mutually exclusive manner as they are scheduled on the serializer.
// Fields accessed *only* in these serializer callbacks, can therefore be
// accessed without a mutex.
curState resolver.State
// mu guards access to the below fields.
mu sync.Mutex
closed bool
resolver resolver.Resolver // Accessed only from outgoing calls.
}
// ccResolverWrapperOpts wraps the arguments to be passed when creating a new
// ccResolverWrapper.
type ccResolverWrapperOpts struct {
target resolver.Target // User specified dial target to resolve.
builder resolver.Builder // Resolver builder to use.
bOpts resolver.BuildOptions // Resolver build options to use.
channelzID *channelz.Identifier // Channelz identifier for the channel.
}
// newCCResolverWrapper uses the resolver.Builder to build a Resolver and
// returns a ccResolverWrapper object which wraps the newly built resolver.
func newCCResolverWrapper(cc resolverStateUpdater, opts ccResolverWrapperOpts) (*ccResolverWrapper, error) {
ctx, cancel := context.WithCancel(context.Background())
ccr := &ccResolverWrapper{
cc: cc,
channelzID: opts.channelzID,
ignoreServiceConfig: opts.bOpts.DisableServiceConfig,
opts: opts,
serializer: grpcsync.NewCallbackSerializer(ctx),
serializerCancel: cancel,
}
// Cannot hold the lock at build time because the resolver can send an
// update or error inline and these incoming calls grab the lock to schedule
// a callback in the serializer.
r, err := opts.builder.Build(opts.target, ccr, opts.bOpts)
if err != nil {
cancel()
return nil, err
}
// Any error reported by the resolver at build time that leads to a
// re-resolution request from the balancer is dropped by grpc until we
// return from this function. So, we don't have to handle pending resolveNow
// requests here.
ccr.mu.Lock()
ccr.resolver = r
ccr.mu.Unlock()
return ccr, nil
}
func (ccr *ccResolverWrapper) resolveNow(o resolver.ResolveNowOptions) {
ccr.mu.Lock()
defer ccr.mu.Unlock()
// ccr.resolver field is set only after the call to Build() returns. But in
// the process of building, the resolver may send an error update which when
// propagated to the balancer may result in a re-resolution request.
if ccr.closed || ccr.resolver == nil {
return
}
ccr.resolver.ResolveNow(o)
}
func (ccr *ccResolverWrapper) close() {
ccr.mu.Lock()
if ccr.closed {
ccr.mu.Unlock()
return
}
channelz.Info(logger, ccr.channelzID, "Closing the name resolver")
// Close the serializer to ensure that no more calls from the resolver are
// handled, before actually closing the resolver.
ccr.serializerCancel()
ccr.closed = true
r := ccr.resolver
ccr.mu.Unlock()
// Give enqueued callbacks a chance to finish.
<-ccr.serializer.Done()
// Spawn a goroutine to close the resolver (since it may block trying to
// cleanup all allocated resources) and return early.
go r.Close()
}
// serializerScheduleLocked is a convenience method to schedule a function to be
// run on the serializer while holding ccr.mu.
func (ccr *ccResolverWrapper) serializerScheduleLocked(f func(context.Context)) {
ccr.mu.Lock()
ccr.serializer.Schedule(f)
ccr.mu.Unlock()
}
// UpdateState is called by resolver implementations to report new state to gRPC
// which includes addresses and service config.
func (ccr *ccResolverWrapper) UpdateState(s resolver.State) error {
errCh := make(chan error, 1)
if s.Endpoints == nil {
s.Endpoints = make([]resolver.Endpoint, 0, len(s.Addresses))
for _, a := range s.Addresses {
ep := resolver.Endpoint{Addresses: []resolver.Address{a}, Attributes: a.BalancerAttributes}
ep.Addresses[0].BalancerAttributes = nil
s.Endpoints = append(s.Endpoints, ep)
}
}
ok := ccr.serializer.Schedule(func(context.Context) {
ccr.addChannelzTraceEvent(s)
ccr.curState = s
if err := ccr.cc.updateResolverState(ccr.curState, nil); err == balancer.ErrBadResolverState {
errCh <- balancer.ErrBadResolverState
return
}
errCh <- nil
})
if !ok {
// The only time when Schedule() fail to add the callback to the
// serializer is when the serializer is closed, and this happens only
// when the resolver wrapper is closed.
return nil
}
return <-errCh
}
// ReportError is called by resolver implementations to report errors
// encountered during name resolution to gRPC.
func (ccr *ccResolverWrapper) ReportError(err error) {
ccr.serializerScheduleLocked(func(_ context.Context) {
channelz.Warningf(logger, ccr.channelzID, "ccResolverWrapper: reporting error to cc: %v", err)
ccr.cc.updateResolverState(resolver.State{}, err)
})
}
// NewAddress is called by the resolver implementation to send addresses to
// gRPC.
func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
ccr.serializerScheduleLocked(func(_ context.Context) {
ccr.addChannelzTraceEvent(resolver.State{Addresses: addrs, ServiceConfig: ccr.curState.ServiceConfig})
ccr.curState.Addresses = addrs
ccr.cc.updateResolverState(ccr.curState, nil)
})
}
// NewServiceConfig is called by the resolver implementation to send service
// configs to gRPC.
func (ccr *ccResolverWrapper) NewServiceConfig(sc string) {
ccr.serializerScheduleLocked(func(_ context.Context) {
channelz.Infof(logger, ccr.channelzID, "ccResolverWrapper: got new service config: %s", sc)
if ccr.ignoreServiceConfig {
channelz.Info(logger, ccr.channelzID, "Service config lookups disabled; ignoring config")
return
}
scpr := parseServiceConfig(sc)
if scpr.Err != nil {
channelz.Warningf(logger, ccr.channelzID, "ccResolverWrapper: error parsing service config: %v", scpr.Err)
return
}
ccr.addChannelzTraceEvent(resolver.State{Addresses: ccr.curState.Addresses, ServiceConfig: scpr})
ccr.curState.ServiceConfig = scpr
ccr.cc.updateResolverState(ccr.curState, nil)
})
}
// ParseServiceConfig is called by resolver implementations to parse a JSON
// representation of the service config.
func (ccr *ccResolverWrapper) ParseServiceConfig(scJSON string) *serviceconfig.ParseResult {
return parseServiceConfig(scJSON)
}
// addChannelzTraceEvent adds a channelz trace event containing the new
// state received from resolver implementations.
func (ccr *ccResolverWrapper) addChannelzTraceEvent(s resolver.State) {
var updates []string
var oldSC, newSC *ServiceConfig
var oldOK, newOK bool
if ccr.curState.ServiceConfig != nil {
oldSC, oldOK = ccr.curState.ServiceConfig.Config.(*ServiceConfig)
}
if s.ServiceConfig != nil {
newSC, newOK = s.ServiceConfig.Config.(*ServiceConfig)
}
if oldOK != newOK || (oldOK && newOK && oldSC.rawJSONString != newSC.rawJSONString) {
updates = append(updates, "service config updated")
}
if len(ccr.curState.Addresses) > 0 && len(s.Addresses) == 0 {
updates = append(updates, "resolver returned an empty address list")
} else if len(ccr.curState.Addresses) == 0 && len(s.Addresses) > 0 {
updates = append(updates, "resolver returned new addresses")
}
channelz.Infof(logger, ccr.channelzID, "Resolver state updated: %s (%v)", pretty.ToJSON(s), strings.Join(updates, "; "))
}

197
vendor/google.golang.org/grpc/resolver_wrapper.go generated vendored Normal file
View File

@ -0,0 +1,197 @@
/*
*
* Copyright 2017 gRPC 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 grpc
import (
"context"
"strings"
"sync"
"google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/internal/grpcsync"
"google.golang.org/grpc/internal/pretty"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/serviceconfig"
)
// ccResolverWrapper is a wrapper on top of cc for resolvers.
// It implements resolver.ClientConn interface.
type ccResolverWrapper struct {
// The following fields are initialized when the wrapper is created and are
// read-only afterwards, and therefore can be accessed without a mutex.
cc *ClientConn
ignoreServiceConfig bool
serializer *grpcsync.CallbackSerializer
serializerCancel context.CancelFunc
resolver resolver.Resolver // only accessed within the serializer
// The following fields are protected by mu. Caller must take cc.mu before
// taking mu.
mu sync.Mutex
curState resolver.State
closed bool
}
// newCCResolverWrapper initializes the ccResolverWrapper. It can only be used
// after calling start, which builds the resolver.
func newCCResolverWrapper(cc *ClientConn) *ccResolverWrapper {
ctx, cancel := context.WithCancel(cc.ctx)
return &ccResolverWrapper{
cc: cc,
ignoreServiceConfig: cc.dopts.disableServiceConfig,
serializer: grpcsync.NewCallbackSerializer(ctx),
serializerCancel: cancel,
}
}
// start builds the name resolver using the resolver.Builder in cc and returns
// any error encountered. It must always be the first operation performed on
// any newly created ccResolverWrapper, except that close may be called instead.
func (ccr *ccResolverWrapper) start() error {
errCh := make(chan error)
ccr.serializer.Schedule(func(ctx context.Context) {
if ctx.Err() != nil {
return
}
opts := resolver.BuildOptions{
DisableServiceConfig: ccr.cc.dopts.disableServiceConfig,
DialCreds: ccr.cc.dopts.copts.TransportCredentials,
CredsBundle: ccr.cc.dopts.copts.CredsBundle,
Dialer: ccr.cc.dopts.copts.Dialer,
}
var err error
ccr.resolver, err = ccr.cc.resolverBuilder.Build(ccr.cc.parsedTarget, ccr, opts)
errCh <- err
})
return <-errCh
}
func (ccr *ccResolverWrapper) resolveNow(o resolver.ResolveNowOptions) {
ccr.serializer.Schedule(func(ctx context.Context) {
if ctx.Err() != nil || ccr.resolver == nil {
return
}
ccr.resolver.ResolveNow(o)
})
}
// close initiates async shutdown of the wrapper. To determine the wrapper has
// finished shutting down, the channel should block on ccr.serializer.Done()
// without cc.mu held.
func (ccr *ccResolverWrapper) close() {
channelz.Info(logger, ccr.cc.channelzID, "Closing the name resolver")
ccr.mu.Lock()
ccr.closed = true
ccr.mu.Unlock()
ccr.serializer.Schedule(func(context.Context) {
if ccr.resolver == nil {
return
}
ccr.resolver.Close()
ccr.resolver = nil
})
ccr.serializerCancel()
}
// UpdateState is called by resolver implementations to report new state to gRPC
// which includes addresses and service config.
func (ccr *ccResolverWrapper) UpdateState(s resolver.State) error {
ccr.cc.mu.Lock()
ccr.mu.Lock()
if ccr.closed {
ccr.mu.Unlock()
ccr.cc.mu.Unlock()
return nil
}
if s.Endpoints == nil {
s.Endpoints = make([]resolver.Endpoint, 0, len(s.Addresses))
for _, a := range s.Addresses {
ep := resolver.Endpoint{Addresses: []resolver.Address{a}, Attributes: a.BalancerAttributes}
ep.Addresses[0].BalancerAttributes = nil
s.Endpoints = append(s.Endpoints, ep)
}
}
ccr.addChannelzTraceEvent(s)
ccr.curState = s
ccr.mu.Unlock()
return ccr.cc.updateResolverStateAndUnlock(s, nil)
}
// ReportError is called by resolver implementations to report errors
// encountered during name resolution to gRPC.
func (ccr *ccResolverWrapper) ReportError(err error) {
ccr.cc.mu.Lock()
ccr.mu.Lock()
if ccr.closed {
ccr.mu.Unlock()
ccr.cc.mu.Unlock()
return
}
ccr.mu.Unlock()
channelz.Warningf(logger, ccr.cc.channelzID, "ccResolverWrapper: reporting error to cc: %v", err)
ccr.cc.updateResolverStateAndUnlock(resolver.State{}, err)
}
// NewAddress is called by the resolver implementation to send addresses to
// gRPC.
func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
ccr.cc.mu.Lock()
ccr.mu.Lock()
if ccr.closed {
ccr.mu.Unlock()
ccr.cc.mu.Unlock()
return
}
s := resolver.State{Addresses: addrs, ServiceConfig: ccr.curState.ServiceConfig}
ccr.addChannelzTraceEvent(s)
ccr.curState = s
ccr.mu.Unlock()
ccr.cc.updateResolverStateAndUnlock(s, nil)
}
// ParseServiceConfig is called by resolver implementations to parse a JSON
// representation of the service config.
func (ccr *ccResolverWrapper) ParseServiceConfig(scJSON string) *serviceconfig.ParseResult {
return parseServiceConfig(scJSON)
}
// addChannelzTraceEvent adds a channelz trace event containing the new
// state received from resolver implementations.
func (ccr *ccResolverWrapper) addChannelzTraceEvent(s resolver.State) {
var updates []string
var oldSC, newSC *ServiceConfig
var oldOK, newOK bool
if ccr.curState.ServiceConfig != nil {
oldSC, oldOK = ccr.curState.ServiceConfig.Config.(*ServiceConfig)
}
if s.ServiceConfig != nil {
newSC, newOK = s.ServiceConfig.Config.(*ServiceConfig)
}
if oldOK != newOK || (oldOK && newOK && oldSC.rawJSONString != newSC.rawJSONString) {
updates = append(updates, "service config updated")
}
if len(ccr.curState.Addresses) > 0 && len(s.Addresses) == 0 {
updates = append(updates, "resolver returned an empty address list")
} else if len(ccr.curState.Addresses) == 0 && len(s.Addresses) > 0 {
updates = append(updates, "resolver returned new addresses")
}
channelz.Infof(logger, ccr.cc.channelzID, "Resolver state updated: %s (%v)", pretty.ToJSON(s), strings.Join(updates, "; "))
}

View File

@ -70,6 +70,10 @@ func init() {
internal.GetServerCredentials = func(srv *Server) credentials.TransportCredentials { internal.GetServerCredentials = func(srv *Server) credentials.TransportCredentials {
return srv.opts.creds return srv.opts.creds
} }
internal.IsRegisteredMethod = func(srv *Server, method string) bool {
return srv.isRegisteredMethod(method)
}
internal.ServerFromContext = serverFromContext
internal.DrainServerTransports = func(srv *Server, addr string) { internal.DrainServerTransports = func(srv *Server, addr string) {
srv.drainServerTransports(addr) srv.drainServerTransports(addr)
} }
@ -81,6 +85,7 @@ func init() {
} }
internal.BinaryLogger = binaryLogger internal.BinaryLogger = binaryLogger
internal.JoinServerOptions = newJoinServerOption internal.JoinServerOptions = newJoinServerOption
internal.RecvBufferPool = recvBufferPool
} }
var statusOK = status.New(codes.OK, "") var statusOK = status.New(codes.OK, "")
@ -139,7 +144,8 @@ type Server struct {
channelzID *channelz.Identifier channelzID *channelz.Identifier
czData *channelzData czData *channelzData
serverWorkerChannel chan func() serverWorkerChannel chan func()
serverWorkerChannelClose func()
} }
type serverOptions struct { type serverOptions struct {
@ -578,11 +584,13 @@ func NumStreamWorkers(numServerWorkers uint32) ServerOption {
// options are used: StatsHandler, EnableTracing, or binary logging. In such // options are used: StatsHandler, EnableTracing, or binary logging. In such
// cases, the shared buffer pool will be ignored. // cases, the shared buffer pool will be ignored.
// //
// # Experimental // Deprecated: use experimental.WithRecvBufferPool instead. Will be deleted in
// // v1.60.0 or later.
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func RecvBufferPool(bufferPool SharedBufferPool) ServerOption { func RecvBufferPool(bufferPool SharedBufferPool) ServerOption {
return recvBufferPool(bufferPool)
}
func recvBufferPool(bufferPool SharedBufferPool) ServerOption {
return newFuncServerOption(func(o *serverOptions) { return newFuncServerOption(func(o *serverOptions) {
o.recvBufferPool = bufferPool o.recvBufferPool = bufferPool
}) })
@ -616,15 +624,14 @@ func (s *Server) serverWorker() {
// connections to reduce the time spent overall on runtime.morestack. // connections to reduce the time spent overall on runtime.morestack.
func (s *Server) initServerWorkers() { func (s *Server) initServerWorkers() {
s.serverWorkerChannel = make(chan func()) s.serverWorkerChannel = make(chan func())
s.serverWorkerChannelClose = grpcsync.OnceFunc(func() {
close(s.serverWorkerChannel)
})
for i := uint32(0); i < s.opts.numServerWorkers; i++ { for i := uint32(0); i < s.opts.numServerWorkers; i++ {
go s.serverWorker() go s.serverWorker()
} }
} }
func (s *Server) stopServerWorkers() {
close(s.serverWorkerChannel)
}
// NewServer creates a gRPC server which has no service registered and has not // NewServer creates a gRPC server which has no service registered and has not
// started to accept requests yet. // started to accept requests yet.
func NewServer(opt ...ServerOption) *Server { func NewServer(opt ...ServerOption) *Server {
@ -806,6 +813,18 @@ func (l *listenSocket) Close() error {
// Serve returns when lis.Accept fails with fatal errors. lis will be closed when // Serve returns when lis.Accept fails with fatal errors. lis will be closed when
// this method returns. // this method returns.
// Serve will return a non-nil error unless Stop or GracefulStop is called. // Serve will return a non-nil error unless Stop or GracefulStop is called.
//
// Note: All supported releases of Go (as of December 2023) override the OS
// defaults for TCP keepalive time and interval to 15s. To enable TCP keepalive
// with OS defaults for keepalive time and interval, callers need to do the
// following two things:
// - pass a net.Listener created by calling the Listen method on a
// net.ListenConfig with the `KeepAlive` field set to a negative value. This
// will result in the Go standard library not overriding OS defaults for TCP
// keepalive interval and time. But this will also result in the Go standard
// library not enabling TCP keepalives by default.
// - override the Accept method on the passed in net.Listener and set the
// SO_KEEPALIVE socket option to enable TCP keepalives, with OS defaults.
func (s *Server) Serve(lis net.Listener) error { func (s *Server) Serve(lis net.Listener) error {
s.mu.Lock() s.mu.Lock()
s.printf("serving") s.printf("serving")
@ -917,7 +936,7 @@ func (s *Server) handleRawConn(lisAddr string, rawConn net.Conn) {
return return
} }
go func() { go func() {
s.serveStreams(st) s.serveStreams(context.Background(), st, rawConn)
s.removeConn(lisAddr, st) s.removeConn(lisAddr, st)
}() }()
} }
@ -971,18 +990,29 @@ func (s *Server) newHTTP2Transport(c net.Conn) transport.ServerTransport {
return st return st
} }
func (s *Server) serveStreams(st transport.ServerTransport) { func (s *Server) serveStreams(ctx context.Context, st transport.ServerTransport, rawConn net.Conn) {
defer st.Close(errors.New("finished serving streams for the server transport")) ctx = transport.SetConnection(ctx, rawConn)
var wg sync.WaitGroup ctx = peer.NewContext(ctx, st.Peer())
for _, sh := range s.opts.statsHandlers {
ctx = sh.TagConn(ctx, &stats.ConnTagInfo{
RemoteAddr: st.Peer().Addr,
LocalAddr: st.Peer().LocalAddr,
})
sh.HandleConn(ctx, &stats.ConnBegin{})
}
defer func() {
st.Close(errors.New("finished serving streams for the server transport"))
for _, sh := range s.opts.statsHandlers {
sh.HandleConn(ctx, &stats.ConnEnd{})
}
}()
streamQuota := newHandlerQuota(s.opts.maxConcurrentStreams) streamQuota := newHandlerQuota(s.opts.maxConcurrentStreams)
st.HandleStreams(func(stream *transport.Stream) { st.HandleStreams(ctx, func(stream *transport.Stream) {
wg.Add(1)
streamQuota.acquire() streamQuota.acquire()
f := func() { f := func() {
defer streamQuota.release() defer streamQuota.release()
defer wg.Done()
s.handleStream(st, stream) s.handleStream(st, stream)
} }
@ -996,7 +1026,6 @@ func (s *Server) serveStreams(st transport.ServerTransport) {
} }
go f() go f()
}) })
wg.Wait()
} }
var _ http.Handler = (*Server)(nil) var _ http.Handler = (*Server)(nil)
@ -1040,7 +1069,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} }
defer s.removeConn(listenerAddressForServeHTTP, st) defer s.removeConn(listenerAddressForServeHTTP, st)
s.serveStreams(st) s.serveStreams(r.Context(), st, nil)
} }
func (s *Server) addConn(addr string, st transport.ServerTransport) bool { func (s *Server) addConn(addr string, st transport.ServerTransport) bool {
@ -1689,6 +1718,7 @@ func (s *Server) processStreamingRPC(ctx context.Context, t transport.ServerTran
func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Stream) { func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Stream) {
ctx := stream.Context() ctx := stream.Context()
ctx = contextWithServer(ctx, s)
var ti *traceInfo var ti *traceInfo
if EnableTracing { if EnableTracing {
tr := trace.New("grpc.Recv."+methodFamily(stream.Method()), stream.Method()) tr := trace.New("grpc.Recv."+methodFamily(stream.Method()), stream.Method())
@ -1697,7 +1727,7 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
tr: tr, tr: tr,
firstLine: firstLine{ firstLine: firstLine{
client: false, client: false,
remoteAddr: t.RemoteAddr(), remoteAddr: t.Peer().Addr,
}, },
} }
if dl, ok := ctx.Deadline(); ok { if dl, ok := ctx.Deadline(); ok {
@ -1731,6 +1761,22 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
service := sm[:pos] service := sm[:pos]
method := sm[pos+1:] method := sm[pos+1:]
md, _ := metadata.FromIncomingContext(ctx)
for _, sh := range s.opts.statsHandlers {
ctx = sh.TagRPC(ctx, &stats.RPCTagInfo{FullMethodName: stream.Method()})
sh.HandleRPC(ctx, &stats.InHeader{
FullMethod: stream.Method(),
RemoteAddr: t.Peer().Addr,
LocalAddr: t.Peer().LocalAddr,
Compression: stream.RecvCompress(),
WireLength: stream.HeaderWireLength(),
Header: md,
})
}
// To have calls in stream callouts work. Will delete once all stats handler
// calls come from the gRPC layer.
stream.SetContext(ctx)
srv, knownService := s.services[service] srv, knownService := s.services[service]
if knownService { if knownService {
if md, ok := srv.methods[method]; ok { if md, ok := srv.methods[method]; ok {
@ -1820,62 +1866,68 @@ func ServerTransportStreamFromContext(ctx context.Context) ServerTransportStream
// pending RPCs on the client side will get notified by connection // pending RPCs on the client side will get notified by connection
// errors. // errors.
func (s *Server) Stop() { func (s *Server) Stop() {
s.quit.Fire() s.stop(false)
defer func() {
s.serveWG.Wait()
s.done.Fire()
}()
s.channelzRemoveOnce.Do(func() { channelz.RemoveEntry(s.channelzID) })
s.mu.Lock()
listeners := s.lis
s.lis = nil
conns := s.conns
s.conns = nil
// interrupt GracefulStop if Stop and GracefulStop are called concurrently.
s.cv.Broadcast()
s.mu.Unlock()
for lis := range listeners {
lis.Close()
}
for _, cs := range conns {
for st := range cs {
st.Close(errors.New("Server.Stop called"))
}
}
if s.opts.numServerWorkers > 0 {
s.stopServerWorkers()
}
s.mu.Lock()
if s.events != nil {
s.events.Finish()
s.events = nil
}
s.mu.Unlock()
} }
// GracefulStop stops the gRPC server gracefully. It stops the server from // GracefulStop stops the gRPC server gracefully. It stops the server from
// accepting new connections and RPCs and blocks until all the pending RPCs are // accepting new connections and RPCs and blocks until all the pending RPCs are
// finished. // finished.
func (s *Server) GracefulStop() { func (s *Server) GracefulStop() {
s.stop(true)
}
func (s *Server) stop(graceful bool) {
s.quit.Fire() s.quit.Fire()
defer s.done.Fire() defer s.done.Fire()
s.channelzRemoveOnce.Do(func() { channelz.RemoveEntry(s.channelzID) }) s.channelzRemoveOnce.Do(func() { channelz.RemoveEntry(s.channelzID) })
s.mu.Lock() s.mu.Lock()
if s.conns == nil { s.closeListenersLocked()
s.mu.Unlock() // Wait for serving threads to be ready to exit. Only then can we be sure no
return // new conns will be created.
s.mu.Unlock()
s.serveWG.Wait()
s.mu.Lock()
defer s.mu.Unlock()
if graceful {
s.drainAllServerTransportsLocked()
} else {
s.closeServerTransportsLocked()
} }
for lis := range s.lis { for len(s.conns) != 0 {
lis.Close() s.cv.Wait()
} }
s.lis = nil s.conns = nil
if s.opts.numServerWorkers > 0 {
// Closing the channel (only once, via grpcsync.OnceFunc) after all the
// connections have been closed above ensures that there are no
// goroutines executing the callback passed to st.HandleStreams (where
// the channel is written to).
s.serverWorkerChannelClose()
}
if s.events != nil {
s.events.Finish()
s.events = nil
}
}
// s.mu must be held by the caller.
func (s *Server) closeServerTransportsLocked() {
for _, conns := range s.conns {
for st := range conns {
st.Close(errors.New("Server.Stop called"))
}
}
}
// s.mu must be held by the caller.
func (s *Server) drainAllServerTransportsLocked() {
if !s.drain { if !s.drain {
for _, conns := range s.conns { for _, conns := range s.conns {
for st := range conns { for st := range conns {
@ -1884,22 +1936,14 @@ func (s *Server) GracefulStop() {
} }
s.drain = true s.drain = true
} }
}
// Wait for serving threads to be ready to exit. Only then can we be sure no // s.mu must be held by the caller.
// new conns will be created. func (s *Server) closeListenersLocked() {
s.mu.Unlock() for lis := range s.lis {
s.serveWG.Wait() lis.Close()
s.mu.Lock()
for len(s.conns) != 0 {
s.cv.Wait()
} }
s.conns = nil s.lis = nil
if s.events != nil {
s.events.Finish()
s.events = nil
}
s.mu.Unlock()
} }
// contentSubtype must be lowercase // contentSubtype must be lowercase
@ -1913,11 +1957,50 @@ func (s *Server) getCodec(contentSubtype string) baseCodec {
} }
codec := encoding.GetCodec(contentSubtype) codec := encoding.GetCodec(contentSubtype)
if codec == nil { if codec == nil {
logger.Warningf("Unsupported codec %q. Defaulting to %q for now. This will start to fail in future releases.", contentSubtype, proto.Name)
return encoding.GetCodec(proto.Name) return encoding.GetCodec(proto.Name)
} }
return codec return codec
} }
type serverKey struct{}
// serverFromContext gets the Server from the context.
func serverFromContext(ctx context.Context) *Server {
s, _ := ctx.Value(serverKey{}).(*Server)
return s
}
// contextWithServer sets the Server in the context.
func contextWithServer(ctx context.Context, server *Server) context.Context {
return context.WithValue(ctx, serverKey{}, server)
}
// isRegisteredMethod returns whether the passed in method is registered as a
// method on the server. /service/method and service/method will match if the
// service and method are registered on the server.
func (s *Server) isRegisteredMethod(serviceMethod string) bool {
if serviceMethod != "" && serviceMethod[0] == '/' {
serviceMethod = serviceMethod[1:]
}
pos := strings.LastIndex(serviceMethod, "/")
if pos == -1 { // Invalid method name syntax.
return false
}
service := serviceMethod[:pos]
method := serviceMethod[pos+1:]
srv, knownService := s.services[service]
if knownService {
if _, ok := srv.methods[method]; ok {
return true
}
if _, ok := srv.streams[method]; ok {
return true
}
}
return false
}
// SetHeader sets the header metadata to be sent from the server to the client. // SetHeader sets the header metadata to be sent from the server to the client.
// The context provided must be the context passed to the server's handler. // The context provided must be the context passed to the server's handler.
// //

View File

@ -19,4 +19,4 @@
package grpc package grpc
// Version is the current grpc version. // Version is the current grpc version.
const Version = "1.59.0" const Version = "1.60.1"

166
vendor/google.golang.org/grpc/vet.sh generated vendored
View File

@ -35,7 +35,6 @@ if [[ "$1" = "-install" ]]; then
# Install the pinned versions as defined in module tools. # Install the pinned versions as defined in module tools.
pushd ./test/tools pushd ./test/tools
go install \ go install \
golang.org/x/lint/golint \
golang.org/x/tools/cmd/goimports \ golang.org/x/tools/cmd/goimports \
honnef.co/go/tools/cmd/staticcheck \ honnef.co/go/tools/cmd/staticcheck \
github.com/client9/misspell/cmd/misspell github.com/client9/misspell/cmd/misspell
@ -77,12 +76,16 @@ fi
not grep 'func Test[^(]' *_test.go not grep 'func Test[^(]' *_test.go
not grep 'func Test[^(]' test/*.go not grep 'func Test[^(]' test/*.go
# - Check for typos in test function names
git grep 'func (s) ' -- "*_test.go" | not grep -v 'func (s) Test'
git grep 'func [A-Z]' -- "*_test.go" | not grep -v 'func Test\|Benchmark\|Example'
# - Do not import x/net/context. # - Do not import x/net/context.
not git grep -l 'x/net/context' -- "*.go" not git grep -l 'x/net/context' -- "*.go"
# - Do not import math/rand for real library code. Use internal/grpcrand for # - Do not import math/rand for real library code. Use internal/grpcrand for
# thread safety. # thread safety.
git grep -l '"math/rand"' -- "*.go" 2>&1 | not grep -v '^examples\|^stress\|grpcrand\|^benchmark\|wrr_test' git grep -l '"math/rand"' -- "*.go" 2>&1 | not grep -v '^examples\|^interop/stress\|grpcrand\|^benchmark\|wrr_test'
# - Do not use "interface{}"; use "any" instead. # - Do not use "interface{}"; use "any" instead.
git grep -l 'interface{}' -- "*.go" 2>&1 | not grep -v '\.pb\.go\|protoc-gen-go-grpc' git grep -l 'interface{}' -- "*.go" 2>&1 | not grep -v '\.pb\.go\|protoc-gen-go-grpc'
@ -94,15 +97,14 @@ git grep -l -e 'grpclog.I' --or -e 'grpclog.W' --or -e 'grpclog.E' --or -e 'grpc
not git grep "\(import \|^\s*\)\"github.com/golang/protobuf/ptypes/" -- "*.go" not git grep "\(import \|^\s*\)\"github.com/golang/protobuf/ptypes/" -- "*.go"
# - Ensure all usages of grpc_testing package are renamed when importing. # - Ensure all usages of grpc_testing package are renamed when importing.
not git grep "\(import \|^\s*\)\"google.golang.org/grpc/interop/grpc_testing" -- "*.go" not git grep "\(import \|^\s*\)\"google.golang.org/grpc/interop/grpc_testing" -- "*.go"
# - Ensure all xds proto imports are renamed to *pb or *grpc. # - Ensure all xds proto imports are renamed to *pb or *grpc.
git grep '"github.com/envoyproxy/go-control-plane/envoy' -- '*.go' ':(exclude)*.pb.go' | not grep -v 'pb "\|grpc "' git grep '"github.com/envoyproxy/go-control-plane/envoy' -- '*.go' ':(exclude)*.pb.go' | not grep -v 'pb "\|grpc "'
misspell -error . misspell -error .
# - gofmt, goimports, golint (with exceptions for generated code), go vet, # - gofmt, goimports, go vet, go mod tidy.
# go mod tidy.
# Perform these checks on each module inside gRPC. # Perform these checks on each module inside gRPC.
for MOD_FILE in $(find . -name 'go.mod'); do for MOD_FILE in $(find . -name 'go.mod'); do
MOD_DIR=$(dirname ${MOD_FILE}) MOD_DIR=$(dirname ${MOD_FILE})
@ -110,7 +112,6 @@ for MOD_FILE in $(find . -name 'go.mod'); do
go vet -all ./... | fail_on_output go vet -all ./... | fail_on_output
gofmt -s -d -l . 2>&1 | fail_on_output gofmt -s -d -l . 2>&1 | fail_on_output
goimports -l . 2>&1 | not grep -vE "\.pb\.go" goimports -l . 2>&1 | not grep -vE "\.pb\.go"
golint ./... 2>&1 | not grep -vE "/grpc_testing_not_regenerate/.*\.pb\.go:"
go mod tidy -compat=1.19 go mod tidy -compat=1.19
git status --porcelain 2>&1 | fail_on_output || \ git status --porcelain 2>&1 | fail_on_output || \
@ -119,94 +120,73 @@ for MOD_FILE in $(find . -name 'go.mod'); do
done done
# - Collection of static analysis checks # - Collection of static analysis checks
#
# TODO(dfawley): don't use deprecated functions in examples or first-party
# plugins.
# TODO(dfawley): enable ST1019 (duplicate imports) but allow for protobufs.
SC_OUT="$(mktemp)" SC_OUT="$(mktemp)"
staticcheck -go 1.19 -checks 'inherit,-ST1015,-ST1019,-SA1019' ./... > "${SC_OUT}" || true staticcheck -go 1.19 -checks 'all' ./... > "${SC_OUT}" || true
# Error if anything other than deprecation warnings are printed.
not grep -v "is deprecated:.*SA1019" "${SC_OUT}" # Error for anything other than checks that need exclusions.
# Only ignore the following deprecated types/fields/functions. grep -v "(ST1000)" "${SC_OUT}" | grep -v "(SA1019)" | grep -v "(ST1003)" | not grep -v "(ST1019)\|\(other import of\)"
not grep -Fv '.CredsBundle
.HeaderMap # Exclude underscore checks for generated code.
.Metadata is deprecated: use Attributes grep "(ST1003)" "${SC_OUT}" | not grep -v '\(.pb.go:\)\|\(code_string_test.go:\)'
.NewAddress
.NewServiceConfig # Error for duplicate imports not including grpc protos.
.Type is deprecated: use Attributes grep "(ST1019)\|\(other import of\)" "${SC_OUT}" | not grep -Fv 'XXXXX PleaseIgnoreUnused
BuildVersion is deprecated channelz/grpc_channelz_v1"
balancer.ErrTransientFailure go-control-plane/envoy
balancer.Picker grpclb/grpc_lb_v1"
extDesc.Filename is deprecated health/grpc_health_v1"
github.com/golang/protobuf/jsonpb is deprecated interop/grpc_testing"
grpc.CallCustomCodec orca/v3"
grpc.Code proto/grpc_gcp"
grpc.Compressor proto/grpc_lookup_v1"
grpc.CustomCodec reflection/grpc_reflection_v1"
grpc.Decompressor reflection/grpc_reflection_v1alpha"
grpc.MaxMsgSize XXXXX PleaseIgnoreUnused'
grpc.MethodConfig
grpc.NewGZIPCompressor # Error for any package comments not in generated code.
grpc.NewGZIPDecompressor grep "(ST1000)" "${SC_OUT}" | not grep -v "\.pb\.go:"
grpc.RPCCompressor
grpc.RPCDecompressor # Only ignore the following deprecated types/fields/functions and exclude
grpc.ServiceConfig # generated code.
grpc.WithCompressor grep "(SA1019)" "${SC_OUT}" | not grep -Fv 'XXXXX PleaseIgnoreUnused
grpc.WithDecompressor XXXXX Protobuf related deprecation errors:
grpc.WithDialer "github.com/golang/protobuf
grpc.WithMaxMsgSize .pb.go:
grpc.WithServiceConfig : ptypes.
grpc.WithTimeout proto.RegisterType
http.CloseNotifier XXXXX gRPC internal usage deprecation errors:
info.SecurityVersion "google.golang.org/grpc
proto is deprecated : grpc.
proto.InternalMessageInfo is deprecated : v1alpha.
proto.EnumName is deprecated : v1alphareflectionpb.
proto.ErrInternalBadWireType is deprecated BalancerAttributes is deprecated:
proto.FileDescriptor is deprecated CredsBundle is deprecated:
proto.Marshaler is deprecated Metadata is deprecated: use Attributes instead.
proto.MessageType is deprecated NewSubConn is deprecated:
proto.RegisterEnum is deprecated OverrideServerName is deprecated:
proto.RegisterFile is deprecated RemoveSubConn is deprecated:
proto.RegisterType is deprecated SecurityVersion is deprecated:
proto.RegisterExtension is deprecated
proto.RegisteredExtension is deprecated
proto.RegisteredExtensions is deprecated
proto.RegisterMapType is deprecated
proto.Unmarshaler is deprecated
Target is deprecated: Use the Target field in the BuildOptions instead. Target is deprecated: Use the Target field in the BuildOptions instead.
xxx_messageInfo_ UpdateAddresses is deprecated:
' "${SC_OUT}" UpdateSubConnState is deprecated:
balancer.ErrTransientFailure is deprecated:
# - special golint on package comments. grpc/reflection/v1alpha/reflection.proto
lint_package_comment_per_package() { XXXXX xDS deprecated fields we support
# Number of files in this go package. .ExactMatch
fileCount=$(go list -f '{{len .GoFiles}}' $1) .PrefixMatch
if [ ${fileCount} -eq 0 ]; then .SafeRegexMatch
return 0 .SuffixMatch
fi GetContainsMatch
# Number of package errors generated by golint. GetExactMatch
lintPackageCommentErrorsCount=$(golint --min_confidence 0 $1 | grep -c "should have a package comment") GetMatchSubjectAltNames
# golint complains about every file that's missing the package comment. If the GetPrefixMatch
# number of files for this package is greater than the number of errors, there's GetSafeRegexMatch
# at least one file with package comment, good. Otherwise, fail. GetSuffixMatch
if [ ${fileCount} -le ${lintPackageCommentErrorsCount} ]; then GetTlsCertificateCertificateProviderInstance
echo "Package $1 (with ${fileCount} files) is missing package comment" GetValidationContextCertificateProviderInstance
return 1 XXXXX TODO: Remove the below deprecation usages:
fi CloseNotifier
} Roots.Subjects
lint_package_comment() { XXXXX PleaseIgnoreUnused'
set +ex
count=0
for i in $(go list ./...); do
lint_package_comment_per_package "$i"
((count += $?))
done
set -ex
return $count
}
lint_package_comment
echo SUCCESS echo SUCCESS

30
vendor/modules.txt vendored
View File

@ -150,7 +150,7 @@ github.com/compose-spec/compose-go/v2/validation
# github.com/containerd/console v1.0.4 # github.com/containerd/console v1.0.4
## explicit; go 1.13 ## explicit; go 1.13
github.com/containerd/console github.com/containerd/console
# github.com/containerd/containerd v1.7.19 # github.com/containerd/containerd v1.7.20
## explicit; go 1.21 ## explicit; go 1.21
github.com/containerd/containerd/archive/compression github.com/containerd/containerd/archive/compression
github.com/containerd/containerd/content github.com/containerd/containerd/content
@ -219,7 +219,7 @@ github.com/davecgh/go-spew/spew
# github.com/distribution/reference v0.6.0 # github.com/distribution/reference v0.6.0
## explicit; go 1.20 ## explicit; go 1.20
github.com/distribution/reference github.com/distribution/reference
# github.com/docker/cli v27.0.3+incompatible # github.com/docker/cli v27.1.1+incompatible
## explicit ## explicit
github.com/docker/cli/cli github.com/docker/cli/cli
github.com/docker/cli/cli-plugins/hooks github.com/docker/cli/cli-plugins/hooks
@ -271,7 +271,7 @@ github.com/docker/distribution/registry/client/transport
github.com/docker/distribution/registry/storage/cache github.com/docker/distribution/registry/storage/cache
github.com/docker/distribution/registry/storage/cache/memory github.com/docker/distribution/registry/storage/cache/memory
github.com/docker/distribution/uuid github.com/docker/distribution/uuid
# github.com/docker/docker v27.0.3+incompatible # github.com/docker/docker v27.1.1+incompatible
## explicit ## explicit
github.com/docker/docker/api github.com/docker/docker/api
github.com/docker/docker/api/types github.com/docker/docker/api/types
@ -516,7 +516,7 @@ github.com/mitchellh/go-wordwrap
github.com/mitchellh/hashstructure/v2 github.com/mitchellh/hashstructure/v2
# github.com/mitchellh/mapstructure v1.5.0 # github.com/mitchellh/mapstructure v1.5.0
## explicit; go 1.14 ## explicit; go 1.14
# github.com/moby/buildkit v0.15.1 # github.com/moby/buildkit v0.15.1-0.20240810140024-664c2b469f19
## explicit; go 1.21.0 ## explicit; go 1.21.0
github.com/moby/buildkit/api/services/control github.com/moby/buildkit/api/services/control
github.com/moby/buildkit/api/types github.com/moby/buildkit/api/types
@ -531,6 +531,7 @@ github.com/moby/buildkit/client/llb
github.com/moby/buildkit/client/llb/sourceresolver github.com/moby/buildkit/client/llb/sourceresolver
github.com/moby/buildkit/client/ociindex github.com/moby/buildkit/client/ociindex
github.com/moby/buildkit/cmd/buildkitd/config github.com/moby/buildkit/cmd/buildkitd/config
github.com/moby/buildkit/errdefs
github.com/moby/buildkit/executor/resources/types github.com/moby/buildkit/executor/resources/types
github.com/moby/buildkit/exporter/containerimage/exptypes github.com/moby/buildkit/exporter/containerimage/exptypes
github.com/moby/buildkit/exporter/exptypes github.com/moby/buildkit/exporter/exptypes
@ -620,7 +621,7 @@ github.com/moby/sys/sequential
# github.com/moby/sys/signal v0.7.0 # github.com/moby/sys/signal v0.7.0
## explicit; go 1.16 ## explicit; go 1.16
github.com/moby/sys/signal github.com/moby/sys/signal
# github.com/moby/sys/user v0.1.0 # github.com/moby/sys/user v0.3.0
## explicit; go 1.17 ## explicit; go 1.17
github.com/moby/sys/user github.com/moby/sys/user
# github.com/moby/term v0.5.0 # github.com/moby/term v0.5.0
@ -779,10 +780,7 @@ go.opentelemetry.io/otel/internal/global
go.opentelemetry.io/otel/propagation go.opentelemetry.io/otel/propagation
go.opentelemetry.io/otel/semconv/v1.17.0 go.opentelemetry.io/otel/semconv/v1.17.0
go.opentelemetry.io/otel/semconv/v1.21.0 go.opentelemetry.io/otel/semconv/v1.21.0
# go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 # go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0
## explicit; go 1.20
go.opentelemetry.io/otel/exporters/otlp/otlpmetric
# go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0
## explicit; go 1.20 ## explicit; go 1.20
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal
@ -790,7 +788,7 @@ go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/envco
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/retry go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/retry
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform
# go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.42.0 # go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0
## explicit; go 1.20 ## explicit; go 1.20
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal
@ -886,7 +884,7 @@ golang.org/x/net/internal/socks
golang.org/x/net/internal/timeseries golang.org/x/net/internal/timeseries
golang.org/x/net/proxy golang.org/x/net/proxy
golang.org/x/net/trace golang.org/x/net/trace
# golang.org/x/oauth2 v0.11.0 # golang.org/x/oauth2 v0.13.0
## explicit; go 1.18 ## explicit; go 1.18
golang.org/x/oauth2 golang.org/x/oauth2
golang.org/x/oauth2/internal golang.org/x/oauth2/internal
@ -949,17 +947,17 @@ google.golang.org/appengine/internal/log
google.golang.org/appengine/internal/remote_api google.golang.org/appengine/internal/remote_api
google.golang.org/appengine/internal/urlfetch google.golang.org/appengine/internal/urlfetch
google.golang.org/appengine/urlfetch google.golang.org/appengine/urlfetch
# google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b # google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3
## explicit; go 1.19 ## explicit; go 1.19
google.golang.org/genproto/protobuf/field_mask google.golang.org/genproto/protobuf/field_mask
# google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b # google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f
## explicit; go 1.19 ## explicit; go 1.19
google.golang.org/genproto/googleapis/api/httpbody google.golang.org/genproto/googleapis/api/httpbody
# google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b # google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0
## explicit; go 1.19 ## explicit; go 1.19
google.golang.org/genproto/googleapis/rpc/errdetails google.golang.org/genproto/googleapis/rpc/errdetails
google.golang.org/genproto/googleapis/rpc/status google.golang.org/genproto/googleapis/rpc/status
# google.golang.org/grpc v1.59.0 # google.golang.org/grpc v1.60.1
## explicit; go 1.19 ## explicit; go 1.19
google.golang.org/grpc google.golang.org/grpc
google.golang.org/grpc/attributes google.golang.org/grpc/attributes
@ -998,6 +996,7 @@ google.golang.org/grpc/internal/metadata
google.golang.org/grpc/internal/pretty google.golang.org/grpc/internal/pretty
google.golang.org/grpc/internal/resolver google.golang.org/grpc/internal/resolver
google.golang.org/grpc/internal/resolver/dns google.golang.org/grpc/internal/resolver/dns
google.golang.org/grpc/internal/resolver/dns/internal
google.golang.org/grpc/internal/resolver/passthrough google.golang.org/grpc/internal/resolver/passthrough
google.golang.org/grpc/internal/resolver/unix google.golang.org/grpc/internal/resolver/unix
google.golang.org/grpc/internal/serviceconfig google.golang.org/grpc/internal/serviceconfig
@ -1009,6 +1008,7 @@ google.golang.org/grpc/keepalive
google.golang.org/grpc/metadata google.golang.org/grpc/metadata
google.golang.org/grpc/peer google.golang.org/grpc/peer
google.golang.org/grpc/resolver google.golang.org/grpc/resolver
google.golang.org/grpc/resolver/dns
google.golang.org/grpc/serviceconfig google.golang.org/grpc/serviceconfig
google.golang.org/grpc/stats google.golang.org/grpc/stats
google.golang.org/grpc/status google.golang.org/grpc/status