mirror of https://github.com/docker/buildx.git
vendor: update buildkit to 664c2b469f19
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
parent
38a8261f05
commit
4b27fb3022
25
go.mod
25
go.mod
|
@ -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
64
go.sum
|
@ -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=
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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`
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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:])
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
26
vendor/github.com/moby/buildkit/util/testutil/integration/run_unix.go
generated
vendored
Normal file
26
vendor/github.com/moby/buildkit/util/testutil/integration/run_unix.go
generated
vendored
Normal 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
|
||||||
|
}
|
13
vendor/github.com/moby/buildkit/util/testutil/integration/run_windows.go
generated
vendored
Normal file
13
vendor/github.com/moby/buildkit/util/testutil/integration/run_windows.go
generated
vendored
Normal 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
|
||||||
|
}
|
|
@ -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:
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
|
28
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/client.go
generated
vendored
28
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/client.go
generated
vendored
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
8
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/config.go
generated
vendored
8
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/config.go
generated
vendored
|
@ -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
|
||||||
|
|
83
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/doc.go
generated
vendored
83
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/doc.go
generated
vendored
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
18
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/client.go
generated
vendored
18
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/client.go
generated
vendored
|
@ -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)
|
||||||
|
|
||||||
|
|
81
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/doc.go
generated
vendored
81
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/doc.go
generated
vendored
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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"
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
|
||||||
|
|
|
@ -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"},
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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 (
|
||||||
|
|
|
@ -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",
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
})
|
})
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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".
|
||||||
|
|
|
@ -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)
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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 == "" {
|
||||||
|
|
70
vendor/google.golang.org/grpc/internal/resolver/dns/internal/internal.go
generated
vendored
Normal file
70
vendor/google.golang.org/grpc/internal/resolver/dns/internal/internal.go
generated
vendored
Normal 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)
|
||||||
|
)
|
|
@ -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{}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) {}},
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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, "; "))
|
|
||||||
}
|
|
|
@ -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, "; "))
|
||||||
|
}
|
|
@ -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.
|
||||||
//
|
//
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue