command: Unmanaged providers
This adds supports for "unmanaged" providers, or providers with process lifecycles not controlled by Terraform. These providers are assumed to be started before Terraform is launched, and are assumed to shut themselves down after Terraform has finished running. To do this, we must update the go-plugin dependency to v1.3.0, which added support for the "test mode" plugin serving that powers all this. As a side-effect of not needing to manage the process lifecycle anymore, Terraform also no longer needs to worry about the provider's binary, as it won't be used for anything anymore. Because of this, we can disable the init behavior that concerns itself with downloading that provider's binary, checking its version, and otherwise managing the binary. This is all managed on a per-provider basis, so managed providers that Terraform downloads, starts, and stops can be used in the same commands as unmanaged providers. The TF_REATTACH_PROVIDERS environment variable is added, and is a JSON encoding of the provider's address to the information we need to connect to it. This change enables two benefits: first, delve and other debuggers can now be attached to provider server processes, and Terraform can connect. This allows for attaching debuggers to provider processes, which before was difficult to impossible. Second, it allows the SDK test framework to host the provider in the same process as the test driver, while running a production Terraform binary against the provider. This allows for Go's built-in race detector and test coverage tooling to work as expected in provider tests. Unmanaged providers are expected to work in the exact same way as managed providers, with one caveat: Terraform kills provider processes and restarts them once per graph walk, meaning multiple times during most Terraform CLI commands. As unmanaged providers can't be killed by Terraform, and have no visibility into graph walks, unmanaged providers are likely to have differences in how their global mutable state behaves when compared to managed providers. Namely, unmanaged providers are likely to retain global state when managed providers would have reset it. Developers relying on global state should be aware of this.
This commit is contained in:
parent
5d0b75df7a
commit
5127f1ef8b
|
@ -42,6 +42,7 @@ func Provider() terraform.ResourceProvider {
|
||||||
"test_resource_config_mode": testResourceConfigMode(),
|
"test_resource_config_mode": testResourceConfigMode(),
|
||||||
"test_resource_nested_id": testResourceNestedId(),
|
"test_resource_nested_id": testResourceNestedId(),
|
||||||
"test_resource_provider_meta": testResourceProviderMeta(),
|
"test_resource_provider_meta": testResourceProviderMeta(),
|
||||||
|
"test_resource_signal": testResourceSignal(),
|
||||||
"test_undeleteable": testResourceUndeleteable(),
|
"test_undeleteable": testResourceUndeleteable(),
|
||||||
"test_resource_required_min": testResourceRequiredMin(),
|
"test_resource_required_min": testResourceRequiredMin(),
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testResourceSignal() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: testResourceSignalCreate,
|
||||||
|
Read: testResourceSignalRead,
|
||||||
|
Update: testResourceSignalUpdate,
|
||||||
|
Delete: testResourceSignalDelete,
|
||||||
|
|
||||||
|
Importer: &schema.ResourceImporter{
|
||||||
|
State: schema.ImportStatePassthrough,
|
||||||
|
},
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"optional": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testResourceSignalCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
d.SetId("testId")
|
||||||
|
|
||||||
|
return testResourceSignalRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testResourceSignalRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testResourceSignalUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
return testResourceSignalRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testResourceSignalDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
provider "test" {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "test_resource_signal" "test" {
|
||||||
|
}
|
|
@ -0,0 +1,158 @@
|
||||||
|
package e2etest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-hclog"
|
||||||
|
"github.com/hashicorp/go-plugin"
|
||||||
|
"github.com/hashicorp/terraform/builtin/providers/test"
|
||||||
|
"github.com/hashicorp/terraform/e2e"
|
||||||
|
grpcplugin "github.com/hashicorp/terraform/helper/plugin"
|
||||||
|
proto "github.com/hashicorp/terraform/internal/tfplugin5"
|
||||||
|
tfplugin "github.com/hashicorp/terraform/plugin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The tests in this file are for the "unmanaged provider workflow", which
|
||||||
|
// includes variants of the following sequence, with different details:
|
||||||
|
// terraform init
|
||||||
|
// terraform plan
|
||||||
|
// terraform apply
|
||||||
|
//
|
||||||
|
// These tests are run against an in-process server, and checked to make sure
|
||||||
|
// they're not trying to control the lifecycle of the binary. They are not
|
||||||
|
// checked for correctness of the operations themselves.
|
||||||
|
|
||||||
|
type reattachConfig struct {
|
||||||
|
Protocol string
|
||||||
|
Pid int
|
||||||
|
Test bool
|
||||||
|
Addr reattachConfigAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
type reattachConfigAddr struct {
|
||||||
|
Network string
|
||||||
|
String string
|
||||||
|
}
|
||||||
|
|
||||||
|
type providerServer struct {
|
||||||
|
*grpcplugin.GRPCProviderServer
|
||||||
|
planResourceChangeCalled bool
|
||||||
|
applyResourceChangeCalled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *providerServer) PlanResourceChange(ctx context.Context, req *proto.PlanResourceChange_Request) (*proto.PlanResourceChange_Response, error) {
|
||||||
|
p.planResourceChangeCalled = true
|
||||||
|
return p.GRPCProviderServer.PlanResourceChange(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *providerServer) ApplyResourceChange(ctx context.Context, req *proto.ApplyResourceChange_Request) (*proto.ApplyResourceChange_Response, error) {
|
||||||
|
p.applyResourceChangeCalled = true
|
||||||
|
return p.GRPCProviderServer.ApplyResourceChange(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmanagedSeparatePlan(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
fixturePath := filepath.Join("testdata", "test-provider")
|
||||||
|
tf := e2e.NewBinary(terraformBin, fixturePath)
|
||||||
|
defer tf.Close()
|
||||||
|
|
||||||
|
reattachCh := make(chan *plugin.ReattachConfig)
|
||||||
|
closeCh := make(chan struct{})
|
||||||
|
provider := &providerServer{
|
||||||
|
GRPCProviderServer: grpcplugin.NewGRPCProviderServerShim(test.Provider()),
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
go plugin.Serve(&plugin.ServeConfig{
|
||||||
|
Logger: hclog.New(&hclog.LoggerOptions{
|
||||||
|
Name: "plugintest",
|
||||||
|
Level: hclog.Trace,
|
||||||
|
Output: ioutil.Discard,
|
||||||
|
}),
|
||||||
|
Test: &plugin.ServeTestConfig{
|
||||||
|
Context: ctx,
|
||||||
|
ReattachConfigCh: reattachCh,
|
||||||
|
CloseCh: closeCh,
|
||||||
|
},
|
||||||
|
GRPCServer: plugin.DefaultGRPCServer,
|
||||||
|
VersionedPlugins: map[int]plugin.PluginSet{
|
||||||
|
5: plugin.PluginSet{
|
||||||
|
"provider": &tfplugin.GRPCProviderPlugin{
|
||||||
|
GRPCProvider: func() proto.ProviderServer {
|
||||||
|
return provider
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
config := <-reattachCh
|
||||||
|
if config == nil {
|
||||||
|
t.Fatalf("no reattach config received")
|
||||||
|
}
|
||||||
|
reattachStr, err := json.Marshal(map[string]reattachConfig{
|
||||||
|
"hashicorp/test": reattachConfig{
|
||||||
|
Protocol: string(config.Protocol),
|
||||||
|
Pid: config.Pid,
|
||||||
|
Test: true,
|
||||||
|
Addr: reattachConfigAddr{
|
||||||
|
Network: config.Addr.Network(),
|
||||||
|
String: config.Addr.String(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
tf.AddEnv("TF_REATTACH_PROVIDERS=" + string(reattachStr))
|
||||||
|
tf.AddEnv("PLUGIN_PROTOCOL_VERSION=5")
|
||||||
|
|
||||||
|
//// INIT
|
||||||
|
stdout, stderr, err := tf.Run("init")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected init error: %s\nstderr:\n%s", err, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we didn't download the binary
|
||||||
|
if strings.Contains(stdout, "Installing hashicorp/test v") {
|
||||||
|
t.Errorf("test provider download message is present in init output:\n%s", stdout)
|
||||||
|
}
|
||||||
|
if tf.FileExists(filepath.Join(".terraform", "plugins", "registry.terraform.io", "hashicorp", "test")) {
|
||||||
|
t.Errorf("test provider binary found in .terraform dir")
|
||||||
|
}
|
||||||
|
|
||||||
|
//// PLAN
|
||||||
|
_, stderr, err = tf.Run("plan", "-out=tfplan")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected plan error: %s\nstderr:\n%s", err, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !provider.planResourceChangeCalled {
|
||||||
|
t.Error("PlanResourceChange not called on in-process provider")
|
||||||
|
}
|
||||||
|
|
||||||
|
//// APPLY
|
||||||
|
_, stderr, err = tf.Run("apply", "tfplan")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected apply error: %s\nstderr:\n%s", err, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !provider.applyResourceChangeCalled {
|
||||||
|
t.Error("ApplyResourceChange not called on in-process provider")
|
||||||
|
}
|
||||||
|
provider.applyResourceChangeCalled = false
|
||||||
|
|
||||||
|
//// DESTROY
|
||||||
|
_, stderr, err = tf.Run("destroy", "-auto-approve")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected destroy error: %s\nstderr:\n%s", err, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !provider.applyResourceChangeCalled {
|
||||||
|
t.Error("ApplyResourceChange (destroy) not called on in-process provider")
|
||||||
|
}
|
||||||
|
cancel()
|
||||||
|
<-closeCh
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
plugin "github.com/hashicorp/go-plugin"
|
||||||
"github.com/hashicorp/terraform-svchost/disco"
|
"github.com/hashicorp/terraform-svchost/disco"
|
||||||
"github.com/hashicorp/terraform/addrs"
|
"github.com/hashicorp/terraform/addrs"
|
||||||
"github.com/hashicorp/terraform/backend"
|
"github.com/hashicorp/terraform/backend"
|
||||||
|
@ -92,6 +93,9 @@ type Meta struct {
|
||||||
// When this channel is closed, the command will be cancelled.
|
// When this channel is closed, the command will be cancelled.
|
||||||
ShutdownCh <-chan struct{}
|
ShutdownCh <-chan struct{}
|
||||||
|
|
||||||
|
// UnmanagedProviders are a set of providers that exist as processes predating Terraform, which Terraform should use but not worry about the lifecycle of.
|
||||||
|
UnmanagedProviders map[addrs.Provider]*plugin.ReattachConfig
|
||||||
|
|
||||||
//----------------------------------------------------------
|
//----------------------------------------------------------
|
||||||
// Protected: commands can set these
|
// Protected: commands can set these
|
||||||
//----------------------------------------------------------
|
//----------------------------------------------------------
|
||||||
|
|
|
@ -63,6 +63,11 @@ func (m *Meta) providerInstallerCustomSource(source getproviders.Source) *provid
|
||||||
builtinProviderTypes = append(builtinProviderTypes, ty)
|
builtinProviderTypes = append(builtinProviderTypes, ty)
|
||||||
}
|
}
|
||||||
inst.SetBuiltInProviderTypes(builtinProviderTypes)
|
inst.SetBuiltInProviderTypes(builtinProviderTypes)
|
||||||
|
unmanagedProviderTypes := make(map[addrs.Provider]struct{}, len(m.UnmanagedProviders))
|
||||||
|
for ty := range m.UnmanagedProviders {
|
||||||
|
unmanagedProviderTypes[ty] = struct{}{}
|
||||||
|
}
|
||||||
|
inst.SetUnmanagedProviderTypes(unmanagedProviderTypes)
|
||||||
return inst
|
return inst
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,10 +177,13 @@ func (m *Meta) providerFactories() (map[addrs.Provider]providers.Factory, error)
|
||||||
// and they'll just be ignored if not used.
|
// and they'll just be ignored if not used.
|
||||||
internalFactories := m.internalProviders()
|
internalFactories := m.internalProviders()
|
||||||
|
|
||||||
factories := make(map[addrs.Provider]providers.Factory, len(selected)+len(internalFactories))
|
factories := make(map[addrs.Provider]providers.Factory, len(selected)+len(internalFactories)+len(m.UnmanagedProviders))
|
||||||
for name, factory := range internalFactories {
|
for name, factory := range internalFactories {
|
||||||
factories[addrs.NewBuiltInProvider(name)] = factory
|
factories[addrs.NewBuiltInProvider(name)] = factory
|
||||||
}
|
}
|
||||||
|
for provider, reattach := range m.UnmanagedProviders {
|
||||||
|
factories[provider] = unmanagedProviderFactory(provider, reattach)
|
||||||
|
}
|
||||||
for provider, cached := range selected {
|
for provider, cached := range selected {
|
||||||
factories[provider] = providerFactory(cached)
|
factories[provider] = providerFactory(cached)
|
||||||
}
|
}
|
||||||
|
@ -202,14 +210,15 @@ func providerFactory(meta *providercache.CachedProvider) providers.Factory {
|
||||||
})
|
})
|
||||||
|
|
||||||
config := &plugin.ClientConfig{
|
config := &plugin.ClientConfig{
|
||||||
Cmd: exec.Command(meta.ExecutableFile),
|
|
||||||
HandshakeConfig: tfplugin.Handshake,
|
HandshakeConfig: tfplugin.Handshake,
|
||||||
VersionedPlugins: tfplugin.VersionedPlugins,
|
|
||||||
Managed: true,
|
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC},
|
AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC},
|
||||||
|
Managed: true,
|
||||||
|
Cmd: exec.Command(meta.ExecutableFile),
|
||||||
AutoMTLS: enableProviderAutoMTLS,
|
AutoMTLS: enableProviderAutoMTLS,
|
||||||
|
VersionedPlugins: tfplugin.VersionedPlugins,
|
||||||
}
|
}
|
||||||
|
|
||||||
client := plugin.NewClient(config)
|
client := plugin.NewClient(config)
|
||||||
rpcClient, err := client.Client()
|
rpcClient, err := client.Client()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -224,6 +233,51 @@ func providerFactory(meta *providercache.CachedProvider) providers.Factory {
|
||||||
// store the client so that the plugin can kill the child process
|
// store the client so that the plugin can kill the child process
|
||||||
p := raw.(*tfplugin.GRPCProvider)
|
p := raw.(*tfplugin.GRPCProvider)
|
||||||
p.PluginClient = client
|
p.PluginClient = client
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unmanagedProviderFactory produces a provider factory that uses the passed
|
||||||
|
// reattach information to connect to go-plugin processes that are already
|
||||||
|
// running, and implements providers.Interface against it.
|
||||||
|
func unmanagedProviderFactory(provider addrs.Provider, reattach *plugin.ReattachConfig) providers.Factory {
|
||||||
|
return func() (providers.Interface, error) {
|
||||||
|
logger := hclog.New(&hclog.LoggerOptions{
|
||||||
|
Name: "unmanaged-plugin",
|
||||||
|
Level: hclog.Trace,
|
||||||
|
Output: os.Stderr,
|
||||||
|
})
|
||||||
|
|
||||||
|
config := &plugin.ClientConfig{
|
||||||
|
HandshakeConfig: tfplugin.Handshake,
|
||||||
|
Logger: logger,
|
||||||
|
AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC},
|
||||||
|
Managed: false,
|
||||||
|
Reattach: reattach,
|
||||||
|
}
|
||||||
|
// TODO: we probably shouldn't hardcode the protocol version
|
||||||
|
// here, but it'll do for now, because only one protocol
|
||||||
|
// version is supported. Eventually, we'll probably want to
|
||||||
|
// sneak it into the JSON ReattachConfigs.
|
||||||
|
if plugins, ok := tfplugin.VersionedPlugins[5]; !ok {
|
||||||
|
return nil, fmt.Errorf("no supported plugins for protocol 5")
|
||||||
|
} else {
|
||||||
|
config.Plugins = plugins
|
||||||
|
}
|
||||||
|
|
||||||
|
client := plugin.NewClient(config)
|
||||||
|
rpcClient, err := client.Client()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := rpcClient.Dispense(tfplugin.ProviderPluginName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p := raw.(*tfplugin.GRPCProvider)
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,11 @@ import (
|
||||||
|
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-plugin"
|
||||||
svchost "github.com/hashicorp/terraform-svchost"
|
svchost "github.com/hashicorp/terraform-svchost"
|
||||||
"github.com/hashicorp/terraform-svchost/auth"
|
"github.com/hashicorp/terraform-svchost/auth"
|
||||||
"github.com/hashicorp/terraform-svchost/disco"
|
"github.com/hashicorp/terraform-svchost/disco"
|
||||||
|
"github.com/hashicorp/terraform/addrs"
|
||||||
"github.com/hashicorp/terraform/command"
|
"github.com/hashicorp/terraform/command"
|
||||||
"github.com/hashicorp/terraform/command/cliconfig"
|
"github.com/hashicorp/terraform/command/cliconfig"
|
||||||
"github.com/hashicorp/terraform/command/webbrowser"
|
"github.com/hashicorp/terraform/command/webbrowser"
|
||||||
|
@ -38,7 +40,7 @@ const (
|
||||||
OutputPrefix = "o:"
|
OutputPrefix = "o:"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initCommands(config *cliconfig.Config, services *disco.Disco, providerSrc getproviders.Source) {
|
func initCommands(config *cliconfig.Config, services *disco.Disco, providerSrc getproviders.Source, unmanagedProviders map[addrs.Provider]*plugin.ReattachConfig) {
|
||||||
var inAutomation bool
|
var inAutomation bool
|
||||||
if v := os.Getenv(runningInAutomationEnvName); v != "" {
|
if v := os.Getenv(runningInAutomationEnvName); v != "" {
|
||||||
inAutomation = true
|
inAutomation = true
|
||||||
|
@ -77,6 +79,7 @@ func initCommands(config *cliconfig.Config, services *disco.Disco, providerSrc g
|
||||||
OverrideDataDir: dataDir,
|
OverrideDataDir: dataDir,
|
||||||
|
|
||||||
ShutdownCh: makeShutdownCh(),
|
ShutdownCh: makeShutdownCh(),
|
||||||
|
UnmanagedProviders: unmanagedProviders,
|
||||||
}
|
}
|
||||||
|
|
||||||
// The command list is included in the terraform -help
|
// The command list is included in the terraform -help
|
||||||
|
|
8
go.mod
8
go.mod
|
@ -4,7 +4,6 @@ require (
|
||||||
cloud.google.com/go v0.45.1
|
cloud.google.com/go v0.45.1
|
||||||
github.com/Azure/azure-sdk-for-go v40.3.0+incompatible
|
github.com/Azure/azure-sdk-for-go v40.3.0+incompatible
|
||||||
github.com/Azure/go-autorest/autorest v0.10.0
|
github.com/Azure/go-autorest/autorest v0.10.0
|
||||||
github.com/Unknwon/com v0.0.0-20151008135407-28b053d5a292 // indirect
|
|
||||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af // indirect
|
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af // indirect
|
||||||
github.com/agext/levenshtein v1.2.2
|
github.com/agext/levenshtein v1.2.2
|
||||||
github.com/agl/ed25519 v0.0.0-20150830182803-278e1ec8e8a6 // indirect
|
github.com/agl/ed25519 v0.0.0-20150830182803-278e1ec8e8a6 // indirect
|
||||||
|
@ -32,14 +31,13 @@ require (
|
||||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d // indirect
|
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d // indirect
|
||||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/dnaeon/go-vcr v0.0.0-20180920040454-5637cf3d8a31 // indirect
|
|
||||||
github.com/dylanmei/iso8601 v0.1.0 // indirect
|
github.com/dylanmei/iso8601 v0.1.0 // indirect
|
||||||
github.com/dylanmei/winrmtest v0.0.0-20190225150635-99b7fe2fddf1
|
github.com/dylanmei/winrmtest v0.0.0-20190225150635-99b7fe2fddf1
|
||||||
github.com/go-test/deep v1.0.3
|
github.com/go-test/deep v1.0.3
|
||||||
github.com/gogo/protobuf v1.2.0 // indirect
|
github.com/gogo/protobuf v1.2.0 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 // indirect
|
github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 // indirect
|
||||||
github.com/golang/mock v1.3.1
|
github.com/golang/mock v1.3.1
|
||||||
github.com/golang/protobuf v1.3.2
|
github.com/golang/protobuf v1.3.4
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
|
||||||
github.com/google/go-cmp v0.3.1
|
github.com/google/go-cmp v0.3.1
|
||||||
github.com/google/uuid v1.1.1
|
github.com/google/uuid v1.1.1
|
||||||
|
@ -61,7 +59,7 @@ require (
|
||||||
github.com/hashicorp/go-immutable-radix v0.0.0-20180129170900-7f3cd4390caa // indirect
|
github.com/hashicorp/go-immutable-radix v0.0.0-20180129170900-7f3cd4390caa // indirect
|
||||||
github.com/hashicorp/go-msgpack v0.5.4 // indirect
|
github.com/hashicorp/go-msgpack v0.5.4 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.0.0
|
github.com/hashicorp/go-multierror v1.0.0
|
||||||
github.com/hashicorp/go-plugin v1.0.1-0.20190610192547-a1bc61569a26
|
github.com/hashicorp/go-plugin v1.3.0
|
||||||
github.com/hashicorp/go-retryablehttp v0.5.2
|
github.com/hashicorp/go-retryablehttp v0.5.2
|
||||||
github.com/hashicorp/go-rootcerts v1.0.0
|
github.com/hashicorp/go-rootcerts v1.0.0
|
||||||
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 // indirect
|
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 // indirect
|
||||||
|
@ -135,7 +133,7 @@ require (
|
||||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9
|
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9
|
||||||
golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371
|
golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371
|
||||||
google.golang.org/api v0.9.0
|
google.golang.org/api v0.9.0
|
||||||
google.golang.org/grpc v1.21.1
|
google.golang.org/grpc v1.27.1
|
||||||
gopkg.in/ini.v1 v1.42.0 // indirect
|
gopkg.in/ini.v1 v1.42.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.2.7
|
gopkg.in/yaml.v2 v2.2.7
|
||||||
)
|
)
|
||||||
|
|
53
go.sum
53
go.sum
|
@ -46,7 +46,6 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
|
||||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022 h1:y8Gs8CzNfDF5AZvjr+5UyGQvQEBL7pwo+v+wX6q9JI8=
|
github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022 h1:y8Gs8CzNfDF5AZvjr+5UyGQvQEBL7pwo+v+wX6q9JI8=
|
||||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
|
github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
|
||||||
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
|
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
|
||||||
github.com/Unknwon/com v0.0.0-20151008135407-28b053d5a292/go.mod h1:KYCjqMOeHpNuTOiFQU6WEcTG7poCJrUs0YgyHNtn1no=
|
|
||||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14=
|
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14=
|
||||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
|
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
|
||||||
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||||
|
@ -87,7 +86,6 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
|
||||||
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
||||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
|
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
|
||||||
github.com/aws/aws-sdk-go v1.25.3 h1:uM16hIw9BotjZKMZlX05SN2EFtaWfi/NonPKIARiBLQ=
|
|
||||||
github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||||
github.com/aws/aws-sdk-go v1.30.12 h1:KrjyosZvkpJjcwMk0RNxMZewQ47v7+ZkbQDXjWsJMs8=
|
github.com/aws/aws-sdk-go v1.30.12 h1:KrjyosZvkpJjcwMk0RNxMZewQ47v7+ZkbQDXjWsJMs8=
|
||||||
github.com/aws/aws-sdk-go v1.30.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
github.com/aws/aws-sdk-go v1.30.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||||
|
@ -105,6 +103,7 @@ github.com/bmatcuk/doublestar v1.1.5 h1:2bNwBOmhyFEFcoB3tGvTD5xanq+4kyOZlB8wFYbM
|
||||||
github.com/bmatcuk/doublestar v1.1.5/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
|
github.com/bmatcuk/doublestar v1.1.5/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
|
||||||
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
|
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
|
||||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
|
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
|
||||||
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
@ -131,12 +130,12 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
|
||||||
github.com/dimchansky/utfbom v1.0.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
github.com/dimchansky/utfbom v1.0.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||||
github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4=
|
github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4=
|
||||||
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||||
github.com/dnaeon/go-vcr v0.0.0-20180920040454-5637cf3d8a31 h1:Dzuw9GtbmllUqEcoHfScT9YpKFUssSiZ5PgZkIGf/YQ=
|
|
||||||
github.com/dnaeon/go-vcr v0.0.0-20180920040454-5637cf3d8a31/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
|
||||||
github.com/dylanmei/iso8601 v0.1.0 h1:812NGQDBcqquTfH5Yeo7lwR0nzx/cKdsmf3qMjPURUI=
|
github.com/dylanmei/iso8601 v0.1.0 h1:812NGQDBcqquTfH5Yeo7lwR0nzx/cKdsmf3qMjPURUI=
|
||||||
github.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ=
|
github.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ=
|
||||||
github.com/dylanmei/winrmtest v0.0.0-20190225150635-99b7fe2fddf1 h1:r1oACdS2XYiAWcfF8BJXkoU8l1J71KehGR+d99yWEDA=
|
github.com/dylanmei/winrmtest v0.0.0-20190225150635-99b7fe2fddf1 h1:r1oACdS2XYiAWcfF8BJXkoU8l1J71KehGR+d99yWEDA=
|
||||||
github.com/dylanmei/winrmtest v0.0.0-20190225150635-99b7fe2fddf1/go.mod h1:lcy9/2gH1jn/VCLouHA6tOEwLoNVd4GW6zhuKLmHC2Y=
|
github.com/dylanmei/winrmtest v0.0.0-20190225150635-99b7fe2fddf1/go.mod h1:lcy9/2gH1jn/VCLouHA6tOEwLoNVd4GW6zhuKLmHC2Y=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||||
|
@ -162,15 +161,15 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU
|
||||||
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
|
||||||
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
@ -211,7 +210,6 @@ github.com/hashicorp/go-azure-helpers v0.10.0 h1:KhjDnQhCqEMKlt4yH00MCevJQPJ6LkH
|
||||||
github.com/hashicorp/go-azure-helpers v0.10.0/go.mod h1:YuAtHxm2v74s+IjQwUG88dHBJPd5jL+cXr5BGVzSKhE=
|
github.com/hashicorp/go-azure-helpers v0.10.0/go.mod h1:YuAtHxm2v74s+IjQwUG88dHBJPd5jL+cXr5BGVzSKhE=
|
||||||
github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU=
|
github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU=
|
||||||
github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=
|
github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
@ -226,8 +224,8 @@ github.com/hashicorp/go-msgpack v0.5.4 h1:SFT72YqIkOcLdWJUYcriVX7hbrZpwc/f7h8aW2
|
||||||
github.com/hashicorp/go-msgpack v0.5.4/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
github.com/hashicorp/go-msgpack v0.5.4/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
github.com/hashicorp/go-plugin v1.0.1-0.20190610192547-a1bc61569a26 h1:hRho44SAoNu1CBtn5r8Q9J3rCs4ZverWZ4R+UeeNuWM=
|
github.com/hashicorp/go-plugin v1.3.0 h1:4d/wJojzvHV1I4i/rrjVaeuyxWrLzDE1mDCyDy8fXS8=
|
||||||
github.com/hashicorp/go-plugin v1.0.1-0.20190610192547-a1bc61569a26/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
|
github.com/hashicorp/go-plugin v1.3.0/go.mod h1:F9eH4LrE/ZsRdbwhfjs9k9HoDUwAHnYtXdgmf1AVNs0=
|
||||||
github.com/hashicorp/go-retryablehttp v0.5.2 h1:AoISa4P4IsW0/m4T6St8Yw38gTl5GtBAgfkhYh1xAz4=
|
github.com/hashicorp/go-retryablehttp v0.5.2 h1:AoISa4P4IsW0/m4T6St8Yw38gTl5GtBAgfkhYh1xAz4=
|
||||||
github.com/hashicorp/go-retryablehttp v0.5.2/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
github.com/hashicorp/go-retryablehttp v0.5.2/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||||
github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
|
github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
|
||||||
|
@ -238,14 +236,11 @@ github.com/hashicorp/go-slug v0.4.1 h1:/jAo8dNuLgSImoLXaX7Od7QB4TfYCVPam+OpAt5bZ
|
||||||
github.com/hashicorp/go-slug v0.4.1/go.mod h1:I5tq5Lv0E2xcNXNkmx7BSfzi1PsJ2cNjs3cC3LwyhK8=
|
github.com/hashicorp/go-slug v0.4.1/go.mod h1:I5tq5Lv0E2xcNXNkmx7BSfzi1PsJ2cNjs3cC3LwyhK8=
|
||||||
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 h1:7YOlAIO2YWnJZkQp7B5eFykaIY7C9JndqAFQyVV5BhM=
|
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 h1:7YOlAIO2YWnJZkQp7B5eFykaIY7C9JndqAFQyVV5BhM=
|
||||||
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
github.com/hashicorp/go-tfe v0.8.0 h1:kz3x3tbIKRkEAzKg05P/qbFY88fkEU7TiSX3w8xUrmE=
|
|
||||||
github.com/hashicorp/go-tfe v0.8.0/go.mod h1:XAV72S4O1iP8BDaqiaPLmL2B4EE6almocnOn8E8stHc=
|
|
||||||
github.com/hashicorp/go-tfe v0.8.1 h1:J6ulpLaKPHrcnwudRjxvlMYIGzqQFlnPhg3SVFh5N4E=
|
github.com/hashicorp/go-tfe v0.8.1 h1:J6ulpLaKPHrcnwudRjxvlMYIGzqQFlnPhg3SVFh5N4E=
|
||||||
github.com/hashicorp/go-tfe v0.8.1/go.mod h1:XAV72S4O1iP8BDaqiaPLmL2B4EE6almocnOn8E8stHc=
|
github.com/hashicorp/go-tfe v0.8.1/go.mod h1:XAV72S4O1iP8BDaqiaPLmL2B4EE6almocnOn8E8stHc=
|
||||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
|
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
|
||||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
|
|
||||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
|
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
|
||||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
|
@ -271,8 +266,9 @@ github.com/hashicorp/vault v0.10.4 h1:4x0lHxui/ZRp/B3E0Auv1QNBJpzETqHR2kQD3mHSBJ
|
||||||
github.com/hashicorp/vault v0.10.4/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0=
|
github.com/hashicorp/vault v0.10.4/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0=
|
||||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
|
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
|
||||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||||
|
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
|
||||||
|
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
|
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
|
||||||
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
||||||
|
@ -375,7 +371,6 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98=
|
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98=
|
||||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
@ -388,11 +383,10 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
|
||||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA=
|
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA=
|
||||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f h1:BVwpUVJDADN2ufcGik7W992pyps0wZ888b/y9GXcLTU=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU=
|
github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU=
|
||||||
github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU=
|
|
||||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
|
||||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1 h1:/K3IL0Z1quvmJ7X0A1AwNEK7CRkVK3YwfOU/QAL4WGg=
|
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1 h1:/K3IL0Z1quvmJ7X0A1AwNEK7CRkVK3YwfOU/QAL4WGg=
|
||||||
|
@ -415,12 +409,10 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k
|
||||||
github.com/spf13/afero v1.2.1 h1:qgMbHoJbPbw579P+1zVY+6n4nIFuIchaIjzZ/I/Yq8M=
|
github.com/spf13/afero v1.2.1 h1:qgMbHoJbPbw579P+1zVY+6n4nIFuIchaIjzZ/I/Yq8M=
|
||||||
github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||||
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
@ -432,16 +424,10 @@ github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c h1:iRD1Cq
|
||||||
github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c/go.mod h1:wk2XFUg6egk4tSDNZtXeKfe2G6690UVyt163PuUxBZk=
|
github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c/go.mod h1:wk2XFUg6egk4tSDNZtXeKfe2G6690UVyt163PuUxBZk=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/tombuildsstuff/giovanni v0.10.0 h1:XqZBPVD2hETa30FFdMz/zVfnidMnUrIMMmKIH7hWnWA=
|
|
||||||
github.com/tombuildsstuff/giovanni v0.10.0/go.mod h1:WwPhFP2+WnhJzvPYDnsyBab2wOIksMX6xm+Tg+jVvKw=
|
|
||||||
github.com/tombuildsstuff/giovanni v0.10.1 h1:AWfooo7kvVgfpjdp+NF/WDM5Pbkt4KMSv/h57t3DNRU=
|
github.com/tombuildsstuff/giovanni v0.10.1 h1:AWfooo7kvVgfpjdp+NF/WDM5Pbkt4KMSv/h57t3DNRU=
|
||||||
github.com/tombuildsstuff/giovanni v0.10.1/go.mod h1:WwPhFP2+WnhJzvPYDnsyBab2wOIksMX6xm+Tg+jVvKw=
|
github.com/tombuildsstuff/giovanni v0.10.1/go.mod h1:WwPhFP2+WnhJzvPYDnsyBab2wOIksMX6xm+Tg+jVvKw=
|
||||||
github.com/tombuildsstuff/go-autorest v14.0.1-0.20200317095413-f2d2d0252c3c+incompatible h1:H+ytGDYKp8E7Mb0GBK3LMaJd9O2lUo2Cmb4sTMHM3N8=
|
|
||||||
github.com/tombuildsstuff/go-autorest v14.0.1-0.20200317095413-f2d2d0252c3c+incompatible/go.mod h1:OVwh0+NZeL2RTqclVEX+p20Qys7Ihpd52PD0eqFDXtY=
|
|
||||||
github.com/tombuildsstuff/go-autorest v14.0.1-0.20200416184303-d4e299a3c04a+incompatible h1:9645FYqYopS+TFknygW7EC9PCbIC5T4WvWUpktyE2JA=
|
github.com/tombuildsstuff/go-autorest v14.0.1-0.20200416184303-d4e299a3c04a+incompatible h1:9645FYqYopS+TFknygW7EC9PCbIC5T4WvWUpktyE2JA=
|
||||||
github.com/tombuildsstuff/go-autorest v14.0.1-0.20200416184303-d4e299a3c04a+incompatible/go.mod h1:OVwh0+NZeL2RTqclVEX+p20Qys7Ihpd52PD0eqFDXtY=
|
github.com/tombuildsstuff/go-autorest v14.0.1-0.20200416184303-d4e299a3c04a+incompatible/go.mod h1:OVwh0+NZeL2RTqclVEX+p20Qys7Ihpd52PD0eqFDXtY=
|
||||||
github.com/tombuildsstuff/go-autorest/autorest v0.10.1-0.20200317095413-f2d2d0252c3c h1:9KiivLYWWeGGT2jzfB5T5Tj/gsSWf1KgbDwD9H7rrWM=
|
|
||||||
github.com/tombuildsstuff/go-autorest/autorest v0.10.1-0.20200317095413-f2d2d0252c3c/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
|
|
||||||
github.com/tombuildsstuff/go-autorest/autorest v0.10.1-0.20200416184303-d4e299a3c04a h1:F/4zKpn8ra3rhPMBzrVc7LYL1GB1ucl/va4I+4ubUWg=
|
github.com/tombuildsstuff/go-autorest/autorest v0.10.1-0.20200416184303-d4e299a3c04a h1:F/4zKpn8ra3rhPMBzrVc7LYL1GB1ucl/va4I+4ubUWg=
|
||||||
github.com/tombuildsstuff/go-autorest/autorest v0.10.1-0.20200416184303-d4e299a3c04a/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
|
github.com/tombuildsstuff/go-autorest/autorest v0.10.1-0.20200416184303-d4e299a3c04a/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
|
||||||
github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5 h1:cMjKdf4PxEBN9K5HaD9UMW8gkTbM0kMzkTa9SJe0WNQ=
|
github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5 h1:cMjKdf4PxEBN9K5HaD9UMW8gkTbM0kMzkTa9SJe0WNQ=
|
||||||
|
@ -458,12 +444,8 @@ github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q
|
||||||
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557 h1:Jpn2j6wHkC9wJv5iMfJhKqrZJx3TahFx+7sbZ7zQdxs=
|
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557 h1:Jpn2j6wHkC9wJv5iMfJhKqrZJx3TahFx+7sbZ7zQdxs=
|
||||||
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||||
github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||||
github.com/zclconf/go-cty v1.1.0 h1:uJwc9HiBOCpoKIObTQaLR+tsEXx1HBHnOsOOpcdhZgw=
|
|
||||||
github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||||
github.com/zclconf/go-cty v1.2.0 h1:sPHsy7ADcIZQP3vILvTjrh74ZA175TFP5vqiNK1UmlI=
|
|
||||||
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
|
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
|
||||||
github.com/zclconf/go-cty v1.4.0 h1:+q+tmgyUB94HIdH/uVTIi/+kt3pt4sHwEZAcTyLoGsQ=
|
|
||||||
github.com/zclconf/go-cty v1.4.0/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ=
|
|
||||||
github.com/zclconf/go-cty v1.4.1 h1:Xzr4m4utRDhHDifag1onwwUSq32HLoLBsp+w6tD0880=
|
github.com/zclconf/go-cty v1.4.1 h1:Xzr4m4utRDhHDifag1onwwUSq32HLoLBsp+w6tD0880=
|
||||||
github.com/zclconf/go-cty v1.4.1/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ=
|
github.com/zclconf/go-cty v1.4.1/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ=
|
||||||
github.com/zclconf/go-cty-yaml v1.0.1 h1:up11wlgAaDvlAGENcFDnZgkn0qUJurso7k6EpURKNF8=
|
github.com/zclconf/go-cty-yaml v1.0.1 h1:up11wlgAaDvlAGENcFDnZgkn0qUJurso7k6EpURKNF8=
|
||||||
|
@ -484,7 +466,6 @@ golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnf
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
@ -501,6 +482,7 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl
|
||||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@ -534,7 +516,6 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -563,6 +544,7 @@ golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
@ -580,6 +562,7 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
|
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
|
||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
|
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
@ -588,14 +571,17 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn
|
||||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
|
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
|
||||||
|
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||||
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
|
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
|
||||||
|
@ -610,4 +596,5 @@ gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
|
|
|
@ -41,6 +41,12 @@ type Installer struct {
|
||||||
// namespace, which we use for providers that are built in to Terraform
|
// namespace, which we use for providers that are built in to Terraform
|
||||||
// and thus do not need any separate installation step.
|
// and thus do not need any separate installation step.
|
||||||
builtInProviderTypes []string
|
builtInProviderTypes []string
|
||||||
|
|
||||||
|
// unmanagedProviderTypes is a set of provider addresses that should be
|
||||||
|
// considered implemented, but that Terraform does not manage the
|
||||||
|
// lifecycle for, and therefore does not need to worry about the
|
||||||
|
// installation of.
|
||||||
|
unmanagedProviderTypes map[addrs.Provider]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInstaller constructs and returns a new installer with the given target
|
// NewInstaller constructs and returns a new installer with the given target
|
||||||
|
@ -94,6 +100,16 @@ func (i *Installer) SetBuiltInProviderTypes(types []string) {
|
||||||
i.builtInProviderTypes = types
|
i.builtInProviderTypes = types
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetUnmanagedProviderTypes tells the receiver to consider the providers
|
||||||
|
// indicated by the passed addrs.Providers as unmanaged. Terraform does not
|
||||||
|
// need to control the lifecycle of these providers, and they are assumed to be
|
||||||
|
// running already when Terraform is started. Because these are essentially
|
||||||
|
// processes, not binaries, Terraform will not do any work to ensure presence
|
||||||
|
// or versioning of these binaries.
|
||||||
|
func (i *Installer) SetUnmanagedProviderTypes(types map[addrs.Provider]struct{}) {
|
||||||
|
i.unmanagedProviderTypes = types
|
||||||
|
}
|
||||||
|
|
||||||
// EnsureProviderVersions compares the given provider requirements with what
|
// EnsureProviderVersions compares the given provider requirements with what
|
||||||
// is already available in the installer's target directory and then takes
|
// is already available in the installer's target directory and then takes
|
||||||
// appropriate installation actions to ensure that suitable packages
|
// appropriate installation actions to ensure that suitable packages
|
||||||
|
@ -173,6 +189,10 @@ MightNeedProvider:
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if _, ok := i.unmanagedProviderTypes[provider]; ok {
|
||||||
|
// unmanaged providers do not require installation
|
||||||
|
continue
|
||||||
|
}
|
||||||
acceptableVersions := versions.MeetingConstraints(versionConstraints)
|
acceptableVersions := versions.MeetingConstraints(versionConstraints)
|
||||||
if mode.forceQueryAllProviders() {
|
if mode.forceQueryAllProviders() {
|
||||||
// If our mode calls for us to look for newer versions regardless
|
// If our mode calls for us to look for newer versions regardless
|
||||||
|
|
64
main.go
64
main.go
|
@ -1,10 +1,12 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -13,6 +15,7 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/go-plugin"
|
"github.com/hashicorp/go-plugin"
|
||||||
"github.com/hashicorp/terraform-svchost/disco"
|
"github.com/hashicorp/terraform-svchost/disco"
|
||||||
|
"github.com/hashicorp/terraform/addrs"
|
||||||
"github.com/hashicorp/terraform/command/cliconfig"
|
"github.com/hashicorp/terraform/command/cliconfig"
|
||||||
"github.com/hashicorp/terraform/command/format"
|
"github.com/hashicorp/terraform/command/format"
|
||||||
"github.com/hashicorp/terraform/helper/logging"
|
"github.com/hashicorp/terraform/helper/logging"
|
||||||
|
@ -182,12 +185,21 @@ func wrappedMain() int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The user can declare that certain providers are being managed on
|
||||||
|
// Terraform's behalf using this environment variable. Thsi is used
|
||||||
|
// primarily by the SDK's acceptance testing framework.
|
||||||
|
unmanagedProviders, err := parseReattachProviders(os.Getenv("TF_REATTACH_PROVIDERS"))
|
||||||
|
if err != nil {
|
||||||
|
Ui.Error(err.Error())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize the backends.
|
// Initialize the backends.
|
||||||
backendInit.Init(services)
|
backendInit.Init(services)
|
||||||
|
|
||||||
// In tests, Commands may already be set to provide mock commands
|
// In tests, Commands may already be set to provide mock commands
|
||||||
if Commands == nil {
|
if Commands == nil {
|
||||||
initCommands(config, services, providerSrc)
|
initCommands(config, services, providerSrc, unmanagedProviders)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run checkpoint
|
// Run checkpoint
|
||||||
|
@ -365,3 +377,53 @@ func mergeEnvArgs(envName string, cmd string, args []string) ([]string, error) {
|
||||||
copy(newArgs[len(extra)+idx:], args[idx:])
|
copy(newArgs[len(extra)+idx:], args[idx:])
|
||||||
return newArgs, nil
|
return newArgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse information on reattaching to unmanaged providers out of a
|
||||||
|
// JSON-encoded environment variable.
|
||||||
|
func parseReattachProviders(in string) (map[addrs.Provider]*plugin.ReattachConfig, error) {
|
||||||
|
unmanagedProviders := map[addrs.Provider]*plugin.ReattachConfig{}
|
||||||
|
if in != "" {
|
||||||
|
type reattachConfig struct {
|
||||||
|
Protocol string
|
||||||
|
Addr struct {
|
||||||
|
Network string
|
||||||
|
String string
|
||||||
|
}
|
||||||
|
Pid int
|
||||||
|
Test bool
|
||||||
|
}
|
||||||
|
var m map[string]reattachConfig
|
||||||
|
err := json.Unmarshal([]byte(in), &m)
|
||||||
|
if err != nil {
|
||||||
|
return unmanagedProviders, fmt.Errorf("Invalid format for TF_REATTACH_PROVIDERS: %w", err)
|
||||||
|
}
|
||||||
|
for p, c := range m {
|
||||||
|
a, diags := addrs.ParseProviderSourceString(p)
|
||||||
|
if diags.HasErrors() {
|
||||||
|
return unmanagedProviders, fmt.Errorf("Error parsing %q as a provider address: %w", a, diags.Err())
|
||||||
|
}
|
||||||
|
var addr net.Addr
|
||||||
|
switch c.Addr.Network {
|
||||||
|
case "unix":
|
||||||
|
addr, err = net.ResolveUnixAddr("unix", c.Addr.String)
|
||||||
|
if err != nil {
|
||||||
|
return unmanagedProviders, fmt.Errorf("Invalid unix socket path %q for %q: %w", c.Addr.String, p, err)
|
||||||
|
}
|
||||||
|
case "tcp":
|
||||||
|
addr, err = net.ResolveTCPAddr("tcp", c.Addr.String)
|
||||||
|
if err != nil {
|
||||||
|
return unmanagedProviders, fmt.Errorf("Invalid TCP address %q for %q: %w", c.Addr.String, p, err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return unmanagedProviders, fmt.Errorf("Unknown address type %q for %q", c.Addr.Network, p)
|
||||||
|
}
|
||||||
|
unmanagedProviders[a] = &plugin.ReattachConfig{
|
||||||
|
Protocol: plugin.Protocol(c.Protocol),
|
||||||
|
Pid: c.Pid,
|
||||||
|
Test: c.Test,
|
||||||
|
Addr: addr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unmanagedProviders, nil
|
||||||
|
}
|
||||||
|
|
|
@ -393,7 +393,7 @@ func (p *Buffer) Bytes() []byte { return p.buf }
|
||||||
// than relying on this API.
|
// than relying on this API.
|
||||||
//
|
//
|
||||||
// If deterministic serialization is requested, map entries will be sorted
|
// If deterministic serialization is requested, map entries will be sorted
|
||||||
// by keys in lexographical order. This is an implementation detail and
|
// by keys in lexicographical order. This is an implementation detail and
|
||||||
// subject to change.
|
// subject to change.
|
||||||
func (p *Buffer) SetDeterministic(deterministic bool) {
|
func (p *Buffer) SetDeterministic(deterministic bool) {
|
||||||
p.deterministic = deterministic
|
p.deterministic = deterministic
|
||||||
|
|
|
@ -456,6 +456,8 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
|
||||||
|
|
||||||
// writeAny writes an arbitrary field.
|
// writeAny writes an arbitrary field.
|
||||||
func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
|
func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
|
||||||
v = reflect.Indirect(v)
|
v = reflect.Indirect(v)
|
||||||
|
@ -519,8 +521,8 @@ func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Propert
|
||||||
// mutating this value.
|
// mutating this value.
|
||||||
v = v.Addr()
|
v = v.Addr()
|
||||||
}
|
}
|
||||||
if etm, ok := v.Interface().(encoding.TextMarshaler); ok {
|
if v.Type().Implements(textMarshalerType) {
|
||||||
text, err := etm.MarshalText()
|
text, err := v.Interface().(encoding.TextMarshaler).MarshalText()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1376,8 +1376,8 @@ type FileOptions struct {
|
||||||
// determining the namespace.
|
// determining the namespace.
|
||||||
PhpNamespace *string `protobuf:"bytes,41,opt,name=php_namespace,json=phpNamespace" json:"php_namespace,omitempty"`
|
PhpNamespace *string `protobuf:"bytes,41,opt,name=php_namespace,json=phpNamespace" json:"php_namespace,omitempty"`
|
||||||
// Use this option to change the namespace of php generated metadata classes.
|
// Use this option to change the namespace of php generated metadata classes.
|
||||||
// Default is empty. When this option is empty, the proto file name will be used
|
// Default is empty. When this option is empty, the proto file name will be
|
||||||
// for determining the namespace.
|
// used for determining the namespace.
|
||||||
PhpMetadataNamespace *string `protobuf:"bytes,44,opt,name=php_metadata_namespace,json=phpMetadataNamespace" json:"php_metadata_namespace,omitempty"`
|
PhpMetadataNamespace *string `protobuf:"bytes,44,opt,name=php_metadata_namespace,json=phpMetadataNamespace" json:"php_metadata_namespace,omitempty"`
|
||||||
// Use this option to change the package of ruby generated classes. Default
|
// Use this option to change the package of ruby generated classes. Default
|
||||||
// is empty. When this option is not set, the package name will be used for
|
// is empty. When this option is not set, the package name will be used for
|
||||||
|
@ -1627,7 +1627,7 @@ type MessageOptions struct {
|
||||||
//
|
//
|
||||||
// Implementations may choose not to generate the map_entry=true message, but
|
// Implementations may choose not to generate the map_entry=true message, but
|
||||||
// use a native map in the target language to hold the keys and values.
|
// use a native map in the target language to hold the keys and values.
|
||||||
// The reflection APIs in such implementions still need to work as
|
// The reflection APIs in such implementations still need to work as
|
||||||
// if the field is a repeated message field.
|
// if the field is a repeated message field.
|
||||||
//
|
//
|
||||||
// NOTE: Do not set the option in .proto files. Always use the maps syntax
|
// NOTE: Do not set the option in .proto files. Always use the maps syntax
|
||||||
|
@ -2377,7 +2377,7 @@ type SourceCodeInfo struct {
|
||||||
// beginning of the "extend" block and is shared by all extensions within
|
// beginning of the "extend" block and is shared by all extensions within
|
||||||
// the block.
|
// the block.
|
||||||
// - Just because a location's span is a subset of some other location's span
|
// - Just because a location's span is a subset of some other location's span
|
||||||
// does not mean that it is a descendent. For example, a "group" defines
|
// does not mean that it is a descendant. For example, a "group" defines
|
||||||
// both a type and a field in a single declaration. Thus, the locations
|
// both a type and a field in a single declaration. Thus, the locations
|
||||||
// corresponding to the type and field and their components will overlap.
|
// corresponding to the type and field and their components will overlap.
|
||||||
// - Code which tries to interpret locations should probably be designed to
|
// - Code which tries to interpret locations should probably be designed to
|
||||||
|
@ -2718,7 +2718,9 @@ func init() {
|
||||||
proto.RegisterType((*GeneratedCodeInfo_Annotation)(nil), "google.protobuf.GeneratedCodeInfo.Annotation")
|
proto.RegisterType((*GeneratedCodeInfo_Annotation)(nil), "google.protobuf.GeneratedCodeInfo.Annotation")
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { proto.RegisterFile("google/protobuf/descriptor.proto", fileDescriptor_e5baabe45344a177) }
|
func init() {
|
||||||
|
proto.RegisterFile("google/protobuf/descriptor.proto", fileDescriptor_e5baabe45344a177)
|
||||||
|
}
|
||||||
|
|
||||||
var fileDescriptor_e5baabe45344a177 = []byte{
|
var fileDescriptor_e5baabe45344a177 = []byte{
|
||||||
// 2589 bytes of a gzipped FileDescriptorProto
|
// 2589 bytes of a gzipped FileDescriptorProto
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
syntax = "proto2";
|
syntax = "proto2";
|
||||||
|
|
||||||
package google.protobuf;
|
package google.protobuf;
|
||||||
|
|
||||||
option go_package = "github.com/golang/protobuf/protoc-gen-go/descriptor;descriptor";
|
option go_package = "github.com/golang/protobuf/protoc-gen-go/descriptor;descriptor";
|
||||||
option java_package = "com.google.protobuf";
|
option java_package = "com.google.protobuf";
|
||||||
option java_outer_classname = "DescriptorProtos";
|
option java_outer_classname = "DescriptorProtos";
|
||||||
|
@ -100,8 +101,8 @@ message DescriptorProto {
|
||||||
repeated EnumDescriptorProto enum_type = 4;
|
repeated EnumDescriptorProto enum_type = 4;
|
||||||
|
|
||||||
message ExtensionRange {
|
message ExtensionRange {
|
||||||
optional int32 start = 1;
|
optional int32 start = 1; // Inclusive.
|
||||||
optional int32 end = 2;
|
optional int32 end = 2; // Exclusive.
|
||||||
|
|
||||||
optional ExtensionRangeOptions options = 3;
|
optional ExtensionRangeOptions options = 3;
|
||||||
}
|
}
|
||||||
|
@ -165,14 +166,14 @@ message FieldDescriptorProto {
|
||||||
TYPE_SFIXED64 = 16;
|
TYPE_SFIXED64 = 16;
|
||||||
TYPE_SINT32 = 17; // Uses ZigZag encoding.
|
TYPE_SINT32 = 17; // Uses ZigZag encoding.
|
||||||
TYPE_SINT64 = 18; // Uses ZigZag encoding.
|
TYPE_SINT64 = 18; // Uses ZigZag encoding.
|
||||||
};
|
}
|
||||||
|
|
||||||
enum Label {
|
enum Label {
|
||||||
// 0 is reserved for errors
|
// 0 is reserved for errors
|
||||||
LABEL_OPTIONAL = 1;
|
LABEL_OPTIONAL = 1;
|
||||||
LABEL_REQUIRED = 2;
|
LABEL_REQUIRED = 2;
|
||||||
LABEL_REPEATED = 3;
|
LABEL_REPEATED = 3;
|
||||||
};
|
}
|
||||||
|
|
||||||
optional string name = 1;
|
optional string name = 1;
|
||||||
optional int32 number = 3;
|
optional int32 number = 3;
|
||||||
|
@ -314,7 +315,6 @@ message MethodDescriptorProto {
|
||||||
// If this turns out to be popular, a web service will be set up
|
// If this turns out to be popular, a web service will be set up
|
||||||
// to automatically assign option numbers.
|
// to automatically assign option numbers.
|
||||||
|
|
||||||
|
|
||||||
message FileOptions {
|
message FileOptions {
|
||||||
|
|
||||||
// Sets the Java package where classes generated from this .proto will be
|
// Sets the Java package where classes generated from this .proto will be
|
||||||
|
@ -369,6 +369,7 @@ message FileOptions {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Should generic services be generated in each language? "Generic" services
|
// Should generic services be generated in each language? "Generic" services
|
||||||
// are not specific to any particular RPC system. They are generated by the
|
// are not specific to any particular RPC system. They are generated by the
|
||||||
// main code generators in each language (without additional plugins).
|
// main code generators in each language (without additional plugins).
|
||||||
|
@ -417,10 +418,9 @@ message FileOptions {
|
||||||
// determining the namespace.
|
// determining the namespace.
|
||||||
optional string php_namespace = 41;
|
optional string php_namespace = 41;
|
||||||
|
|
||||||
|
|
||||||
// Use this option to change the namespace of php generated metadata classes.
|
// Use this option to change the namespace of php generated metadata classes.
|
||||||
// Default is empty. When this option is empty, the proto file name will be used
|
// Default is empty. When this option is empty, the proto file name will be
|
||||||
// for determining the namespace.
|
// used for determining the namespace.
|
||||||
optional string php_metadata_namespace = 44;
|
optional string php_metadata_namespace = 44;
|
||||||
|
|
||||||
// Use this option to change the package of ruby generated classes. Default
|
// Use this option to change the package of ruby generated classes. Default
|
||||||
|
@ -428,6 +428,7 @@ message FileOptions {
|
||||||
// determining the ruby package.
|
// determining the ruby package.
|
||||||
optional string ruby_package = 45;
|
optional string ruby_package = 45;
|
||||||
|
|
||||||
|
|
||||||
// The parser stores options it doesn't recognize here.
|
// The parser stores options it doesn't recognize here.
|
||||||
// See the documentation for the "Options" section above.
|
// See the documentation for the "Options" section above.
|
||||||
repeated UninterpretedOption uninterpreted_option = 999;
|
repeated UninterpretedOption uninterpreted_option = 999;
|
||||||
|
@ -486,7 +487,7 @@ message MessageOptions {
|
||||||
//
|
//
|
||||||
// Implementations may choose not to generate the map_entry=true message, but
|
// Implementations may choose not to generate the map_entry=true message, but
|
||||||
// use a native map in the target language to hold the keys and values.
|
// use a native map in the target language to hold the keys and values.
|
||||||
// The reflection APIs in such implementions still need to work as
|
// The reflection APIs in such implementations still need to work as
|
||||||
// if the field is a repeated message field.
|
// if the field is a repeated message field.
|
||||||
//
|
//
|
||||||
// NOTE: Do not set the option in .proto files. Always use the maps syntax
|
// NOTE: Do not set the option in .proto files. Always use the maps syntax
|
||||||
|
@ -497,6 +498,7 @@ message MessageOptions {
|
||||||
reserved 8; // javalite_serializable
|
reserved 8; // javalite_serializable
|
||||||
reserved 9; // javanano_as_lite
|
reserved 9; // javanano_as_lite
|
||||||
|
|
||||||
|
|
||||||
// The parser stores options it doesn't recognize here. See above.
|
// The parser stores options it doesn't recognize here. See above.
|
||||||
repeated UninterpretedOption uninterpreted_option = 999;
|
repeated UninterpretedOption uninterpreted_option = 999;
|
||||||
|
|
||||||
|
@ -681,8 +683,8 @@ message MethodOptions {
|
||||||
NO_SIDE_EFFECTS = 1; // implies idempotent
|
NO_SIDE_EFFECTS = 1; // implies idempotent
|
||||||
IDEMPOTENT = 2; // idempotent, but may have side effects
|
IDEMPOTENT = 2; // idempotent, but may have side effects
|
||||||
}
|
}
|
||||||
optional IdempotencyLevel idempotency_level =
|
optional IdempotencyLevel idempotency_level = 34
|
||||||
34 [default=IDEMPOTENCY_UNKNOWN];
|
[default = IDEMPOTENCY_UNKNOWN];
|
||||||
|
|
||||||
// The parser stores options it doesn't recognize here. See above.
|
// The parser stores options it doesn't recognize here. See above.
|
||||||
repeated UninterpretedOption uninterpreted_option = 999;
|
repeated UninterpretedOption uninterpreted_option = 999;
|
||||||
|
@ -763,7 +765,7 @@ message SourceCodeInfo {
|
||||||
// beginning of the "extend" block and is shared by all extensions within
|
// beginning of the "extend" block and is shared by all extensions within
|
||||||
// the block.
|
// the block.
|
||||||
// - Just because a location's span is a subset of some other location's span
|
// - Just because a location's span is a subset of some other location's span
|
||||||
// does not mean that it is a descendent. For example, a "group" defines
|
// does not mean that it is a descendant. For example, a "group" defines
|
||||||
// both a type and a field in a single declaration. Thus, the locations
|
// both a type and a field in a single declaration. Thus, the locations
|
||||||
// corresponding to the type and field and their components will overlap.
|
// corresponding to the type and field and their components will overlap.
|
||||||
// - Code which tries to interpret locations should probably be designed to
|
// - Code which tries to interpret locations should probably be designed to
|
||||||
|
|
|
@ -102,7 +102,8 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
//
|
//
|
||||||
type Any struct {
|
type Any struct {
|
||||||
// A URL/resource name that uniquely identifies the type of the serialized
|
// A URL/resource name that uniquely identifies the type of the serialized
|
||||||
// protocol buffer message. The last segment of the URL's path must represent
|
// protocol buffer message. This string must contain at least
|
||||||
|
// one "/" character. The last segment of the URL's path must represent
|
||||||
// the fully qualified name of the type (as in
|
// the fully qualified name of the type (as in
|
||||||
// `path/google.protobuf.Duration`). The name should be in a canonical form
|
// `path/google.protobuf.Duration`). The name should be in a canonical form
|
||||||
// (e.g., leading "." is not accepted).
|
// (e.g., leading "." is not accepted).
|
||||||
|
@ -181,7 +182,9 @@ func init() {
|
||||||
proto.RegisterType((*Any)(nil), "google.protobuf.Any")
|
proto.RegisterType((*Any)(nil), "google.protobuf.Any")
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { proto.RegisterFile("google/protobuf/any.proto", fileDescriptor_b53526c13ae22eb4) }
|
func init() {
|
||||||
|
proto.RegisterFile("google/protobuf/any.proto", fileDescriptor_b53526c13ae22eb4)
|
||||||
|
}
|
||||||
|
|
||||||
var fileDescriptor_b53526c13ae22eb4 = []byte{
|
var fileDescriptor_b53526c13ae22eb4 = []byte{
|
||||||
// 185 bytes of a gzipped FileDescriptorProto
|
// 185 bytes of a gzipped FileDescriptorProto
|
||||||
|
|
|
@ -121,7 +121,8 @@ option objc_class_prefix = "GPB";
|
||||||
//
|
//
|
||||||
message Any {
|
message Any {
|
||||||
// A URL/resource name that uniquely identifies the type of the serialized
|
// A URL/resource name that uniquely identifies the type of the serialized
|
||||||
// protocol buffer message. The last segment of the URL's path must represent
|
// protocol buffer message. This string must contain at least
|
||||||
|
// one "/" character. The last segment of the URL's path must represent
|
||||||
// the fully qualified name of the type (as in
|
// the fully qualified name of the type (as in
|
||||||
// `path/google.protobuf.Duration`). The name should be in a canonical form
|
// `path/google.protobuf.Duration`). The name should be in a canonical form
|
||||||
// (e.g., leading "." is not accepted).
|
// (e.g., leading "." is not accepted).
|
||||||
|
|
|
@ -41,7 +41,7 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
// if (duration.seconds < 0 && duration.nanos > 0) {
|
// if (duration.seconds < 0 && duration.nanos > 0) {
|
||||||
// duration.seconds += 1;
|
// duration.seconds += 1;
|
||||||
// duration.nanos -= 1000000000;
|
// duration.nanos -= 1000000000;
|
||||||
// } else if (durations.seconds > 0 && duration.nanos < 0) {
|
// } else if (duration.seconds > 0 && duration.nanos < 0) {
|
||||||
// duration.seconds -= 1;
|
// duration.seconds -= 1;
|
||||||
// duration.nanos += 1000000000;
|
// duration.nanos += 1000000000;
|
||||||
// }
|
// }
|
||||||
|
@ -142,7 +142,9 @@ func init() {
|
||||||
proto.RegisterType((*Duration)(nil), "google.protobuf.Duration")
|
proto.RegisterType((*Duration)(nil), "google.protobuf.Duration")
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { proto.RegisterFile("google/protobuf/duration.proto", fileDescriptor_23597b2ebd7ac6c5) }
|
func init() {
|
||||||
|
proto.RegisterFile("google/protobuf/duration.proto", fileDescriptor_23597b2ebd7ac6c5)
|
||||||
|
}
|
||||||
|
|
||||||
var fileDescriptor_23597b2ebd7ac6c5 = []byte{
|
var fileDescriptor_23597b2ebd7ac6c5 = []byte{
|
||||||
// 190 bytes of a gzipped FileDescriptorProto
|
// 190 bytes of a gzipped FileDescriptorProto
|
||||||
|
|
|
@ -61,7 +61,7 @@ option objc_class_prefix = "GPB";
|
||||||
// if (duration.seconds < 0 && duration.nanos > 0) {
|
// if (duration.seconds < 0 && duration.nanos > 0) {
|
||||||
// duration.seconds += 1;
|
// duration.seconds += 1;
|
||||||
// duration.nanos -= 1000000000;
|
// duration.nanos -= 1000000000;
|
||||||
// } else if (durations.seconds > 0 && duration.nanos < 0) {
|
// } else if (duration.seconds > 0 && duration.nanos < 0) {
|
||||||
// duration.seconds -= 1;
|
// duration.seconds -= 1;
|
||||||
// duration.nanos += 1000000000;
|
// duration.nanos += 1000000000;
|
||||||
// }
|
// }
|
||||||
|
@ -101,7 +101,6 @@ option objc_class_prefix = "GPB";
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
message Duration {
|
message Duration {
|
||||||
|
|
||||||
// Signed seconds of the span of time. Must be from -315,576,000,000
|
// Signed seconds of the span of time. Must be from -315,576,000,000
|
||||||
// to +315,576,000,000 inclusive. Note: these bounds are computed from:
|
// to +315,576,000,000 inclusive. Note: these bounds are computed from:
|
||||||
// 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
|
// 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// source: google/protobuf/empty.proto
|
||||||
|
|
||||||
|
package empty
|
||||||
|
|
||||||
|
import (
|
||||||
|
fmt "fmt"
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
|
math "math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
|
|
||||||
|
// A generic empty message that you can re-use to avoid defining duplicated
|
||||||
|
// empty messages in your APIs. A typical example is to use it as the request
|
||||||
|
// or the response type of an API method. For instance:
|
||||||
|
//
|
||||||
|
// service Foo {
|
||||||
|
// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// The JSON representation for `Empty` is empty JSON object `{}`.
|
||||||
|
type Empty struct {
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Empty) Reset() { *m = Empty{} }
|
||||||
|
func (m *Empty) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Empty) ProtoMessage() {}
|
||||||
|
func (*Empty) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_900544acb223d5b8, []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Empty) XXX_WellKnownType() string { return "Empty" }
|
||||||
|
|
||||||
|
func (m *Empty) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_Empty.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_Empty.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *Empty) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_Empty.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *Empty) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_Empty.Size(m)
|
||||||
|
}
|
||||||
|
func (m *Empty) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_Empty.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_Empty proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*Empty)(nil), "google.protobuf.Empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterFile("google/protobuf/empty.proto", fileDescriptor_900544acb223d5b8)
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileDescriptor_900544acb223d5b8 = []byte{
|
||||||
|
// 148 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4e, 0xcf, 0xcf, 0x4f,
|
||||||
|
0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcd, 0x2d, 0x28,
|
||||||
|
0xa9, 0xd4, 0x03, 0x73, 0x85, 0xf8, 0x21, 0x92, 0x7a, 0x30, 0x49, 0x25, 0x76, 0x2e, 0x56, 0x57,
|
||||||
|
0x90, 0xbc, 0x53, 0x19, 0x97, 0x70, 0x72, 0x7e, 0xae, 0x1e, 0x9a, 0xbc, 0x13, 0x17, 0x58, 0x36,
|
||||||
|
0x00, 0xc4, 0x0d, 0x60, 0x8c, 0x52, 0x4f, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf,
|
||||||
|
0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc, 0x4b, 0x47, 0x58, 0x53, 0x50, 0x52, 0x59, 0x90, 0x5a, 0x0c,
|
||||||
|
0xb1, 0xed, 0x07, 0x23, 0xe3, 0x22, 0x26, 0x66, 0xf7, 0x00, 0xa7, 0x55, 0x4c, 0x72, 0xee, 0x10,
|
||||||
|
0x13, 0x03, 0xa0, 0xea, 0xf4, 0xc2, 0x53, 0x73, 0x72, 0xbc, 0xf3, 0xf2, 0xcb, 0xf3, 0x42, 0x40,
|
||||||
|
0xea, 0x93, 0xd8, 0xc0, 0x06, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x64, 0xd4, 0xb3, 0xa6,
|
||||||
|
0xb7, 0x00, 0x00, 0x00,
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Protocol Buffers - Google's data interchange format
|
||||||
|
// Copyright 2008 Google Inc. All rights reserved.
|
||||||
|
// https://developers.google.com/protocol-buffers/
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package google.protobuf;
|
||||||
|
|
||||||
|
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
|
||||||
|
option go_package = "github.com/golang/protobuf/ptypes/empty";
|
||||||
|
option java_package = "com.google.protobuf";
|
||||||
|
option java_outer_classname = "EmptyProto";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
option objc_class_prefix = "GPB";
|
||||||
|
option cc_enable_arenas = true;
|
||||||
|
|
||||||
|
// A generic empty message that you can re-use to avoid defining duplicated
|
||||||
|
// empty messages in your APIs. A typical example is to use it as the request
|
||||||
|
// or the response type of an API method. For instance:
|
||||||
|
//
|
||||||
|
// service Foo {
|
||||||
|
// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// The JSON representation for `Empty` is empty JSON object `{}`.
|
||||||
|
message Empty {}
|
|
@ -20,17 +20,19 @@ var _ = math.Inf
|
||||||
// proto package needs to be updated.
|
// proto package needs to be updated.
|
||||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
|
|
||||||
// A Timestamp represents a point in time independent of any time zone
|
// A Timestamp represents a point in time independent of any time zone or local
|
||||||
// or calendar, represented as seconds and fractions of seconds at
|
// calendar, encoded as a count of seconds and fractions of seconds at
|
||||||
// nanosecond resolution in UTC Epoch time. It is encoded using the
|
// nanosecond resolution. The count is relative to an epoch at UTC midnight on
|
||||||
// Proleptic Gregorian Calendar which extends the Gregorian calendar
|
// January 1, 1970, in the proleptic Gregorian calendar which extends the
|
||||||
// backwards to year one. It is encoded assuming all minutes are 60
|
// Gregorian calendar backwards to year one.
|
||||||
// seconds long, i.e. leap seconds are "smeared" so that no leap second
|
//
|
||||||
// table is needed for interpretation. Range is from
|
// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
|
||||||
// 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z.
|
// second table is needed for interpretation, using a [24-hour linear
|
||||||
// By restricting to that range, we ensure that we can convert to
|
// smear](https://developers.google.com/time/smear).
|
||||||
// and from RFC 3339 date strings.
|
//
|
||||||
// See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt).
|
// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
|
||||||
|
// restricting to that range, we ensure that we can convert to and from [RFC
|
||||||
|
// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
|
||||||
//
|
//
|
||||||
// # Examples
|
// # Examples
|
||||||
//
|
//
|
||||||
|
@ -91,12 +93,14 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
// 01:30 UTC on January 15, 2017.
|
// 01:30 UTC on January 15, 2017.
|
||||||
//
|
//
|
||||||
// In JavaScript, one can convert a Date object to this format using the
|
// In JavaScript, one can convert a Date object to this format using the
|
||||||
// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString]
|
// standard
|
||||||
|
// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
|
||||||
// method. In Python, a standard `datetime.datetime` object can be converted
|
// method. In Python, a standard `datetime.datetime` object can be converted
|
||||||
// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
|
// to this format using
|
||||||
// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
|
// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
|
||||||
// can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
|
// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
|
||||||
// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--
|
// the Joda Time's [`ISODateTimeFormat.dateTime()`](
|
||||||
|
// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D
|
||||||
// ) to obtain a formatter capable of generating timestamps in this format.
|
// ) to obtain a formatter capable of generating timestamps in this format.
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
@ -160,7 +164,9 @@ func init() {
|
||||||
proto.RegisterType((*Timestamp)(nil), "google.protobuf.Timestamp")
|
proto.RegisterType((*Timestamp)(nil), "google.protobuf.Timestamp")
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { proto.RegisterFile("google/protobuf/timestamp.proto", fileDescriptor_292007bbfe81227e) }
|
func init() {
|
||||||
|
proto.RegisterFile("google/protobuf/timestamp.proto", fileDescriptor_292007bbfe81227e)
|
||||||
|
}
|
||||||
|
|
||||||
var fileDescriptor_292007bbfe81227e = []byte{
|
var fileDescriptor_292007bbfe81227e = []byte{
|
||||||
// 191 bytes of a gzipped FileDescriptorProto
|
// 191 bytes of a gzipped FileDescriptorProto
|
||||||
|
|
|
@ -40,17 +40,19 @@ option java_outer_classname = "TimestampProto";
|
||||||
option java_multiple_files = true;
|
option java_multiple_files = true;
|
||||||
option objc_class_prefix = "GPB";
|
option objc_class_prefix = "GPB";
|
||||||
|
|
||||||
// A Timestamp represents a point in time independent of any time zone
|
// A Timestamp represents a point in time independent of any time zone or local
|
||||||
// or calendar, represented as seconds and fractions of seconds at
|
// calendar, encoded as a count of seconds and fractions of seconds at
|
||||||
// nanosecond resolution in UTC Epoch time. It is encoded using the
|
// nanosecond resolution. The count is relative to an epoch at UTC midnight on
|
||||||
// Proleptic Gregorian Calendar which extends the Gregorian calendar
|
// January 1, 1970, in the proleptic Gregorian calendar which extends the
|
||||||
// backwards to year one. It is encoded assuming all minutes are 60
|
// Gregorian calendar backwards to year one.
|
||||||
// seconds long, i.e. leap seconds are "smeared" so that no leap second
|
//
|
||||||
// table is needed for interpretation. Range is from
|
// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
|
||||||
// 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z.
|
// second table is needed for interpretation, using a [24-hour linear
|
||||||
// By restricting to that range, we ensure that we can convert to
|
// smear](https://developers.google.com/time/smear).
|
||||||
// and from RFC 3339 date strings.
|
//
|
||||||
// See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt).
|
// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
|
||||||
|
// restricting to that range, we ensure that we can convert to and from [RFC
|
||||||
|
// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
|
||||||
//
|
//
|
||||||
// # Examples
|
// # Examples
|
||||||
//
|
//
|
||||||
|
@ -111,17 +113,18 @@ option objc_class_prefix = "GPB";
|
||||||
// 01:30 UTC on January 15, 2017.
|
// 01:30 UTC on January 15, 2017.
|
||||||
//
|
//
|
||||||
// In JavaScript, one can convert a Date object to this format using the
|
// In JavaScript, one can convert a Date object to this format using the
|
||||||
// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString]
|
// standard
|
||||||
|
// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
|
||||||
// method. In Python, a standard `datetime.datetime` object can be converted
|
// method. In Python, a standard `datetime.datetime` object can be converted
|
||||||
// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
|
// to this format using
|
||||||
// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
|
// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
|
||||||
// can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
|
// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
|
||||||
// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--
|
// the Joda Time's [`ISODateTimeFormat.dateTime()`](
|
||||||
|
// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D
|
||||||
// ) to obtain a formatter capable of generating timestamps in this format.
|
// ) to obtain a formatter capable of generating timestamps in this format.
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
message Timestamp {
|
message Timestamp {
|
||||||
|
|
||||||
// Represents seconds of UTC time since Unix epoch
|
// Represents seconds of UTC time since Unix epoch
|
||||||
// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
|
// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
|
||||||
// 9999-12-31T23:59:59Z inclusive.
|
// 9999-12-31T23:59:59Z inclusive.
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.idea
|
||||||
|
|
|
@ -141,11 +141,6 @@ This plugin system will give host processes a system for constraining
|
||||||
versions. This is in addition to the protocol versioning already present
|
versions. This is in addition to the protocol versioning already present
|
||||||
which is more for larger underlying changes.
|
which is more for larger underlying changes.
|
||||||
|
|
||||||
**Plugin fetching.** We will integrate with [go-getter](https://github.com/hashicorp/go-getter)
|
|
||||||
to support automatic download + install of plugins. Paired with cryptographically
|
|
||||||
secure plugins (above), we can make this a safe operation for an amazing
|
|
||||||
user experience.
|
|
||||||
|
|
||||||
## What About Shared Libraries?
|
## What About Shared Libraries?
|
||||||
|
|
||||||
When we started using plugins (late 2012, early 2013), plugins over RPC
|
When we started using plugins (late 2012, early 2013), plugins over RPC
|
||||||
|
|
|
@ -159,11 +159,8 @@ type ClientConfig struct {
|
||||||
|
|
||||||
// SyncStdout, SyncStderr can be set to override the
|
// SyncStdout, SyncStderr can be set to override the
|
||||||
// respective os.Std* values in the plugin. Care should be taken to
|
// respective os.Std* values in the plugin. Care should be taken to
|
||||||
// avoid races here. If these are nil, then this will automatically be
|
// avoid races here. If these are nil, then this will be set to
|
||||||
// hooked up to os.Stdin, Stdout, and Stderr, respectively.
|
// ioutil.Discard.
|
||||||
//
|
|
||||||
// If the default values (nil) are used, then this package will not
|
|
||||||
// sync any of these streams.
|
|
||||||
SyncStdout io.Writer
|
SyncStdout io.Writer
|
||||||
SyncStderr io.Writer
|
SyncStderr io.Writer
|
||||||
|
|
||||||
|
@ -215,6 +212,12 @@ type ReattachConfig struct {
|
||||||
Protocol Protocol
|
Protocol Protocol
|
||||||
Addr net.Addr
|
Addr net.Addr
|
||||||
Pid int
|
Pid int
|
||||||
|
|
||||||
|
// Test is set to true if this is reattaching to to a plugin in "test mode"
|
||||||
|
// (see ServeConfig.Test). In this mode, client.Kill will NOT kill the
|
||||||
|
// process and instead will rely on the plugin to terminate itself. This
|
||||||
|
// should not be used in non-test environments.
|
||||||
|
Test bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// SecureConfig is used to configure a client to verify the integrity of an
|
// SecureConfig is used to configure a client to verify the integrity of an
|
||||||
|
@ -690,14 +693,14 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||||
|
|
||||||
// Check the core protocol. Wrapped in a {} for scoping.
|
// Check the core protocol. Wrapped in a {} for scoping.
|
||||||
{
|
{
|
||||||
var coreProtocol int64
|
var coreProtocol int
|
||||||
coreProtocol, err = strconv.ParseInt(parts[0], 10, 0)
|
coreProtocol, err = strconv.Atoi(parts[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Error parsing core protocol version: %s", err)
|
err = fmt.Errorf("Error parsing core protocol version: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if int(coreProtocol) != CoreProtocolVersion {
|
if coreProtocol != CoreProtocolVersion {
|
||||||
err = fmt.Errorf("Incompatible core API version with plugin. "+
|
err = fmt.Errorf("Incompatible core API version with plugin. "+
|
||||||
"Plugin version: %s, Core version: %d\n\n"+
|
"Plugin version: %s, Core version: %d\n\n"+
|
||||||
"To fix this, the plugin usually only needs to be recompiled.\n"+
|
"To fix this, the plugin usually only needs to be recompiled.\n"+
|
||||||
|
@ -788,7 +791,10 @@ func (c *Client) reattach() (net.Addr, error) {
|
||||||
// Verify the process still exists. If not, then it is an error
|
// Verify the process still exists. If not, then it is an error
|
||||||
p, err := os.FindProcess(c.config.Reattach.Pid)
|
p, err := os.FindProcess(c.config.Reattach.Pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
// On Unix systems, FindProcess never returns an error.
|
||||||
|
// On Windows, for non-existent pids it returns:
|
||||||
|
// os.SyscallError - 'OpenProcess: the paremter is incorrect'
|
||||||
|
return nil, ErrProcessNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to connect to the addr since on Unix systems FindProcess
|
// Attempt to connect to the addr since on Unix systems FindProcess
|
||||||
|
@ -825,15 +831,21 @@ func (c *Client) reattach() (net.Addr, error) {
|
||||||
c.exited = true
|
c.exited = true
|
||||||
}(p.Pid)
|
}(p.Pid)
|
||||||
|
|
||||||
// Set the address and process
|
// Set the address and protocol
|
||||||
c.address = c.config.Reattach.Addr
|
c.address = c.config.Reattach.Addr
|
||||||
c.process = p
|
|
||||||
c.protocol = c.config.Reattach.Protocol
|
c.protocol = c.config.Reattach.Protocol
|
||||||
if c.protocol == "" {
|
if c.protocol == "" {
|
||||||
// Default the protocol to net/rpc for backwards compatibility
|
// Default the protocol to net/rpc for backwards compatibility
|
||||||
c.protocol = ProtocolNetRPC
|
c.protocol = ProtocolNetRPC
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we're in test mode, we do NOT set the process. This avoids the
|
||||||
|
// process being killed (the only purpose we have for c.process), since
|
||||||
|
// in test mode the process is responsible for exiting on its own.
|
||||||
|
if !c.config.Reattach.Test {
|
||||||
|
c.process = p
|
||||||
|
}
|
||||||
|
|
||||||
return c.address, nil
|
return c.address, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
module github.com/hashicorp/go-plugin
|
module github.com/hashicorp/go-plugin
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
|
github.com/golang/protobuf v1.3.4
|
||||||
github.com/golang/protobuf v1.2.0
|
|
||||||
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd
|
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd
|
||||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb
|
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb
|
||||||
|
github.com/jhump/protoreflect v1.6.0
|
||||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77
|
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77
|
||||||
github.com/oklog/run v1.0.0
|
github.com/oklog/run v1.0.0
|
||||||
github.com/stretchr/testify v1.3.0 // indirect
|
github.com/stretchr/testify v1.3.0 // indirect
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect
|
google.golang.org/grpc v1.27.1
|
||||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc // indirect
|
|
||||||
golang.org/x/text v0.3.0 // indirect
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 // indirect
|
|
||||||
google.golang.org/grpc v1.14.0
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,31 +1,74 @@
|
||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
|
||||||
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd h1:rNuUHR+CvK1IS89MMtcF0EpcVMZtjKfPRp4MEmt/aTs=
|
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd h1:rNuUHR+CvK1IS89MMtcF0EpcVMZtjKfPRp4MEmt/aTs=
|
||||||
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
|
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
|
||||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
|
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
|
||||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||||
|
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
|
||||||
|
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
|
||||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg=
|
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg=
|
||||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc h1:WiYx1rIFmx8c0mXAFtv5D/mHyKe1+jmuP7PViuwqwuQ=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/grpc v1.14.0 h1:ArxJuB1NWfPY6r9Gp9gqwplT0Ge7nqv9msgu03lHLmo=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
||||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
|
||||||
|
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
|
|
@ -3,6 +3,7 @@ package plugin
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -32,6 +33,10 @@ func dialGRPCConn(tls *tls.Config, dialer func(string, time.Duration) (net.Conn,
|
||||||
credentials.NewTLS(tls)))
|
credentials.NewTLS(tls)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opts = append(opts,
|
||||||
|
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(math.MaxInt32)),
|
||||||
|
grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(math.MaxInt32)))
|
||||||
|
|
||||||
// Connect. Note the first parameter is unused because we use a custom
|
// Connect. Note the first parameter is unused because we use a custom
|
||||||
// dialer that has the state to see the address.
|
// dialer that has the state to see the address.
|
||||||
conn, err := grpc.Dial("unused", opts...)
|
conn, err := grpc.Dial("unused", opts...)
|
||||||
|
@ -56,6 +61,13 @@ func newGRPCClient(doneCtx context.Context, c *Client) (*GRPCClient, error) {
|
||||||
go broker.Run()
|
go broker.Run()
|
||||||
go brokerGRPCClient.StartStream()
|
go brokerGRPCClient.StartStream()
|
||||||
|
|
||||||
|
// Start the stdio client
|
||||||
|
stdioClient, err := newGRPCStdioClient(doneCtx, c.logger.Named("stdio"), conn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
go stdioClient.Run(c.config.SyncStdout, c.config.SyncStderr)
|
||||||
|
|
||||||
cl := &GRPCClient{
|
cl := &GRPCClient{
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
Plugins: c.config.Plugins,
|
Plugins: c.config.Plugins,
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/health"
|
"google.golang.org/grpc/health"
|
||||||
"google.golang.org/grpc/health/grpc_health_v1"
|
"google.golang.org/grpc/health/grpc_health_v1"
|
||||||
|
"google.golang.org/grpc/reflection"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GRPCServiceName is the name of the service that the health check should
|
// GRPCServiceName is the name of the service that the health check should
|
||||||
|
@ -54,6 +55,7 @@ type GRPCServer struct {
|
||||||
config GRPCServerConfig
|
config GRPCServerConfig
|
||||||
server *grpc.Server
|
server *grpc.Server
|
||||||
broker *GRPCBroker
|
broker *GRPCBroker
|
||||||
|
stdioServer *grpcStdioServer
|
||||||
|
|
||||||
logger hclog.Logger
|
logger hclog.Logger
|
||||||
}
|
}
|
||||||
|
@ -73,6 +75,9 @@ func (s *GRPCServer) Init() error {
|
||||||
GRPCServiceName, grpc_health_v1.HealthCheckResponse_SERVING)
|
GRPCServiceName, grpc_health_v1.HealthCheckResponse_SERVING)
|
||||||
grpc_health_v1.RegisterHealthServer(s.server, healthCheck)
|
grpc_health_v1.RegisterHealthServer(s.server, healthCheck)
|
||||||
|
|
||||||
|
// Register the reflection service
|
||||||
|
reflection.Register(s.server)
|
||||||
|
|
||||||
// Register the broker service
|
// Register the broker service
|
||||||
brokerServer := newGRPCBrokerServer()
|
brokerServer := newGRPCBrokerServer()
|
||||||
plugin.RegisterGRPCBrokerServer(s.server, brokerServer)
|
plugin.RegisterGRPCBrokerServer(s.server, brokerServer)
|
||||||
|
@ -80,11 +85,13 @@ func (s *GRPCServer) Init() error {
|
||||||
go s.broker.Run()
|
go s.broker.Run()
|
||||||
|
|
||||||
// Register the controller
|
// Register the controller
|
||||||
controllerServer := &grpcControllerServer{
|
controllerServer := &grpcControllerServer{server: s}
|
||||||
server: s,
|
|
||||||
}
|
|
||||||
plugin.RegisterGRPCControllerServer(s.server, controllerServer)
|
plugin.RegisterGRPCControllerServer(s.server, controllerServer)
|
||||||
|
|
||||||
|
// Register the stdio service
|
||||||
|
s.stdioServer = newGRPCStdioServer(s.logger, s.Stdout, s.Stderr)
|
||||||
|
plugin.RegisterGRPCStdioServer(s.server, s.stdioServer)
|
||||||
|
|
||||||
// Register all our plugins onto the gRPC server.
|
// Register all our plugins onto the gRPC server.
|
||||||
for k, raw := range s.Plugins {
|
for k, raw := range s.Plugins {
|
||||||
p, ok := raw.(GRPCPlugin)
|
p, ok := raw.(GRPCPlugin)
|
||||||
|
|
|
@ -0,0 +1,207 @@
|
||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
empty "github.com/golang/protobuf/ptypes/empty"
|
||||||
|
hclog "github.com/hashicorp/go-hclog"
|
||||||
|
"github.com/hashicorp/go-plugin/internal/plugin"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// grpcStdioBuffer is the buffer size we try to fill when sending a chunk of
|
||||||
|
// stdio data. This is currently 1 KB for no reason other than that seems like
|
||||||
|
// enough (stdio data isn't that common) and is fairly low.
|
||||||
|
const grpcStdioBuffer = 1 * 1024
|
||||||
|
|
||||||
|
// grpcStdioServer implements the Stdio service and streams stdiout/stderr.
|
||||||
|
type grpcStdioServer struct {
|
||||||
|
stdoutCh <-chan []byte
|
||||||
|
stderrCh <-chan []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// newGRPCStdioServer creates a new grpcStdioServer and starts the stream
|
||||||
|
// copying for the given out and err readers.
|
||||||
|
//
|
||||||
|
// This must only be called ONCE per srcOut, srcErr.
|
||||||
|
func newGRPCStdioServer(log hclog.Logger, srcOut, srcErr io.Reader) *grpcStdioServer {
|
||||||
|
stdoutCh := make(chan []byte)
|
||||||
|
stderrCh := make(chan []byte)
|
||||||
|
|
||||||
|
// Begin copying the streams
|
||||||
|
go copyChan(log, stdoutCh, srcOut)
|
||||||
|
go copyChan(log, stderrCh, srcErr)
|
||||||
|
|
||||||
|
// Construct our server
|
||||||
|
return &grpcStdioServer{
|
||||||
|
stdoutCh: stdoutCh,
|
||||||
|
stderrCh: stderrCh,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StreamStdio streams our stdout/err as the response.
|
||||||
|
func (s *grpcStdioServer) StreamStdio(
|
||||||
|
_ *empty.Empty,
|
||||||
|
srv plugin.GRPCStdio_StreamStdioServer,
|
||||||
|
) error {
|
||||||
|
// Share the same data value between runs. Sending this over the wire
|
||||||
|
// marshals it so we can reuse this.
|
||||||
|
var data plugin.StdioData
|
||||||
|
|
||||||
|
for {
|
||||||
|
// Read our data
|
||||||
|
select {
|
||||||
|
case data.Data = <-s.stdoutCh:
|
||||||
|
data.Channel = plugin.StdioData_STDOUT
|
||||||
|
|
||||||
|
case data.Data = <-s.stderrCh:
|
||||||
|
data.Channel = plugin.StdioData_STDERR
|
||||||
|
|
||||||
|
case <-srv.Context().Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not sure if this is possible, but if we somehow got here and
|
||||||
|
// we didn't populate any data at all, then just continue.
|
||||||
|
if len(data.Data) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send our data to the client.
|
||||||
|
if err := srv.Send(&data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// grpcStdioClient wraps the stdio service as a client to copy
|
||||||
|
// the stdio data to output writers.
|
||||||
|
type grpcStdioClient struct {
|
||||||
|
log hclog.Logger
|
||||||
|
stdioClient plugin.GRPCStdio_StreamStdioClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// newGRPCStdioClient creates a grpcStdioClient. This will perform the
|
||||||
|
// initial connection to the stdio service. If the stdio service is unavailable
|
||||||
|
// then this will be a no-op. This allows this to work without error for
|
||||||
|
// plugins that don't support this.
|
||||||
|
func newGRPCStdioClient(
|
||||||
|
ctx context.Context,
|
||||||
|
log hclog.Logger,
|
||||||
|
conn *grpc.ClientConn,
|
||||||
|
) (*grpcStdioClient, error) {
|
||||||
|
client := plugin.NewGRPCStdioClient(conn)
|
||||||
|
|
||||||
|
// Connect immediately to the endpoint
|
||||||
|
stdioClient, err := client.StreamStdio(ctx, &empty.Empty{})
|
||||||
|
|
||||||
|
// If we get an Unavailable or Unimplemented error, this means that the plugin isn't
|
||||||
|
// updated and linking to the latest version of go-plugin that supports
|
||||||
|
// this. We fall back to the previous behavior of just not syncing anything.
|
||||||
|
if status.Code(err) == codes.Unavailable || status.Code(err) == codes.Unimplemented {
|
||||||
|
log.Warn("stdio service not available, stdout/stderr syncing unavailable")
|
||||||
|
stdioClient = nil
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &grpcStdioClient{
|
||||||
|
log: log,
|
||||||
|
stdioClient: stdioClient,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run starts the loop that receives stdio data and writes it to the given
|
||||||
|
// writers. This blocks and should be run in a goroutine.
|
||||||
|
func (c *grpcStdioClient) Run(stdout, stderr io.Writer) {
|
||||||
|
// This will be nil if stdio is not supported by the plugin
|
||||||
|
if c.stdioClient == nil {
|
||||||
|
c.log.Warn("stdio service unavailable, run will do nothing")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
c.log.Trace("waiting for stdio data")
|
||||||
|
data, err := c.stdioClient.Recv()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF ||
|
||||||
|
status.Code(err) == codes.Unavailable ||
|
||||||
|
status.Code(err) == codes.Canceled ||
|
||||||
|
status.Code(err) == codes.Unimplemented ||
|
||||||
|
err == context.Canceled {
|
||||||
|
c.log.Warn("received EOF, stopping recv loop", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.log.Error("error receiving data", "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine our output writer based on channel
|
||||||
|
var w io.Writer
|
||||||
|
switch data.Channel {
|
||||||
|
case plugin.StdioData_STDOUT:
|
||||||
|
w = stdout
|
||||||
|
|
||||||
|
case plugin.StdioData_STDERR:
|
||||||
|
w = stderr
|
||||||
|
|
||||||
|
default:
|
||||||
|
c.log.Warn("unknown channel, dropping", "channel", data.Channel)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write! In the event of an error we just continue.
|
||||||
|
if c.log.IsTrace() {
|
||||||
|
c.log.Trace("received data", "channel", data.Channel.String(), "len", len(data.Data))
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(w, bytes.NewReader(data.Data)); err != nil {
|
||||||
|
c.log.Error("failed to copy all bytes", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyChan copies an io.Reader into a channel.
|
||||||
|
func copyChan(log hclog.Logger, dst chan<- []byte, src io.Reader) {
|
||||||
|
bufsrc := bufio.NewReader(src)
|
||||||
|
|
||||||
|
for {
|
||||||
|
// Make our data buffer. We allocate a new one per loop iteration
|
||||||
|
// so that we can send it over the channel.
|
||||||
|
var data [1024]byte
|
||||||
|
|
||||||
|
// Read the data, this will block until data is available
|
||||||
|
n, err := bufsrc.Read(data[:])
|
||||||
|
|
||||||
|
// We have to check if we have data BEFORE err != nil. The bufio
|
||||||
|
// docs guarantee n == 0 on EOF but its better to be safe here.
|
||||||
|
if n > 0 {
|
||||||
|
// We have data! Send it on the channel. This will block if there
|
||||||
|
// is no reader on the other side. We expect that go-plugin will
|
||||||
|
// connect immediately to the stdio server to drain this so we want
|
||||||
|
// this block to happen for backpressure.
|
||||||
|
dst <- data[:n]
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we hit EOF we're done copying
|
||||||
|
if err == io.EOF {
|
||||||
|
log.Debug("stdio EOF, exiting copy loop")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any other error we just exit the loop. We don't expect there to
|
||||||
|
// be errors since our use case for this is reading/writing from
|
||||||
|
// a in-process pipe (os.Pipe).
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("error copying stdio data, stopping copy", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
//go:generate protoc -I ./ ./grpc_broker.proto ./grpc_controller.proto --go_out=plugins=grpc:.
|
//go:generate protoc -I ./ ./grpc_broker.proto ./grpc_controller.proto ./grpc_stdio.proto --go_out=plugins=grpc:.
|
||||||
|
|
||||||
package plugin
|
package plugin
|
||||||
|
|
|
@ -3,12 +3,13 @@
|
||||||
|
|
||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
fmt "fmt"
|
|
||||||
proto "github.com/golang/protobuf/proto"
|
|
||||||
context "golang.org/x/net/context"
|
context "golang.org/x/net/context"
|
||||||
grpc "google.golang.org/grpc"
|
grpc "google.golang.org/grpc"
|
||||||
math "math"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
@ -35,17 +36,16 @@ func (m *ConnInfo) Reset() { *m = ConnInfo{} }
|
||||||
func (m *ConnInfo) String() string { return proto.CompactTextString(m) }
|
func (m *ConnInfo) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ConnInfo) ProtoMessage() {}
|
func (*ConnInfo) ProtoMessage() {}
|
||||||
func (*ConnInfo) Descriptor() ([]byte, []int) {
|
func (*ConnInfo) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_802e9beed3ec3b28, []int{0}
|
return fileDescriptor_grpc_broker_3322b07398605250, []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ConnInfo) XXX_Unmarshal(b []byte) error {
|
func (m *ConnInfo) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_ConnInfo.Unmarshal(m, b)
|
return xxx_messageInfo_ConnInfo.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
func (m *ConnInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
func (m *ConnInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
return xxx_messageInfo_ConnInfo.Marshal(b, m, deterministic)
|
return xxx_messageInfo_ConnInfo.Marshal(b, m, deterministic)
|
||||||
}
|
}
|
||||||
func (m *ConnInfo) XXX_Merge(src proto.Message) {
|
func (dst *ConnInfo) XXX_Merge(src proto.Message) {
|
||||||
xxx_messageInfo_ConnInfo.Merge(m, src)
|
xxx_messageInfo_ConnInfo.Merge(dst, src)
|
||||||
}
|
}
|
||||||
func (m *ConnInfo) XXX_Size() int {
|
func (m *ConnInfo) XXX_Size() int {
|
||||||
return xxx_messageInfo_ConnInfo.Size(m)
|
return xxx_messageInfo_ConnInfo.Size(m)
|
||||||
|
@ -81,23 +81,6 @@ func init() {
|
||||||
proto.RegisterType((*ConnInfo)(nil), "plugin.ConnInfo")
|
proto.RegisterType((*ConnInfo)(nil), "plugin.ConnInfo")
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { proto.RegisterFile("grpc_broker.proto", fileDescriptor_802e9beed3ec3b28) }
|
|
||||||
|
|
||||||
var fileDescriptor_802e9beed3ec3b28 = []byte{
|
|
||||||
// 175 bytes of a gzipped FileDescriptorProto
|
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4c, 0x2f, 0x2a, 0x48,
|
|
||||||
0x8e, 0x4f, 0x2a, 0xca, 0xcf, 0x4e, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2b,
|
|
||||||
0xc8, 0x29, 0x4d, 0xcf, 0xcc, 0x53, 0x8a, 0xe5, 0xe2, 0x70, 0xce, 0xcf, 0xcb, 0xf3, 0xcc, 0x4b,
|
|
||||||
0xcb, 0x17, 0x92, 0xe5, 0xe2, 0x2a, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0x8d, 0xcf, 0x4c, 0x91,
|
|
||||||
0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0d, 0xe2, 0x84, 0x8a, 0x78, 0xa6, 0x08, 0x49, 0x70, 0xb1, 0xe7,
|
|
||||||
0xa5, 0x96, 0x94, 0xe7, 0x17, 0x65, 0x4b, 0x30, 0x29, 0x30, 0x6a, 0x70, 0x06, 0xc1, 0xb8, 0x20,
|
|
||||||
0x99, 0xc4, 0x94, 0x94, 0xa2, 0xd4, 0xe2, 0x62, 0x09, 0x66, 0x88, 0x0c, 0x94, 0x6b, 0xe4, 0xcc,
|
|
||||||
0xc5, 0xe5, 0x1e, 0x14, 0xe0, 0xec, 0x04, 0xb6, 0x5a, 0xc8, 0x94, 0x8b, 0x3b, 0xb8, 0x24, 0xb1,
|
|
||||||
0xa8, 0x24, 0xb8, 0xa4, 0x28, 0x35, 0x31, 0x57, 0x48, 0x40, 0x0f, 0xe2, 0x08, 0x3d, 0x98, 0x0b,
|
|
||||||
0xa4, 0x30, 0x44, 0x34, 0x18, 0x0d, 0x18, 0x9d, 0x38, 0xa2, 0xa0, 0xae, 0x4d, 0x62, 0x03, 0x3b,
|
|
||||||
0xde, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x10, 0x15, 0x39, 0x47, 0xd1, 0x00, 0x00, 0x00,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
var _ context.Context
|
var _ context.Context
|
||||||
var _ grpc.ClientConn
|
var _ grpc.ClientConn
|
||||||
|
@ -201,3 +184,20 @@ var _GRPCBroker_serviceDesc = grpc.ServiceDesc{
|
||||||
},
|
},
|
||||||
Metadata: "grpc_broker.proto",
|
Metadata: "grpc_broker.proto",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("grpc_broker.proto", fileDescriptor_grpc_broker_3322b07398605250) }
|
||||||
|
|
||||||
|
var fileDescriptor_grpc_broker_3322b07398605250 = []byte{
|
||||||
|
// 175 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4c, 0x2f, 0x2a, 0x48,
|
||||||
|
0x8e, 0x4f, 0x2a, 0xca, 0xcf, 0x4e, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2b,
|
||||||
|
0xc8, 0x29, 0x4d, 0xcf, 0xcc, 0x53, 0x8a, 0xe5, 0xe2, 0x70, 0xce, 0xcf, 0xcb, 0xf3, 0xcc, 0x4b,
|
||||||
|
0xcb, 0x17, 0x92, 0xe5, 0xe2, 0x2a, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0x8d, 0xcf, 0x4c, 0x91,
|
||||||
|
0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0d, 0xe2, 0x84, 0x8a, 0x78, 0xa6, 0x08, 0x49, 0x70, 0xb1, 0xe7,
|
||||||
|
0xa5, 0x96, 0x94, 0xe7, 0x17, 0x65, 0x4b, 0x30, 0x29, 0x30, 0x6a, 0x70, 0x06, 0xc1, 0xb8, 0x20,
|
||||||
|
0x99, 0xc4, 0x94, 0x94, 0xa2, 0xd4, 0xe2, 0x62, 0x09, 0x66, 0x88, 0x0c, 0x94, 0x6b, 0xe4, 0xcc,
|
||||||
|
0xc5, 0xe5, 0x1e, 0x14, 0xe0, 0xec, 0x04, 0xb6, 0x5a, 0xc8, 0x94, 0x8b, 0x3b, 0xb8, 0x24, 0xb1,
|
||||||
|
0xa8, 0x24, 0xb8, 0xa4, 0x28, 0x35, 0x31, 0x57, 0x48, 0x40, 0x0f, 0xe2, 0x08, 0x3d, 0x98, 0x0b,
|
||||||
|
0xa4, 0x30, 0x44, 0x34, 0x18, 0x0d, 0x18, 0x9d, 0x38, 0xa2, 0xa0, 0xae, 0x4d, 0x62, 0x03, 0x3b,
|
||||||
|
0xde, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x10, 0x15, 0x39, 0x47, 0xd1, 0x00, 0x00, 0x00,
|
||||||
|
}
|
||||||
|
|
|
@ -11,5 +11,3 @@ message ConnInfo {
|
||||||
service GRPCBroker {
|
service GRPCBroker {
|
||||||
rpc StartStream(stream ConnInfo) returns (stream ConnInfo);
|
rpc StartStream(stream ConnInfo) returns (stream ConnInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,13 @@
|
||||||
|
|
||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
fmt "fmt"
|
|
||||||
proto "github.com/golang/protobuf/proto"
|
|
||||||
context "golang.org/x/net/context"
|
context "golang.org/x/net/context"
|
||||||
grpc "google.golang.org/grpc"
|
grpc "google.golang.org/grpc"
|
||||||
math "math"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
@ -32,17 +33,16 @@ func (m *Empty) Reset() { *m = Empty{} }
|
||||||
func (m *Empty) String() string { return proto.CompactTextString(m) }
|
func (m *Empty) String() string { return proto.CompactTextString(m) }
|
||||||
func (*Empty) ProtoMessage() {}
|
func (*Empty) ProtoMessage() {}
|
||||||
func (*Empty) Descriptor() ([]byte, []int) {
|
func (*Empty) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_23c2c7e42feab570, []int{0}
|
return fileDescriptor_grpc_controller_08f8296ef6d80436, []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Empty) XXX_Unmarshal(b []byte) error {
|
func (m *Empty) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_Empty.Unmarshal(m, b)
|
return xxx_messageInfo_Empty.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
return xxx_messageInfo_Empty.Marshal(b, m, deterministic)
|
return xxx_messageInfo_Empty.Marshal(b, m, deterministic)
|
||||||
}
|
}
|
||||||
func (m *Empty) XXX_Merge(src proto.Message) {
|
func (dst *Empty) XXX_Merge(src proto.Message) {
|
||||||
xxx_messageInfo_Empty.Merge(m, src)
|
xxx_messageInfo_Empty.Merge(dst, src)
|
||||||
}
|
}
|
||||||
func (m *Empty) XXX_Size() int {
|
func (m *Empty) XXX_Size() int {
|
||||||
return xxx_messageInfo_Empty.Size(m)
|
return xxx_messageInfo_Empty.Size(m)
|
||||||
|
@ -57,19 +57,6 @@ func init() {
|
||||||
proto.RegisterType((*Empty)(nil), "plugin.Empty")
|
proto.RegisterType((*Empty)(nil), "plugin.Empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { proto.RegisterFile("grpc_controller.proto", fileDescriptor_23c2c7e42feab570) }
|
|
||||||
|
|
||||||
var fileDescriptor_23c2c7e42feab570 = []byte{
|
|
||||||
// 108 bytes of a gzipped FileDescriptorProto
|
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4d, 0x2f, 0x2a, 0x48,
|
|
||||||
0x8e, 0x4f, 0xce, 0xcf, 0x2b, 0x29, 0xca, 0xcf, 0xc9, 0x49, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f,
|
|
||||||
0xc9, 0x17, 0x62, 0x2b, 0xc8, 0x29, 0x4d, 0xcf, 0xcc, 0x53, 0x62, 0xe7, 0x62, 0x75, 0xcd, 0x2d,
|
|
||||||
0x28, 0xa9, 0x34, 0xb2, 0xe2, 0xe2, 0x73, 0x0f, 0x0a, 0x70, 0x76, 0x86, 0x2b, 0x14, 0xd2, 0xe0,
|
|
||||||
0xe2, 0x08, 0xce, 0x28, 0x2d, 0x49, 0xc9, 0x2f, 0xcf, 0x13, 0xe2, 0xd5, 0x83, 0xa8, 0xd7, 0x03,
|
|
||||||
0x2b, 0x96, 0x42, 0xe5, 0x3a, 0x71, 0x44, 0x41, 0x8d, 0x4b, 0x62, 0x03, 0x9b, 0x6e, 0x0c, 0x08,
|
|
||||||
0x00, 0x00, 0xff, 0xff, 0xab, 0x7c, 0x27, 0xe5, 0x76, 0x00, 0x00, 0x00,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
var _ context.Context
|
var _ context.Context
|
||||||
var _ grpc.ClientConn
|
var _ grpc.ClientConn
|
||||||
|
@ -141,3 +128,18 @@ var _GRPCController_serviceDesc = grpc.ServiceDesc{
|
||||||
Streams: []grpc.StreamDesc{},
|
Streams: []grpc.StreamDesc{},
|
||||||
Metadata: "grpc_controller.proto",
|
Metadata: "grpc_controller.proto",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterFile("grpc_controller.proto", fileDescriptor_grpc_controller_08f8296ef6d80436)
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileDescriptor_grpc_controller_08f8296ef6d80436 = []byte{
|
||||||
|
// 108 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4d, 0x2f, 0x2a, 0x48,
|
||||||
|
0x8e, 0x4f, 0xce, 0xcf, 0x2b, 0x29, 0xca, 0xcf, 0xc9, 0x49, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f,
|
||||||
|
0xc9, 0x17, 0x62, 0x2b, 0xc8, 0x29, 0x4d, 0xcf, 0xcc, 0x53, 0x62, 0xe7, 0x62, 0x75, 0xcd, 0x2d,
|
||||||
|
0x28, 0xa9, 0x34, 0xb2, 0xe2, 0xe2, 0x73, 0x0f, 0x0a, 0x70, 0x76, 0x86, 0x2b, 0x14, 0xd2, 0xe0,
|
||||||
|
0xe2, 0x08, 0xce, 0x28, 0x2d, 0x49, 0xc9, 0x2f, 0xcf, 0x13, 0xe2, 0xd5, 0x83, 0xa8, 0xd7, 0x03,
|
||||||
|
0x2b, 0x96, 0x42, 0xe5, 0x3a, 0x71, 0x44, 0x41, 0x8d, 0x4b, 0x62, 0x03, 0x9b, 0x6e, 0x0c, 0x08,
|
||||||
|
0x00, 0x00, 0xff, 0xff, 0xab, 0x7c, 0x27, 0xe5, 0x76, 0x00, 0x00, 0x00,
|
||||||
|
}
|
||||||
|
|
233
vendor/github.com/hashicorp/go-plugin/internal/plugin/grpc_stdio.pb.go
generated
vendored
Normal file
233
vendor/github.com/hashicorp/go-plugin/internal/plugin/grpc_stdio.pb.go
generated
vendored
Normal file
|
@ -0,0 +1,233 @@
|
||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// source: grpc_stdio.proto
|
||||||
|
|
||||||
|
package plugin
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
import empty "github.com/golang/protobuf/ptypes/empty"
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "golang.org/x/net/context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
type StdioData_Channel int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
StdioData_INVALID StdioData_Channel = 0
|
||||||
|
StdioData_STDOUT StdioData_Channel = 1
|
||||||
|
StdioData_STDERR StdioData_Channel = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
var StdioData_Channel_name = map[int32]string{
|
||||||
|
0: "INVALID",
|
||||||
|
1: "STDOUT",
|
||||||
|
2: "STDERR",
|
||||||
|
}
|
||||||
|
var StdioData_Channel_value = map[string]int32{
|
||||||
|
"INVALID": 0,
|
||||||
|
"STDOUT": 1,
|
||||||
|
"STDERR": 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x StdioData_Channel) String() string {
|
||||||
|
return proto.EnumName(StdioData_Channel_name, int32(x))
|
||||||
|
}
|
||||||
|
func (StdioData_Channel) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_grpc_stdio_db2934322ca63bd5, []int{0, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StdioData is a single chunk of stdout or stderr data that is streamed
|
||||||
|
// from GRPCStdio.
|
||||||
|
type StdioData struct {
|
||||||
|
Channel StdioData_Channel `protobuf:"varint,1,opt,name=channel,proto3,enum=plugin.StdioData_Channel" json:"channel,omitempty"`
|
||||||
|
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StdioData) Reset() { *m = StdioData{} }
|
||||||
|
func (m *StdioData) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*StdioData) ProtoMessage() {}
|
||||||
|
func (*StdioData) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_grpc_stdio_db2934322ca63bd5, []int{0}
|
||||||
|
}
|
||||||
|
func (m *StdioData) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_StdioData.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *StdioData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_StdioData.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *StdioData) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_StdioData.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *StdioData) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_StdioData.Size(m)
|
||||||
|
}
|
||||||
|
func (m *StdioData) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_StdioData.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_StdioData proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *StdioData) GetChannel() StdioData_Channel {
|
||||||
|
if m != nil {
|
||||||
|
return m.Channel
|
||||||
|
}
|
||||||
|
return StdioData_INVALID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StdioData) GetData() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.Data
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*StdioData)(nil), "plugin.StdioData")
|
||||||
|
proto.RegisterEnum("plugin.StdioData_Channel", StdioData_Channel_name, StdioData_Channel_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ context.Context
|
||||||
|
var _ grpc.ClientConn
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
const _ = grpc.SupportPackageIsVersion4
|
||||||
|
|
||||||
|
// GRPCStdioClient is the client API for GRPCStdio service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||||
|
type GRPCStdioClient interface {
|
||||||
|
// StreamStdio returns a stream that contains all the stdout/stderr.
|
||||||
|
// This RPC endpoint must only be called ONCE. Once stdio data is consumed
|
||||||
|
// it is not sent again.
|
||||||
|
//
|
||||||
|
// Callers should connect early to prevent blocking on the plugin process.
|
||||||
|
StreamStdio(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (GRPCStdio_StreamStdioClient, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type gRPCStdioClient struct {
|
||||||
|
cc *grpc.ClientConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGRPCStdioClient(cc *grpc.ClientConn) GRPCStdioClient {
|
||||||
|
return &gRPCStdioClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gRPCStdioClient) StreamStdio(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (GRPCStdio_StreamStdioClient, error) {
|
||||||
|
stream, err := c.cc.NewStream(ctx, &_GRPCStdio_serviceDesc.Streams[0], "/plugin.GRPCStdio/StreamStdio", opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &gRPCStdioStreamStdioClient{stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type GRPCStdio_StreamStdioClient interface {
|
||||||
|
Recv() (*StdioData, error)
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type gRPCStdioStreamStdioClient struct {
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *gRPCStdioStreamStdioClient) Recv() (*StdioData, error) {
|
||||||
|
m := new(StdioData)
|
||||||
|
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GRPCStdioServer is the server API for GRPCStdio service.
|
||||||
|
type GRPCStdioServer interface {
|
||||||
|
// StreamStdio returns a stream that contains all the stdout/stderr.
|
||||||
|
// This RPC endpoint must only be called ONCE. Once stdio data is consumed
|
||||||
|
// it is not sent again.
|
||||||
|
//
|
||||||
|
// Callers should connect early to prevent blocking on the plugin process.
|
||||||
|
StreamStdio(*empty.Empty, GRPCStdio_StreamStdioServer) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterGRPCStdioServer(s *grpc.Server, srv GRPCStdioServer) {
|
||||||
|
s.RegisterService(&_GRPCStdio_serviceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _GRPCStdio_StreamStdio_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(empty.Empty)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(GRPCStdioServer).StreamStdio(m, &gRPCStdioStreamStdioServer{stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
type GRPCStdio_StreamStdioServer interface {
|
||||||
|
Send(*StdioData) error
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type gRPCStdioStreamStdioServer struct {
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *gRPCStdioStreamStdioServer) Send(m *StdioData) error {
|
||||||
|
return x.ServerStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _GRPCStdio_serviceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "plugin.GRPCStdio",
|
||||||
|
HandlerType: (*GRPCStdioServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{},
|
||||||
|
Streams: []grpc.StreamDesc{
|
||||||
|
{
|
||||||
|
StreamName: "StreamStdio",
|
||||||
|
Handler: _GRPCStdio_StreamStdio_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Metadata: "grpc_stdio.proto",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("grpc_stdio.proto", fileDescriptor_grpc_stdio_db2934322ca63bd5) }
|
||||||
|
|
||||||
|
var fileDescriptor_grpc_stdio_db2934322ca63bd5 = []byte{
|
||||||
|
// 221 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x48, 0x2f, 0x2a, 0x48,
|
||||||
|
0x8e, 0x2f, 0x2e, 0x49, 0xc9, 0xcc, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2b, 0xc8,
|
||||||
|
0x29, 0x4d, 0xcf, 0xcc, 0x93, 0x92, 0x4e, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x07, 0x8b, 0x26,
|
||||||
|
0x95, 0xa6, 0xe9, 0xa7, 0xe6, 0x16, 0x94, 0x54, 0x42, 0x14, 0x29, 0xb5, 0x30, 0x72, 0x71, 0x06,
|
||||||
|
0x83, 0x34, 0xb9, 0x24, 0x96, 0x24, 0x0a, 0x19, 0x73, 0xb1, 0x27, 0x67, 0x24, 0xe6, 0xe5, 0xa5,
|
||||||
|
0xe6, 0x48, 0x30, 0x2a, 0x30, 0x6a, 0xf0, 0x19, 0x49, 0xea, 0x41, 0x0c, 0xd1, 0x83, 0xab, 0xd1,
|
||||||
|
0x73, 0x86, 0x28, 0x08, 0x82, 0xa9, 0x14, 0x12, 0xe2, 0x62, 0x49, 0x49, 0x2c, 0x49, 0x94, 0x60,
|
||||||
|
0x52, 0x60, 0xd4, 0xe0, 0x09, 0x02, 0xb3, 0x95, 0xf4, 0xb8, 0xd8, 0xa1, 0xea, 0x84, 0xb8, 0xb9,
|
||||||
|
0xd8, 0x3d, 0xfd, 0xc2, 0x1c, 0x7d, 0x3c, 0x5d, 0x04, 0x18, 0x84, 0xb8, 0xb8, 0xd8, 0x82, 0x43,
|
||||||
|
0x5c, 0xfc, 0x43, 0x43, 0x04, 0x18, 0xa1, 0x6c, 0xd7, 0xa0, 0x20, 0x01, 0x26, 0x23, 0x77, 0x2e,
|
||||||
|
0x4e, 0xf7, 0xa0, 0x00, 0x67, 0xb0, 0x2d, 0x42, 0x56, 0x5c, 0xdc, 0xc1, 0x25, 0x45, 0xa9, 0x89,
|
||||||
|
0xb9, 0x10, 0xae, 0x98, 0x1e, 0xc4, 0x03, 0x7a, 0x30, 0x0f, 0xe8, 0xb9, 0x82, 0x3c, 0x20, 0x25,
|
||||||
|
0x88, 0xe1, 0x36, 0x03, 0x46, 0x27, 0x8e, 0x28, 0xa8, 0xb7, 0x93, 0xd8, 0xc0, 0xca, 0x8d, 0x01,
|
||||||
|
0x01, 0x00, 0x00, 0xff, 0xff, 0x5d, 0xbb, 0xe0, 0x69, 0x19, 0x01, 0x00, 0x00,
|
||||||
|
}
|
30
vendor/github.com/hashicorp/go-plugin/internal/plugin/grpc_stdio.proto
generated
vendored
Normal file
30
vendor/github.com/hashicorp/go-plugin/internal/plugin/grpc_stdio.proto
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
package plugin;
|
||||||
|
option go_package = "plugin";
|
||||||
|
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
|
||||||
|
// GRPCStdio is a service that is automatically run by the plugin process
|
||||||
|
// to stream any stdout/err data so that it can be mirrored on the plugin
|
||||||
|
// host side.
|
||||||
|
service GRPCStdio {
|
||||||
|
// StreamStdio returns a stream that contains all the stdout/stderr.
|
||||||
|
// This RPC endpoint must only be called ONCE. Once stdio data is consumed
|
||||||
|
// it is not sent again.
|
||||||
|
//
|
||||||
|
// Callers should connect early to prevent blocking on the plugin process.
|
||||||
|
rpc StreamStdio(google.protobuf.Empty) returns (stream StdioData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// StdioData is a single chunk of stdout or stderr data that is streamed
|
||||||
|
// from GRPCStdio.
|
||||||
|
message StdioData {
|
||||||
|
enum Channel {
|
||||||
|
INVALID = 0;
|
||||||
|
STDOUT = 1;
|
||||||
|
STDERR = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Channel channel = 1;
|
||||||
|
bytes data = 2;
|
||||||
|
}
|
|
@ -1,11 +1,13 @@
|
||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
@ -15,10 +17,8 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
|
||||||
|
|
||||||
"github.com/hashicorp/go-hclog"
|
|
||||||
|
|
||||||
|
hclog "github.com/hashicorp/go-hclog"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -85,6 +85,51 @@ type ServeConfig struct {
|
||||||
// Logger is used to pass a logger into the server. If none is provided the
|
// Logger is used to pass a logger into the server. If none is provided the
|
||||||
// server will create a default logger.
|
// server will create a default logger.
|
||||||
Logger hclog.Logger
|
Logger hclog.Logger
|
||||||
|
|
||||||
|
// Test, if non-nil, will put plugin serving into "test mode". This is
|
||||||
|
// meant to be used as part of `go test` within a plugin's codebase to
|
||||||
|
// launch the plugin in-process and output a ReattachConfig.
|
||||||
|
//
|
||||||
|
// This changes the behavior of the server in a number of ways to
|
||||||
|
// accomodate the expectation of running in-process:
|
||||||
|
//
|
||||||
|
// * The handshake cookie is not validated.
|
||||||
|
// * Stdout/stderr will receive plugin reads and writes
|
||||||
|
// * Connection information will not be sent to stdout
|
||||||
|
//
|
||||||
|
Test *ServeTestConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeTestConfig configures plugin serving for test mode. See ServeConfig.Test.
|
||||||
|
type ServeTestConfig struct {
|
||||||
|
// Context, if set, will force the plugin serving to end when cancelled.
|
||||||
|
// This is only a test configuration because the non-test configuration
|
||||||
|
// expects to take over the process and therefore end on an interrupt or
|
||||||
|
// kill signal. For tests, we need to kill the plugin serving routinely
|
||||||
|
// and this provides a way to do so.
|
||||||
|
//
|
||||||
|
// If you want to wait for the plugin process to close before moving on,
|
||||||
|
// you can wait on CloseCh.
|
||||||
|
Context context.Context
|
||||||
|
|
||||||
|
// If this channel is non-nil, we will send the ReattachConfig via
|
||||||
|
// this channel. This can be encoded (via JSON recommended) to the
|
||||||
|
// plugin client to attach to this plugin.
|
||||||
|
ReattachConfigCh chan<- *ReattachConfig
|
||||||
|
|
||||||
|
// CloseCh, if non-nil, will be closed when serving exits. This can be
|
||||||
|
// used along with Context to determine when the server is fully shut down.
|
||||||
|
// If this is not set, you can still use Context on its own, but note there
|
||||||
|
// may be a period of time between canceling the context and the plugin
|
||||||
|
// server being shut down.
|
||||||
|
CloseCh chan<- struct{}
|
||||||
|
|
||||||
|
// SyncStdio, if true, will enable the client side "SyncStdout/Stderr"
|
||||||
|
// functionality to work. This defaults to false because the implementation
|
||||||
|
// of making this work within test environments is particularly messy
|
||||||
|
// and SyncStdio functionality is fairly rare, so we default to the simple
|
||||||
|
// scenario.
|
||||||
|
SyncStdio bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// protocolVersion determines the protocol version and plugin set to be used by
|
// protocolVersion determines the protocol version and plugin set to be used by
|
||||||
|
@ -169,17 +214,35 @@ func protocolVersion(opts *ServeConfig) (int, Protocol, PluginSet) {
|
||||||
// Serve serves the plugins given by ServeConfig.
|
// Serve serves the plugins given by ServeConfig.
|
||||||
//
|
//
|
||||||
// Serve doesn't return until the plugin is done being executed. Any
|
// Serve doesn't return until the plugin is done being executed. Any
|
||||||
// errors will be outputted to os.Stderr.
|
// fixable errors will be output to os.Stderr and the process will
|
||||||
|
// exit with a status code of 1. Serve will panic for unexpected
|
||||||
|
// conditions where a user's fix is unknown.
|
||||||
//
|
//
|
||||||
// This is the method that plugins should call in their main() functions.
|
// This is the method that plugins should call in their main() functions.
|
||||||
func Serve(opts *ServeConfig) {
|
func Serve(opts *ServeConfig) {
|
||||||
|
exitCode := -1
|
||||||
|
// We use this to trigger an `os.Exit` so that we can execute our other
|
||||||
|
// deferred functions. In test mode, we just output the err to stderr
|
||||||
|
// and return.
|
||||||
|
defer func() {
|
||||||
|
if opts.Test == nil && exitCode >= 0 {
|
||||||
|
os.Exit(exitCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Test != nil && opts.Test.CloseCh != nil {
|
||||||
|
close(opts.Test.CloseCh)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if opts.Test == nil {
|
||||||
// Validate the handshake config
|
// Validate the handshake config
|
||||||
if opts.MagicCookieKey == "" || opts.MagicCookieValue == "" {
|
if opts.MagicCookieKey == "" || opts.MagicCookieValue == "" {
|
||||||
fmt.Fprintf(os.Stderr,
|
fmt.Fprintf(os.Stderr,
|
||||||
"Misconfigured ServeConfig given to serve this plugin: no magic cookie\n"+
|
"Misconfigured ServeConfig given to serve this plugin: no magic cookie\n"+
|
||||||
"key or value was set. Please notify the plugin author and report\n"+
|
"key or value was set. Please notify the plugin author and report\n"+
|
||||||
"this as a bug.\n")
|
"this as a bug.\n")
|
||||||
os.Exit(1)
|
exitCode = 1
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// First check the cookie
|
// First check the cookie
|
||||||
|
@ -188,7 +251,9 @@ func Serve(opts *ServeConfig) {
|
||||||
"This binary is a plugin. These are not meant to be executed directly.\n"+
|
"This binary is a plugin. These are not meant to be executed directly.\n"+
|
||||||
"Please execute the program that consumes these plugins, which will\n"+
|
"Please execute the program that consumes these plugins, which will\n"+
|
||||||
"load any plugins automatically\n")
|
"load any plugins automatically\n")
|
||||||
os.Exit(1)
|
exitCode = 1
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// negotiate the version and plugins
|
// negotiate the version and plugins
|
||||||
|
@ -208,19 +273,6 @@ func Serve(opts *ServeConfig) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create our new stdout, stderr files. These will override our built-in
|
|
||||||
// stdout/stderr so that it works across the stream boundary.
|
|
||||||
stdout_r, stdout_w, err := os.Pipe()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Error preparing plugin: %s\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
stderr_r, stderr_w, err := os.Pipe()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Error preparing plugin: %s\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register a listener so we can accept a connection
|
// Register a listener so we can accept a connection
|
||||||
listener, err := serverListener()
|
listener, err := serverListener()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -281,6 +333,33 @@ func Serve(opts *ServeConfig) {
|
||||||
// Create the channel to tell us when we're done
|
// Create the channel to tell us when we're done
|
||||||
doneCh := make(chan struct{})
|
doneCh := make(chan struct{})
|
||||||
|
|
||||||
|
// Create our new stdout, stderr files. These will override our built-in
|
||||||
|
// stdout/stderr so that it works across the stream boundary.
|
||||||
|
var stdout_r, stderr_r io.Reader
|
||||||
|
stdout_r, stdout_w, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error preparing plugin: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
stderr_r, stderr_w, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error preparing plugin: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're in test mode, we tee off the reader and write the data
|
||||||
|
// as-is to our normal Stdout and Stderr so that they continue working
|
||||||
|
// while stdio works. This is because in test mode, we assume we're running
|
||||||
|
// in `go test` or some equivalent and we want output to go to standard
|
||||||
|
// locations.
|
||||||
|
if opts.Test != nil {
|
||||||
|
// TODO(mitchellh): This isn't super ideal because a TeeReader
|
||||||
|
// only works if the reader side is actively read. If we never
|
||||||
|
// connect via a plugin client, the output still gets swallowed.
|
||||||
|
stdout_r = io.TeeReader(stdout_r, os.Stdout)
|
||||||
|
stderr_r = io.TeeReader(stderr_r, os.Stderr)
|
||||||
|
}
|
||||||
|
|
||||||
// Build the server type
|
// Build the server type
|
||||||
var server ServerProtocol
|
var server ServerProtocol
|
||||||
switch protoType {
|
switch protoType {
|
||||||
|
@ -323,7 +402,10 @@ func Serve(opts *ServeConfig) {
|
||||||
|
|
||||||
logger.Debug("plugin address", "network", listener.Addr().Network(), "address", listener.Addr().String())
|
logger.Debug("plugin address", "network", listener.Addr().Network(), "address", listener.Addr().String())
|
||||||
|
|
||||||
// Output the address and service name to stdout so that the client can bring it up.
|
// Output the address and service name to stdout so that the client can
|
||||||
|
// bring it up. In test mode, we don't do this because clients will
|
||||||
|
// attach via a reattach config.
|
||||||
|
if opts.Test == nil {
|
||||||
fmt.Printf("%d|%d|%s|%s|%s|%s\n",
|
fmt.Printf("%d|%d|%s|%s|%s|%s\n",
|
||||||
CoreProtocolVersion,
|
CoreProtocolVersion,
|
||||||
protoVersion,
|
protoVersion,
|
||||||
|
@ -332,26 +414,84 @@ func Serve(opts *ServeConfig) {
|
||||||
protoType,
|
protoType,
|
||||||
serverCert)
|
serverCert)
|
||||||
os.Stdout.Sync()
|
os.Stdout.Sync()
|
||||||
|
} else if ch := opts.Test.ReattachConfigCh; ch != nil {
|
||||||
|
// Send back the reattach config that can be used. This isn't
|
||||||
|
// quite ready if they connect immediately but the client should
|
||||||
|
// retry a few times.
|
||||||
|
ch <- &ReattachConfig{
|
||||||
|
Protocol: protoType,
|
||||||
|
Addr: listener.Addr(),
|
||||||
|
Pid: os.Getpid(),
|
||||||
|
Test: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Eat the interrupts
|
// Eat the interrupts. In test mode we disable this so that go test
|
||||||
|
// can be cancelled properly.
|
||||||
|
if opts.Test == nil {
|
||||||
ch := make(chan os.Signal, 1)
|
ch := make(chan os.Signal, 1)
|
||||||
signal.Notify(ch, os.Interrupt)
|
signal.Notify(ch, os.Interrupt)
|
||||||
go func() {
|
go func() {
|
||||||
var count int32 = 0
|
count := 0
|
||||||
for {
|
for {
|
||||||
<-ch
|
<-ch
|
||||||
newCount := atomic.AddInt32(&count, 1)
|
count++
|
||||||
logger.Debug("plugin received interrupt signal, ignoring", "count", newCount)
|
logger.Trace("plugin received interrupt signal, ignoring", "count", count)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
// Set our new out, err
|
// Set our stdout, stderr to the stdio stream that clients can retrieve
|
||||||
|
// using ClientConfig.SyncStdout/err. We only do this for non-test mode
|
||||||
|
// or if the test mode explicitly requests it.
|
||||||
|
//
|
||||||
|
// In test mode, we use a multiwriter so that the data continues going
|
||||||
|
// to the normal stdout/stderr so output can show up in test logs. We
|
||||||
|
// also send to the stdio stream so that clients can continue working
|
||||||
|
// if they depend on that.
|
||||||
|
if opts.Test == nil || opts.Test.SyncStdio {
|
||||||
|
if opts.Test != nil {
|
||||||
|
// In test mode we need to maintain the original values so we can
|
||||||
|
// reset it.
|
||||||
|
defer func(out, err *os.File) {
|
||||||
|
os.Stdout = out
|
||||||
|
os.Stderr = err
|
||||||
|
}(os.Stdout, os.Stderr)
|
||||||
|
}
|
||||||
os.Stdout = stdout_w
|
os.Stdout = stdout_w
|
||||||
os.Stderr = stderr_w
|
os.Stderr = stderr_w
|
||||||
|
}
|
||||||
|
|
||||||
// Accept connections and wait for completion
|
// Accept connections and wait for completion
|
||||||
go server.Serve(listener)
|
go server.Serve(listener)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
if opts.Test != nil && opts.Test.Context != nil {
|
||||||
|
ctx = opts.Test.Context
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
// Cancellation. We can stop the server by closing the listener.
|
||||||
|
// This isn't graceful at all but this is currently only used by
|
||||||
|
// tests and its our only way to stop.
|
||||||
|
listener.Close()
|
||||||
|
|
||||||
|
// If this is a grpc server, then we also ask the server itself to
|
||||||
|
// end which will kill all connections. There isn't an easy way to do
|
||||||
|
// this for net/rpc currently but net/rpc is more and more unused.
|
||||||
|
if s, ok := server.(*GRPCServer); ok {
|
||||||
|
s.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the server itself to shut down
|
||||||
<-doneCh
|
<-doneCh
|
||||||
|
|
||||||
|
case <-doneCh:
|
||||||
|
// Note that given the documentation of Serve we should probably be
|
||||||
|
// setting exitCode = 0 and using os.Exit here. That's how it used to
|
||||||
|
// work before extracting this library. However, for years we've done
|
||||||
|
// this so we'll keep this functionality.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func serverListener() (net.Listener, error) {
|
func serverListener() (net.Listener, error) {
|
||||||
|
@ -390,7 +530,7 @@ func serverListener_tcp() (net.Listener, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if minPort > maxPort {
|
if minPort > maxPort {
|
||||||
return nil, fmt.Errorf("ENV_MIN_PORT value of %d is greater than PLUGIN_MAX_PORT value of %d", minPort, maxPort)
|
return nil, fmt.Errorf("PLUGIN_MIN_PORT value of %d is greater than PLUGIN_MAX_PORT value of %d", minPort, maxPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
for port := minPort; port <= maxPort; port++ {
|
for port := minPort; port <= maxPort; port++ {
|
||||||
|
|
|
@ -7,9 +7,9 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/rpc"
|
"net/rpc"
|
||||||
|
|
||||||
"github.com/mitchellh/go-testing-interface"
|
|
||||||
hclog "github.com/hashicorp/go-hclog"
|
hclog "github.com/hashicorp/go-hclog"
|
||||||
"github.com/hashicorp/go-plugin/internal/plugin"
|
"github.com/hashicorp/go-plugin/internal/plugin"
|
||||||
|
"github.com/mitchellh/go-testing-interface"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,20 @@ language: go
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
|
- go: 1.13.x
|
||||||
|
env: VET=1 GO111MODULE=on
|
||||||
|
- go: 1.13.x
|
||||||
|
env: RACE=1 GO111MODULE=on
|
||||||
|
- go: 1.13.x
|
||||||
|
env: RUN386=1
|
||||||
|
- go: 1.13.x
|
||||||
|
env: GRPC_GO_RETRY=on
|
||||||
|
- go: 1.13.x
|
||||||
|
env: TESTEXTRAS=1
|
||||||
- go: 1.12.x
|
- go: 1.12.x
|
||||||
env: GO111MODULE=on
|
env: GO111MODULE=on
|
||||||
- go: 1.11.x
|
- go: 1.11.x
|
||||||
env: VET=1 GO111MODULE=on
|
env: GO111MODULE=on
|
||||||
- go: 1.11.x
|
|
||||||
env: RACE=1 GO111MODULE=on
|
|
||||||
- go: 1.11.x
|
|
||||||
env: RUN386=1
|
|
||||||
- go: 1.11.x
|
|
||||||
env: GRPC_GO_RETRY=on
|
|
||||||
- go: 1.10.x
|
|
||||||
- go: 1.9.x
|
|
||||||
- go: 1.9.x
|
- go: 1.9.x
|
||||||
env: GAE=1
|
env: GAE=1
|
||||||
|
|
||||||
|
@ -23,17 +25,18 @@ before_install:
|
||||||
- if [[ "${GO111MODULE}" = "on" ]]; then mkdir "${HOME}/go"; export GOPATH="${HOME}/go"; fi
|
- if [[ "${GO111MODULE}" = "on" ]]; then mkdir "${HOME}/go"; export GOPATH="${HOME}/go"; fi
|
||||||
- if [[ -n "${RUN386}" ]]; then export GOARCH=386; fi
|
- if [[ -n "${RUN386}" ]]; then export GOARCH=386; fi
|
||||||
- if [[ "${TRAVIS_EVENT_TYPE}" = "cron" && -z "${RUN386}" ]]; then RACE=1; fi
|
- if [[ "${TRAVIS_EVENT_TYPE}" = "cron" && -z "${RUN386}" ]]; then RACE=1; fi
|
||||||
- if [[ "${TRAVIS_EVENT_TYPE}" != "cron" ]]; then VET_SKIP_PROTO=1; fi
|
- if [[ "${TRAVIS_EVENT_TYPE}" != "cron" ]]; then export VET_SKIP_PROTO=1; fi
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- try3() { eval "$*" || eval "$*" || eval "$*"; }
|
- try3() { eval "$*" || eval "$*" || eval "$*"; }
|
||||||
- try3 'if [[ "${GO111MODULE}" = "on" ]]; then go mod download; else make testdeps; fi'
|
- try3 'if [[ "${GO111MODULE}" = "on" ]]; then go mod download; else make testdeps; fi'
|
||||||
- if [[ "${GAE}" = 1 ]]; then source ./install_gae.sh; make testappenginedeps; fi
|
- if [[ -n "${GAE}" ]]; then source ./install_gae.sh; make testappenginedeps; fi
|
||||||
- if [[ "${VET}" = 1 ]]; then ./vet.sh -install; fi
|
- if [[ -n "${VET}" ]]; then ./vet.sh -install; fi
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- set -e
|
- set -e
|
||||||
- if [[ "${VET}" = 1 ]]; then ./vet.sh; fi
|
- if [[ -n "${TESTEXTRAS}" ]]; then examples/examples_test.sh; interop/interop_test.sh; make testsubmodule; exit 0; fi
|
||||||
- if [[ "${GAE}" = 1 ]]; then make testappengine; exit 0; fi
|
- if [[ -n "${VET}" ]]; then ./vet.sh; fi
|
||||||
- if [[ "${RACE}" = 1 ]]; then make testrace; exit 0; fi
|
- if [[ -n "${GAE}" ]]; then make testappengine; exit 0; fi
|
||||||
|
- if [[ -n "${RACE}" ]]; then make testrace; exit 0; fi
|
||||||
- make test
|
- make test
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
## Community Code of Conduct
|
||||||
|
|
||||||
|
gRPC follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
|
|
@ -1,6 +1,8 @@
|
||||||
# How to contribute
|
# How to contribute
|
||||||
|
|
||||||
We definitely welcome your patches and contributions to gRPC!
|
We definitely welcome your patches and contributions to gRPC! Please read the gRPC
|
||||||
|
organization's [governance rules](https://github.com/grpc/grpc-community/blob/master/governance.md)
|
||||||
|
and [contribution guidelines](https://github.com/grpc/grpc-community/blob/master/CONTRIBUTING.md) before proceeding.
|
||||||
|
|
||||||
If you are new to github, please start by reading [Pull Request howto](https://help.github.com/articles/about-pull-requests/)
|
If you are new to github, please start by reading [Pull Request howto](https://help.github.com/articles/about-pull-requests/)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
This repository is governed by the gRPC organization's [governance rules](https://github.com/grpc/grpc-community/blob/master/governance.md).
|
|
@ -0,0 +1,27 @@
|
||||||
|
This page lists all active maintainers of this repository. If you were a
|
||||||
|
maintainer and would like to add your name to the Emeritus list, please send us a
|
||||||
|
PR.
|
||||||
|
|
||||||
|
See [GOVERNANCE.md](https://github.com/grpc/grpc-community/blob/master/governance.md)
|
||||||
|
for governance guidelines and how to become a maintainer.
|
||||||
|
See [CONTRIBUTING.md](https://github.com/grpc/grpc-community/blob/master/CONTRIBUTING.md)
|
||||||
|
for general contribution guidelines.
|
||||||
|
|
||||||
|
## Maintainers (in alphabetical order)
|
||||||
|
- [canguler](https://github.com/canguler), Google LLC
|
||||||
|
- [cesarghali](https://github.com/cesarghali), Google LLC
|
||||||
|
- [dfawley](https://github.com/dfawley), Google LLC
|
||||||
|
- [easwars](https://github.com/easwars), Google LLC
|
||||||
|
- [jadekler](https://github.com/jadekler), Google LLC
|
||||||
|
- [menghanl](https://github.com/menghanl), Google LLC
|
||||||
|
- [srini100](https://github.com/srini100), Google LLC
|
||||||
|
|
||||||
|
## Emeritus Maintainers (in alphabetical order)
|
||||||
|
- [adelez](https://github.com/adelez), Google LLC
|
||||||
|
- [iamqizhao](https://github.com/iamqizhao), Google LLC
|
||||||
|
- [jtattermusch](https://github.com/jtattermusch), Google LLC
|
||||||
|
- [lyuxuan](https://github.com/lyuxuan), Google LLC
|
||||||
|
- [makmukhi](https://github.com/makmukhi), Google LLC
|
||||||
|
- [matt-kwong](https://github.com/matt-kwong), Google LLC
|
||||||
|
- [nicolasnoble](https://github.com/nicolasnoble), Google LLC
|
||||||
|
- [yongni](https://github.com/yongni), Google LLC
|
|
@ -19,6 +19,9 @@ proto:
|
||||||
test: testdeps
|
test: testdeps
|
||||||
go test -cpu 1,4 -timeout 7m google.golang.org/grpc/...
|
go test -cpu 1,4 -timeout 7m google.golang.org/grpc/...
|
||||||
|
|
||||||
|
testsubmodule: testdeps
|
||||||
|
cd security/advancedtls && go test -cpu 1,4 -timeout 7m google.golang.org/grpc/security/advancedtls/...
|
||||||
|
|
||||||
testappengine: testappenginedeps
|
testappengine: testappenginedeps
|
||||||
goapp test -cpu 1,4 -timeout 7m google.golang.org/grpc/...
|
goapp test -cpu 1,4 -timeout 7m google.golang.org/grpc/...
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2019 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 attributes defines a generic key/value store used in various gRPC
|
||||||
|
// components.
|
||||||
|
//
|
||||||
|
// All APIs in this package are EXPERIMENTAL.
|
||||||
|
package attributes
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Attributes is an immutable struct for storing and retrieving generic
|
||||||
|
// key/value pairs. Keys must be hashable, and users should define their own
|
||||||
|
// types for keys.
|
||||||
|
type Attributes struct {
|
||||||
|
m map[interface{}]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new Attributes containing all key/value pairs in kvs. If the
|
||||||
|
// same key appears multiple times, the last value overwrites all previous
|
||||||
|
// values for that key. Panics if len(kvs) is not even.
|
||||||
|
func New(kvs ...interface{}) *Attributes {
|
||||||
|
if len(kvs)%2 != 0 {
|
||||||
|
panic(fmt.Sprintf("attributes.New called with unexpected input: len(kvs) = %v", len(kvs)))
|
||||||
|
}
|
||||||
|
a := &Attributes{m: make(map[interface{}]interface{}, len(kvs)/2)}
|
||||||
|
for i := 0; i < len(kvs)/2; i++ {
|
||||||
|
a.m[kvs[i*2]] = kvs[i*2+1]
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithValues returns a new Attributes containing all key/value pairs in a and
|
||||||
|
// kvs. Panics if len(kvs) is not even. If the same key appears multiple
|
||||||
|
// times, the last value overwrites all previous values for that key. To
|
||||||
|
// remove an existing key, use a nil value.
|
||||||
|
func (a *Attributes) WithValues(kvs ...interface{}) *Attributes {
|
||||||
|
if len(kvs)%2 != 0 {
|
||||||
|
panic(fmt.Sprintf("attributes.New called with unexpected input: len(kvs) = %v", len(kvs)))
|
||||||
|
}
|
||||||
|
n := &Attributes{m: make(map[interface{}]interface{}, len(a.m)+len(kvs)/2)}
|
||||||
|
for k, v := range a.m {
|
||||||
|
n.m[k] = v
|
||||||
|
}
|
||||||
|
for i := 0; i < len(kvs)/2; i++ {
|
||||||
|
n.m[kvs[i*2]] = kvs[i*2+1]
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the value associated with these attributes for key, or nil if
|
||||||
|
// no value is associated with key.
|
||||||
|
func (a *Attributes) Value(key interface{}) interface{} {
|
||||||
|
return a.m[key]
|
||||||
|
}
|
|
@ -23,16 +23,36 @@ package grpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/backoff"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultBackoffConfig uses values specified for backoff in
|
// DefaultBackoffConfig uses values specified for backoff in
|
||||||
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
|
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
|
||||||
|
//
|
||||||
|
// Deprecated: use ConnectParams instead. Will be supported throughout 1.x.
|
||||||
var DefaultBackoffConfig = BackoffConfig{
|
var DefaultBackoffConfig = BackoffConfig{
|
||||||
MaxDelay: 120 * time.Second,
|
MaxDelay: 120 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
// BackoffConfig defines the parameters for the default gRPC backoff strategy.
|
// BackoffConfig defines the parameters for the default gRPC backoff strategy.
|
||||||
|
//
|
||||||
|
// Deprecated: use ConnectParams instead. Will be supported throughout 1.x.
|
||||||
type BackoffConfig struct {
|
type BackoffConfig struct {
|
||||||
// MaxDelay is the upper bound of backoff delay.
|
// MaxDelay is the upper bound of backoff delay.
|
||||||
MaxDelay time.Duration
|
MaxDelay time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConnectParams defines the parameters for connecting and retrying. Users are
|
||||||
|
// encouraged to use this instead of the BackoffConfig type defined above. See
|
||||||
|
// here for more details:
|
||||||
|
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
|
||||||
|
//
|
||||||
|
// This API is EXPERIMENTAL.
|
||||||
|
type ConnectParams struct {
|
||||||
|
// Backoff specifies the configuration options for connection backoff.
|
||||||
|
Backoff backoff.Config
|
||||||
|
// MinConnectTimeout is the minimum amount of time we are willing to give a
|
||||||
|
// connection to complete.
|
||||||
|
MinConnectTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2019 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 backoff provides configuration options for backoff.
|
||||||
|
//
|
||||||
|
// More details can be found at:
|
||||||
|
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
|
||||||
|
//
|
||||||
|
// All APIs in this package are experimental.
|
||||||
|
package backoff
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Config defines the configuration options for backoff.
|
||||||
|
type Config struct {
|
||||||
|
// BaseDelay is the amount of time to backoff after the first failure.
|
||||||
|
BaseDelay time.Duration
|
||||||
|
// Multiplier is the factor with which to multiply backoffs after a
|
||||||
|
// failed retry. Should ideally be greater than 1.
|
||||||
|
Multiplier float64
|
||||||
|
// Jitter is the factor with which backoffs are randomized.
|
||||||
|
Jitter float64
|
||||||
|
// MaxDelay is the upper bound of backoff delay.
|
||||||
|
MaxDelay time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultConfig is a backoff configuration with the default values specfied
|
||||||
|
// at https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
|
||||||
|
//
|
||||||
|
// This should be useful for callers who want to configure backoff with
|
||||||
|
// non-default values only for a subset of the options.
|
||||||
|
var DefaultConfig = Config{
|
||||||
|
BaseDelay: 1.0 * time.Second,
|
||||||
|
Multiplier: 1.6,
|
||||||
|
Jitter: 0.2,
|
||||||
|
MaxDelay: 120 * time.Second,
|
||||||
|
}
|
|
@ -43,7 +43,7 @@ type Address struct {
|
||||||
|
|
||||||
// BalancerConfig specifies the configurations for Balancer.
|
// BalancerConfig specifies the configurations for Balancer.
|
||||||
//
|
//
|
||||||
// Deprecated: please use package balancer.
|
// Deprecated: please use package balancer. May be removed in a future 1.x release.
|
||||||
type BalancerConfig struct {
|
type BalancerConfig struct {
|
||||||
// DialCreds is the transport credential the Balancer implementation can
|
// DialCreds is the transport credential the Balancer implementation can
|
||||||
// use to dial to a remote load balancer server. The Balancer implementations
|
// use to dial to a remote load balancer server. The Balancer implementations
|
||||||
|
@ -57,7 +57,7 @@ type BalancerConfig struct {
|
||||||
|
|
||||||
// BalancerGetOptions configures a Get call.
|
// BalancerGetOptions configures a Get call.
|
||||||
//
|
//
|
||||||
// Deprecated: please use package balancer.
|
// Deprecated: please use package balancer. May be removed in a future 1.x release.
|
||||||
type BalancerGetOptions struct {
|
type BalancerGetOptions struct {
|
||||||
// BlockingWait specifies whether Get should block when there is no
|
// BlockingWait specifies whether Get should block when there is no
|
||||||
// connected address.
|
// connected address.
|
||||||
|
@ -66,7 +66,7 @@ type BalancerGetOptions struct {
|
||||||
|
|
||||||
// Balancer chooses network addresses for RPCs.
|
// Balancer chooses network addresses for RPCs.
|
||||||
//
|
//
|
||||||
// Deprecated: please use package balancer.
|
// Deprecated: please use package balancer. May be removed in a future 1.x release.
|
||||||
type Balancer interface {
|
type Balancer interface {
|
||||||
// Start does the initialization work to bootstrap a Balancer. For example,
|
// Start does the initialization work to bootstrap a Balancer. For example,
|
||||||
// this function may start the name resolution and watch the updates. It will
|
// this function may start the name resolution and watch the updates. It will
|
||||||
|
@ -120,7 +120,7 @@ type Balancer interface {
|
||||||
// RoundRobin returns a Balancer that selects addresses round-robin. It uses r to watch
|
// RoundRobin returns a Balancer that selects addresses round-robin. It uses r to watch
|
||||||
// the name resolution updates and updates the addresses available correspondingly.
|
// the name resolution updates and updates the addresses available correspondingly.
|
||||||
//
|
//
|
||||||
// Deprecated: please use package balancer/roundrobin.
|
// Deprecated: please use package balancer/roundrobin. May be removed in a future 1.x release.
|
||||||
func RoundRobin(r naming.Resolver) Balancer {
|
func RoundRobin(r naming.Resolver) Balancer {
|
||||||
return &roundRobin{r: r}
|
return &roundRobin{r: r}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ package balancer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -31,6 +32,7 @@ import (
|
||||||
"google.golang.org/grpc/internal"
|
"google.golang.org/grpc/internal"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/resolver"
|
"google.golang.org/grpc/resolver"
|
||||||
|
"google.golang.org/grpc/serviceconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -39,7 +41,10 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Register registers the balancer builder to the balancer map. b.Name
|
// Register registers the balancer builder to the balancer map. b.Name
|
||||||
// (lowercased) will be used as the name registered with this builder.
|
// (lowercased) will be used as the name registered with this builder. If the
|
||||||
|
// Builder implements ConfigParser, ParseConfig will be called when new service
|
||||||
|
// configs are received by the resolver, and the result will be provided to the
|
||||||
|
// Balancer in UpdateClientConnState.
|
||||||
//
|
//
|
||||||
// NOTE: this function must only be called during initialization time (i.e. in
|
// NOTE: this function must only be called during initialization time (i.e. in
|
||||||
// an init() function), and is not thread-safe. If multiple Balancers are
|
// an init() function), and is not thread-safe. If multiple Balancers are
|
||||||
|
@ -112,6 +117,15 @@ type NewSubConnOptions struct {
|
||||||
HealthCheckEnabled bool
|
HealthCheckEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// State contains the balancer's state relevant to the gRPC ClientConn.
|
||||||
|
type State struct {
|
||||||
|
// State contains the connectivity state of the balancer, which is used to
|
||||||
|
// determine the state of the ClientConn.
|
||||||
|
ConnectivityState connectivity.State
|
||||||
|
// Picker is used to choose connections (SubConns) for RPCs.
|
||||||
|
Picker V2Picker
|
||||||
|
}
|
||||||
|
|
||||||
// ClientConn represents a gRPC ClientConn.
|
// ClientConn represents a gRPC ClientConn.
|
||||||
//
|
//
|
||||||
// This interface is to be implemented by gRPC. Users should not need a
|
// This interface is to be implemented by gRPC. Users should not need a
|
||||||
|
@ -132,10 +146,19 @@ type ClientConn interface {
|
||||||
//
|
//
|
||||||
// gRPC will update the connectivity state of the ClientConn, and will call pick
|
// gRPC will update the connectivity state of the ClientConn, and will call pick
|
||||||
// on the new picker to pick new SubConn.
|
// on the new picker to pick new SubConn.
|
||||||
|
//
|
||||||
|
// Deprecated: use UpdateState instead
|
||||||
UpdateBalancerState(s connectivity.State, p Picker)
|
UpdateBalancerState(s connectivity.State, p Picker)
|
||||||
|
|
||||||
|
// UpdateState notifies gRPC that the balancer's internal state has
|
||||||
|
// changed.
|
||||||
|
//
|
||||||
|
// gRPC will update the connectivity state of the ClientConn, and will call pick
|
||||||
|
// on the new picker to pick new SubConns.
|
||||||
|
UpdateState(State)
|
||||||
|
|
||||||
// ResolveNow is called by balancer to notify gRPC to do a name resolving.
|
// ResolveNow is called by balancer to notify gRPC to do a name resolving.
|
||||||
ResolveNow(resolver.ResolveNowOption)
|
ResolveNow(resolver.ResolveNowOptions)
|
||||||
|
|
||||||
// Target returns the dial target for this ClientConn.
|
// Target returns the dial target for this ClientConn.
|
||||||
//
|
//
|
||||||
|
@ -172,11 +195,22 @@ type Builder interface {
|
||||||
Name() string
|
Name() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// PickOptions contains addition information for the Pick operation.
|
// ConfigParser parses load balancer configs.
|
||||||
type PickOptions struct {
|
type ConfigParser interface {
|
||||||
|
// ParseConfig parses the JSON load balancer config provided into an
|
||||||
|
// internal form or returns an error if the config is invalid. For future
|
||||||
|
// compatibility reasons, unknown fields in the config should be ignored.
|
||||||
|
ParseConfig(LoadBalancingConfigJSON json.RawMessage) (serviceconfig.LoadBalancingConfig, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PickInfo contains additional information for the Pick operation.
|
||||||
|
type PickInfo struct {
|
||||||
// FullMethodName is the method name that NewClientStream() is called
|
// FullMethodName is the method name that NewClientStream() is called
|
||||||
// with. The canonical format is /service/Method.
|
// with. The canonical format is /service/Method.
|
||||||
FullMethodName string
|
FullMethodName string
|
||||||
|
// Ctx is the RPC's context, and may contain relevant RPC-level information
|
||||||
|
// like the outgoing header metadata.
|
||||||
|
Ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoneInfo contains additional information for done.
|
// DoneInfo contains additional information for done.
|
||||||
|
@ -202,7 +236,7 @@ var (
|
||||||
ErrNoSubConnAvailable = errors.New("no SubConn is available")
|
ErrNoSubConnAvailable = errors.New("no SubConn is available")
|
||||||
// ErrTransientFailure indicates all SubConns are in TransientFailure.
|
// ErrTransientFailure indicates all SubConns are in TransientFailure.
|
||||||
// WaitForReady RPCs will block, non-WaitForReady RPCs will fail.
|
// WaitForReady RPCs will block, non-WaitForReady RPCs will fail.
|
||||||
ErrTransientFailure = errors.New("all SubConns are in TransientFailure")
|
ErrTransientFailure = TransientFailureError(errors.New("all SubConns are in TransientFailure"))
|
||||||
)
|
)
|
||||||
|
|
||||||
// Picker is used by gRPC to pick a SubConn to send an RPC.
|
// Picker is used by gRPC to pick a SubConn to send an RPC.
|
||||||
|
@ -210,6 +244,8 @@ var (
|
||||||
// internal state has changed.
|
// internal state has changed.
|
||||||
//
|
//
|
||||||
// The pickers used by gRPC can be updated by ClientConn.UpdateBalancerState().
|
// The pickers used by gRPC can be updated by ClientConn.UpdateBalancerState().
|
||||||
|
//
|
||||||
|
// Deprecated: use V2Picker instead
|
||||||
type Picker interface {
|
type Picker interface {
|
||||||
// Pick returns the SubConn to be used to send the RPC.
|
// Pick returns the SubConn to be used to send the RPC.
|
||||||
// The returned SubConn must be one returned by NewSubConn().
|
// The returned SubConn must be one returned by NewSubConn().
|
||||||
|
@ -230,18 +266,76 @@ type Picker interface {
|
||||||
//
|
//
|
||||||
// If the returned error is not nil:
|
// If the returned error is not nil:
|
||||||
// - If the error is ErrNoSubConnAvailable, gRPC will block until UpdateBalancerState()
|
// - If the error is ErrNoSubConnAvailable, gRPC will block until UpdateBalancerState()
|
||||||
// - If the error is ErrTransientFailure:
|
// - If the error is ErrTransientFailure or implements IsTransientFailure()
|
||||||
|
// bool, returning true:
|
||||||
// - If the RPC is wait-for-ready, gRPC will block until UpdateBalancerState()
|
// - If the RPC is wait-for-ready, gRPC will block until UpdateBalancerState()
|
||||||
// is called to pick again;
|
// is called to pick again;
|
||||||
// - Otherwise, RPC will fail with unavailable error.
|
// - Otherwise, RPC will fail with unavailable error.
|
||||||
// - Else (error is other non-nil error):
|
// - Else (error is other non-nil error):
|
||||||
// - The RPC will fail with unavailable error.
|
// - The RPC will fail with the error's status code, or Unknown if it is
|
||||||
|
// not a status error.
|
||||||
//
|
//
|
||||||
// The returned done() function will be called once the rpc has finished,
|
// The returned done() function will be called once the rpc has finished,
|
||||||
// with the final status of that RPC. If the SubConn returned is not a
|
// with the final status of that RPC. If the SubConn returned is not a
|
||||||
// valid SubConn type, done may not be called. done may be nil if balancer
|
// valid SubConn type, done may not be called. done may be nil if balancer
|
||||||
// doesn't care about the RPC status.
|
// doesn't care about the RPC status.
|
||||||
Pick(ctx context.Context, opts PickOptions) (conn SubConn, done func(DoneInfo), err error)
|
Pick(ctx context.Context, info PickInfo) (conn SubConn, done func(DoneInfo), err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PickResult contains information related to a connection chosen for an RPC.
|
||||||
|
type PickResult struct {
|
||||||
|
// SubConn is the connection to use for this pick, if its state is Ready.
|
||||||
|
// If the state is not Ready, gRPC will block the RPC until a new Picker is
|
||||||
|
// provided by the balancer (using ClientConn.UpdateState). The SubConn
|
||||||
|
// must be one returned by ClientConn.NewSubConn.
|
||||||
|
SubConn SubConn
|
||||||
|
|
||||||
|
// Done is called when the RPC is completed. If the SubConn is not ready,
|
||||||
|
// this will be called with a nil parameter. If the SubConn is not a valid
|
||||||
|
// type, Done may not be called. May be nil if the balancer does not wish
|
||||||
|
// to be notified when the RPC completes.
|
||||||
|
Done func(DoneInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
type transientFailureError struct {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *transientFailureError) IsTransientFailure() bool { return true }
|
||||||
|
|
||||||
|
// TransientFailureError wraps err in an error implementing
|
||||||
|
// IsTransientFailure() bool, returning true.
|
||||||
|
func TransientFailureError(err error) error {
|
||||||
|
return &transientFailureError{error: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// V2Picker is used by gRPC to pick a SubConn to send an RPC.
|
||||||
|
// Balancer is expected to generate a new picker from its snapshot every time its
|
||||||
|
// internal state has changed.
|
||||||
|
//
|
||||||
|
// The pickers used by gRPC can be updated by ClientConn.UpdateBalancerState().
|
||||||
|
type V2Picker interface {
|
||||||
|
// Pick returns the connection to use for this RPC and related information.
|
||||||
|
//
|
||||||
|
// Pick should not block. If the balancer needs to do I/O or any blocking
|
||||||
|
// or time-consuming work to service this call, it should return
|
||||||
|
// ErrNoSubConnAvailable, and the Pick call will be repeated by gRPC when
|
||||||
|
// the Picker is updated (using ClientConn.UpdateState).
|
||||||
|
//
|
||||||
|
// If an error is returned:
|
||||||
|
//
|
||||||
|
// - If the error is ErrNoSubConnAvailable, gRPC will block until a new
|
||||||
|
// Picker is provided by the balancer (using ClientConn.UpdateState).
|
||||||
|
//
|
||||||
|
// - If the error implements IsTransientFailure() bool, returning true,
|
||||||
|
// wait for ready RPCs will wait, but non-wait for ready RPCs will be
|
||||||
|
// terminated with this error's Error() string and status code
|
||||||
|
// Unavailable.
|
||||||
|
//
|
||||||
|
// - Any other errors terminate all RPCs with the code and message
|
||||||
|
// provided. If the error is not a status error, it will be converted by
|
||||||
|
// gRPC to a status error with code Unknown.
|
||||||
|
Pick(info PickInfo) (PickResult, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Balancer takes input from gRPC, manages SubConns, and collects and aggregates
|
// Balancer takes input from gRPC, manages SubConns, and collects and aggregates
|
||||||
|
@ -270,7 +364,7 @@ type Balancer interface {
|
||||||
// non-nil error to gRPC.
|
// non-nil error to gRPC.
|
||||||
//
|
//
|
||||||
// Deprecated: if V2Balancer is implemented by the Balancer,
|
// Deprecated: if V2Balancer is implemented by the Balancer,
|
||||||
// UpdateResolverState will be called instead.
|
// UpdateClientConnState will be called instead.
|
||||||
HandleResolvedAddrs([]resolver.Address, error)
|
HandleResolvedAddrs([]resolver.Address, error)
|
||||||
// Close closes the balancer. The balancer is not required to call
|
// Close closes the balancer. The balancer is not required to call
|
||||||
// ClientConn.RemoveSubConn for its existing SubConns.
|
// ClientConn.RemoveSubConn for its existing SubConns.
|
||||||
|
@ -279,18 +373,39 @@ type Balancer interface {
|
||||||
|
|
||||||
// SubConnState describes the state of a SubConn.
|
// SubConnState describes the state of a SubConn.
|
||||||
type SubConnState struct {
|
type SubConnState struct {
|
||||||
|
// ConnectivityState is the connectivity state of the SubConn.
|
||||||
ConnectivityState connectivity.State
|
ConnectivityState connectivity.State
|
||||||
// TODO: add last connection error
|
// ConnectionError is set if the ConnectivityState is TransientFailure,
|
||||||
|
// describing the reason the SubConn failed. Otherwise, it is nil.
|
||||||
|
ConnectionError error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClientConnState describes the state of a ClientConn relevant to the
|
||||||
|
// balancer.
|
||||||
|
type ClientConnState struct {
|
||||||
|
ResolverState resolver.State
|
||||||
|
// The parsed load balancing configuration returned by the builder's
|
||||||
|
// ParseConfig method, if implemented.
|
||||||
|
BalancerConfig serviceconfig.LoadBalancingConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrBadResolverState may be returned by UpdateClientConnState to indicate a
|
||||||
|
// problem with the provided name resolver data.
|
||||||
|
var ErrBadResolverState = errors.New("bad resolver state")
|
||||||
|
|
||||||
// V2Balancer is defined for documentation purposes. If a Balancer also
|
// V2Balancer is defined for documentation purposes. If a Balancer also
|
||||||
// implements V2Balancer, its UpdateResolverState method will be called instead
|
// implements V2Balancer, its UpdateClientConnState method will be called
|
||||||
// of HandleResolvedAddrs and its UpdateSubConnState will be called instead of
|
// instead of HandleResolvedAddrs and its UpdateSubConnState will be called
|
||||||
// HandleSubConnStateChange.
|
// instead of HandleSubConnStateChange.
|
||||||
type V2Balancer interface {
|
type V2Balancer interface {
|
||||||
// UpdateResolverState is called by gRPC when the state of the resolver
|
// UpdateClientConnState is called by gRPC when the state of the ClientConn
|
||||||
// changes.
|
// changes. If the error returned is ErrBadResolverState, the ClientConn
|
||||||
UpdateResolverState(resolver.State)
|
// will begin calling ResolveNow on the active name resolver with
|
||||||
|
// exponential backoff until a subsequent call to UpdateClientConnState
|
||||||
|
// returns a nil error. Any other errors are currently ignored.
|
||||||
|
UpdateClientConnState(ClientConnState) error
|
||||||
|
// ResolverError is called by gRPC when the name resolver reports an error.
|
||||||
|
ResolverError(error)
|
||||||
// UpdateSubConnState is called by gRPC when the state of a SubConn
|
// UpdateSubConnState is called by gRPC when the state of a SubConn
|
||||||
// changes.
|
// changes.
|
||||||
UpdateSubConnState(SubConn, SubConnState)
|
UpdateSubConnState(SubConn, SubConnState)
|
||||||
|
@ -306,7 +421,6 @@ type V2Balancer interface {
|
||||||
type ConnectivityStateEvaluator struct {
|
type ConnectivityStateEvaluator struct {
|
||||||
numReady uint64 // Number of addrConns in ready state.
|
numReady uint64 // Number of addrConns in ready state.
|
||||||
numConnecting uint64 // Number of addrConns in connecting state.
|
numConnecting uint64 // Number of addrConns in connecting state.
|
||||||
numTransientFailure uint64 // Number of addrConns in transientFailure.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecordTransition records state change happening in subConn and based on that
|
// RecordTransition records state change happening in subConn and based on that
|
||||||
|
@ -326,8 +440,6 @@ func (cse *ConnectivityStateEvaluator) RecordTransition(oldState, newState conne
|
||||||
cse.numReady += updateVal
|
cse.numReady += updateVal
|
||||||
case connectivity.Connecting:
|
case connectivity.Connecting:
|
||||||
cse.numConnecting += updateVal
|
cse.numConnecting += updateVal
|
||||||
case connectivity.TransientFailure:
|
|
||||||
cse.numTransientFailure += updateVal
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@ package base
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"google.golang.org/grpc/balancer"
|
"google.golang.org/grpc/balancer"
|
||||||
"google.golang.org/grpc/connectivity"
|
"google.golang.org/grpc/connectivity"
|
||||||
|
@ -30,32 +32,42 @@ import (
|
||||||
type baseBuilder struct {
|
type baseBuilder struct {
|
||||||
name string
|
name string
|
||||||
pickerBuilder PickerBuilder
|
pickerBuilder PickerBuilder
|
||||||
|
v2PickerBuilder V2PickerBuilder
|
||||||
config Config
|
config Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bb *baseBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer {
|
func (bb *baseBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer {
|
||||||
return &baseBalancer{
|
bal := &baseBalancer{
|
||||||
cc: cc,
|
cc: cc,
|
||||||
pickerBuilder: bb.pickerBuilder,
|
pickerBuilder: bb.pickerBuilder,
|
||||||
|
v2PickerBuilder: bb.v2PickerBuilder,
|
||||||
|
|
||||||
subConns: make(map[resolver.Address]balancer.SubConn),
|
subConns: make(map[resolver.Address]balancer.SubConn),
|
||||||
scStates: make(map[balancer.SubConn]connectivity.State),
|
scStates: make(map[balancer.SubConn]connectivity.State),
|
||||||
csEvltr: &balancer.ConnectivityStateEvaluator{},
|
csEvltr: &balancer.ConnectivityStateEvaluator{},
|
||||||
// Initialize picker to a picker that always return
|
|
||||||
// ErrNoSubConnAvailable, because when state of a SubConn changes, we
|
|
||||||
// may call UpdateBalancerState with this picker.
|
|
||||||
picker: NewErrPicker(balancer.ErrNoSubConnAvailable),
|
|
||||||
config: bb.config,
|
config: bb.config,
|
||||||
}
|
}
|
||||||
|
// Initialize picker to a picker that always returns
|
||||||
|
// ErrNoSubConnAvailable, because when state of a SubConn changes, we
|
||||||
|
// may call UpdateState with this picker.
|
||||||
|
if bb.pickerBuilder != nil {
|
||||||
|
bal.picker = NewErrPicker(balancer.ErrNoSubConnAvailable)
|
||||||
|
} else {
|
||||||
|
bal.v2Picker = NewErrPickerV2(balancer.ErrNoSubConnAvailable)
|
||||||
|
}
|
||||||
|
return bal
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bb *baseBuilder) Name() string {
|
func (bb *baseBuilder) Name() string {
|
||||||
return bb.name
|
return bb.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ balancer.V2Balancer = (*baseBalancer)(nil) // Assert that we implement V2Balancer
|
||||||
|
|
||||||
type baseBalancer struct {
|
type baseBalancer struct {
|
||||||
cc balancer.ClientConn
|
cc balancer.ClientConn
|
||||||
pickerBuilder PickerBuilder
|
pickerBuilder PickerBuilder
|
||||||
|
v2PickerBuilder V2PickerBuilder
|
||||||
|
|
||||||
csEvltr *balancer.ConnectivityStateEvaluator
|
csEvltr *balancer.ConnectivityStateEvaluator
|
||||||
state connectivity.State
|
state connectivity.State
|
||||||
|
@ -63,20 +75,53 @@ type baseBalancer struct {
|
||||||
subConns map[resolver.Address]balancer.SubConn
|
subConns map[resolver.Address]balancer.SubConn
|
||||||
scStates map[balancer.SubConn]connectivity.State
|
scStates map[balancer.SubConn]connectivity.State
|
||||||
picker balancer.Picker
|
picker balancer.Picker
|
||||||
|
v2Picker balancer.V2Picker
|
||||||
config Config
|
config Config
|
||||||
|
|
||||||
|
resolverErr error // the last error reported by the resolver; cleared on successful resolution
|
||||||
|
connErr error // the last connection error; cleared upon leaving TransientFailure
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *baseBalancer) HandleResolvedAddrs(addrs []resolver.Address, err error) {
|
func (b *baseBalancer) HandleResolvedAddrs(addrs []resolver.Address, err error) {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *baseBalancer) UpdateResolverState(s resolver.State) {
|
func (b *baseBalancer) ResolverError(err error) {
|
||||||
// TODO: handle s.Err (log if not nil) once implemented.
|
b.resolverErr = err
|
||||||
// TODO: handle s.ServiceConfig?
|
if len(b.subConns) == 0 {
|
||||||
grpclog.Infoln("base.baseBalancer: got new resolver state: ", s)
|
b.state = connectivity.TransientFailure
|
||||||
|
}
|
||||||
|
if b.state != connectivity.TransientFailure {
|
||||||
|
// The picker will not change since the balancer does not currently
|
||||||
|
// report an error.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.regeneratePicker()
|
||||||
|
if b.picker != nil {
|
||||||
|
b.cc.UpdateBalancerState(b.state, b.picker)
|
||||||
|
} else {
|
||||||
|
b.cc.UpdateState(balancer.State{
|
||||||
|
ConnectivityState: b.state,
|
||||||
|
Picker: b.v2Picker,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
|
||||||
|
// TODO: handle s.ResolverState.Err (log if not nil) once implemented.
|
||||||
|
// TODO: handle s.ResolverState.ServiceConfig?
|
||||||
|
if grpclog.V(2) {
|
||||||
|
grpclog.Infoln("base.baseBalancer: got new ClientConn state: ", s)
|
||||||
|
}
|
||||||
|
if len(s.ResolverState.Addresses) == 0 {
|
||||||
|
b.ResolverError(errors.New("produced zero addresses"))
|
||||||
|
return balancer.ErrBadResolverState
|
||||||
|
}
|
||||||
|
// Successful resolution; clear resolver error and ensure we return nil.
|
||||||
|
b.resolverErr = nil
|
||||||
// addrsSet is the set converted from addrs, it's used for quick lookup of an address.
|
// addrsSet is the set converted from addrs, it's used for quick lookup of an address.
|
||||||
addrsSet := make(map[resolver.Address]struct{})
|
addrsSet := make(map[resolver.Address]struct{})
|
||||||
for _, a := range s.Addresses {
|
for _, a := range s.ResolverState.Addresses {
|
||||||
addrsSet[a] = struct{}{}
|
addrsSet[a] = struct{}{}
|
||||||
if _, ok := b.subConns[a]; !ok {
|
if _, ok := b.subConns[a]; !ok {
|
||||||
// a is a new address (not existing in b.subConns).
|
// a is a new address (not existing in b.subConns).
|
||||||
|
@ -99,17 +144,37 @@ func (b *baseBalancer) UpdateResolverState(s resolver.State) {
|
||||||
// The entry will be deleted in HandleSubConnStateChange.
|
// The entry will be deleted in HandleSubConnStateChange.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergeErrors builds an error from the last connection error and the last
|
||||||
|
// resolver error. Must only be called if b.state is TransientFailure.
|
||||||
|
func (b *baseBalancer) mergeErrors() error {
|
||||||
|
// connErr must always be non-nil unless there are no SubConns, in which
|
||||||
|
// case resolverErr must be non-nil.
|
||||||
|
if b.connErr == nil {
|
||||||
|
return fmt.Errorf("last resolver error: %v", b.resolverErr)
|
||||||
|
}
|
||||||
|
if b.resolverErr == nil {
|
||||||
|
return fmt.Errorf("last connection error: %v", b.connErr)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("last connection error: %v; last resolver error: %v", b.connErr, b.resolverErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// regeneratePicker takes a snapshot of the balancer, and generates a picker
|
// regeneratePicker takes a snapshot of the balancer, and generates a picker
|
||||||
// from it. The picker is
|
// from it. The picker is
|
||||||
// - errPicker with ErrTransientFailure if the balancer is in TransientFailure,
|
// - errPicker if the balancer is in TransientFailure,
|
||||||
// - built by the pickerBuilder with all READY SubConns otherwise.
|
// - built by the pickerBuilder with all READY SubConns otherwise.
|
||||||
func (b *baseBalancer) regeneratePicker() {
|
func (b *baseBalancer) regeneratePicker() {
|
||||||
if b.state == connectivity.TransientFailure {
|
if b.state == connectivity.TransientFailure {
|
||||||
|
if b.pickerBuilder != nil {
|
||||||
b.picker = NewErrPicker(balancer.ErrTransientFailure)
|
b.picker = NewErrPicker(balancer.ErrTransientFailure)
|
||||||
|
} else {
|
||||||
|
b.v2Picker = NewErrPickerV2(balancer.TransientFailureError(b.mergeErrors()))
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if b.pickerBuilder != nil {
|
||||||
readySCs := make(map[resolver.Address]balancer.SubConn)
|
readySCs := make(map[resolver.Address]balancer.SubConn)
|
||||||
|
|
||||||
// Filter out all ready SCs from full subConn map.
|
// Filter out all ready SCs from full subConn map.
|
||||||
|
@ -119,6 +184,17 @@ func (b *baseBalancer) regeneratePicker() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b.picker = b.pickerBuilder.Build(readySCs)
|
b.picker = b.pickerBuilder.Build(readySCs)
|
||||||
|
} else {
|
||||||
|
readySCs := make(map[balancer.SubConn]SubConnInfo)
|
||||||
|
|
||||||
|
// Filter out all ready SCs from full subConn map.
|
||||||
|
for addr, sc := range b.subConns {
|
||||||
|
if st, ok := b.scStates[sc]; ok && st == connectivity.Ready {
|
||||||
|
readySCs[sc] = SubConnInfo{Address: addr}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.v2Picker = b.v2PickerBuilder.Build(PickerBuildInfo{ReadySCs: readySCs})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *baseBalancer) HandleSubConnStateChange(sc balancer.SubConn, s connectivity.State) {
|
func (b *baseBalancer) HandleSubConnStateChange(sc balancer.SubConn, s connectivity.State) {
|
||||||
|
@ -127,10 +203,14 @@ func (b *baseBalancer) HandleSubConnStateChange(sc balancer.SubConn, s connectiv
|
||||||
|
|
||||||
func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
|
func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
|
||||||
s := state.ConnectivityState
|
s := state.ConnectivityState
|
||||||
|
if grpclog.V(2) {
|
||||||
grpclog.Infof("base.baseBalancer: handle SubConn state change: %p, %v", sc, s)
|
grpclog.Infof("base.baseBalancer: handle SubConn state change: %p, %v", sc, s)
|
||||||
|
}
|
||||||
oldS, ok := b.scStates[sc]
|
oldS, ok := b.scStates[sc]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
if grpclog.V(2) {
|
||||||
grpclog.Infof("base.baseBalancer: got state changes for an unknown SubConn: %p, %v", sc, s)
|
grpclog.Infof("base.baseBalancer: got state changes for an unknown SubConn: %p, %v", sc, s)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.scStates[sc] = s
|
b.scStates[sc] = s
|
||||||
|
@ -146,6 +226,9 @@ func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.Su
|
||||||
oldAggrState := b.state
|
oldAggrState := b.state
|
||||||
b.state = b.csEvltr.RecordTransition(oldS, s)
|
b.state = b.csEvltr.RecordTransition(oldS, s)
|
||||||
|
|
||||||
|
// Set or clear the last connection error accordingly.
|
||||||
|
b.connErr = state.ConnectionError
|
||||||
|
|
||||||
// Regenerate picker when one of the following happens:
|
// Regenerate picker when one of the following happens:
|
||||||
// - this sc became ready from not-ready
|
// - this sc became ready from not-ready
|
||||||
// - this sc became not-ready from ready
|
// - this sc became not-ready from ready
|
||||||
|
@ -156,7 +239,11 @@ func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.Su
|
||||||
b.regeneratePicker()
|
b.regeneratePicker()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.picker != nil {
|
||||||
b.cc.UpdateBalancerState(b.state, b.picker)
|
b.cc.UpdateBalancerState(b.state, b.picker)
|
||||||
|
} else {
|
||||||
|
b.cc.UpdateState(balancer.State{ConnectivityState: b.state, Picker: b.v2Picker})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close is a nop because base balancer doesn't have internal state to clean up,
|
// Close is a nop because base balancer doesn't have internal state to clean up,
|
||||||
|
@ -173,6 +260,19 @@ type errPicker struct {
|
||||||
err error // Pick() always returns this err.
|
err error // Pick() always returns this err.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *errPicker) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) {
|
func (p *errPicker) Pick(context.Context, balancer.PickInfo) (balancer.SubConn, func(balancer.DoneInfo), error) {
|
||||||
return nil, nil, p.err
|
return nil, nil, p.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewErrPickerV2 returns a V2Picker that always returns err on Pick().
|
||||||
|
func NewErrPickerV2(err error) balancer.V2Picker {
|
||||||
|
return &errPickerV2{err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
type errPickerV2 struct {
|
||||||
|
err error // Pick() always returns this err.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *errPickerV2) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
|
||||||
|
return balancer.PickResult{}, p.err
|
||||||
|
}
|
||||||
|
|
|
@ -42,6 +42,26 @@ type PickerBuilder interface {
|
||||||
Build(readySCs map[resolver.Address]balancer.SubConn) balancer.Picker
|
Build(readySCs map[resolver.Address]balancer.SubConn) balancer.Picker
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// V2PickerBuilder creates balancer.V2Picker.
|
||||||
|
type V2PickerBuilder interface {
|
||||||
|
// Build returns a picker that will be used by gRPC to pick a SubConn.
|
||||||
|
Build(info PickerBuildInfo) balancer.V2Picker
|
||||||
|
}
|
||||||
|
|
||||||
|
// PickerBuildInfo contains information needed by the picker builder to
|
||||||
|
// construct a picker.
|
||||||
|
type PickerBuildInfo struct {
|
||||||
|
// ReadySCs is a map from all ready SubConns to the Addresses used to
|
||||||
|
// create them.
|
||||||
|
ReadySCs map[balancer.SubConn]SubConnInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubConnInfo contains information about a SubConn created by the base
|
||||||
|
// balancer.
|
||||||
|
type SubConnInfo struct {
|
||||||
|
Address resolver.Address // the address used to create this SubConn
|
||||||
|
}
|
||||||
|
|
||||||
// NewBalancerBuilder returns a balancer builder. The balancers
|
// NewBalancerBuilder returns a balancer builder. The balancers
|
||||||
// built by this builder will use the picker builder to build pickers.
|
// built by this builder will use the picker builder to build pickers.
|
||||||
func NewBalancerBuilder(name string, pb PickerBuilder) balancer.Builder {
|
func NewBalancerBuilder(name string, pb PickerBuilder) balancer.Builder {
|
||||||
|
@ -62,3 +82,12 @@ func NewBalancerBuilderWithConfig(name string, pb PickerBuilder, config Config)
|
||||||
config: config,
|
config: config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewBalancerBuilderV2 returns a base balancer builder configured by the provided config.
|
||||||
|
func NewBalancerBuilderV2(name string, pb V2PickerBuilder, config Config) balancer.Builder {
|
||||||
|
return &baseBuilder{
|
||||||
|
name: name,
|
||||||
|
v2PickerBuilder: pb,
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,14 +22,12 @@
|
||||||
package roundrobin
|
package roundrobin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"google.golang.org/grpc/balancer"
|
"google.golang.org/grpc/balancer"
|
||||||
"google.golang.org/grpc/balancer/base"
|
"google.golang.org/grpc/balancer/base"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
"google.golang.org/grpc/internal/grpcrand"
|
"google.golang.org/grpc/internal/grpcrand"
|
||||||
"google.golang.org/grpc/resolver"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Name is the name of round_robin balancer.
|
// Name is the name of round_robin balancer.
|
||||||
|
@ -37,7 +35,7 @@ const Name = "round_robin"
|
||||||
|
|
||||||
// newBuilder creates a new roundrobin balancer builder.
|
// newBuilder creates a new roundrobin balancer builder.
|
||||||
func newBuilder() balancer.Builder {
|
func newBuilder() balancer.Builder {
|
||||||
return base.NewBalancerBuilderWithConfig(Name, &rrPickerBuilder{}, base.Config{HealthCheck: true})
|
return base.NewBalancerBuilderV2(Name, &rrPickerBuilder{}, base.Config{HealthCheck: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -46,13 +44,13 @@ func init() {
|
||||||
|
|
||||||
type rrPickerBuilder struct{}
|
type rrPickerBuilder struct{}
|
||||||
|
|
||||||
func (*rrPickerBuilder) Build(readySCs map[resolver.Address]balancer.SubConn) balancer.Picker {
|
func (*rrPickerBuilder) Build(info base.PickerBuildInfo) balancer.V2Picker {
|
||||||
grpclog.Infof("roundrobinPicker: newPicker called with readySCs: %v", readySCs)
|
grpclog.Infof("roundrobinPicker: newPicker called with info: %v", info)
|
||||||
if len(readySCs) == 0 {
|
if len(info.ReadySCs) == 0 {
|
||||||
return base.NewErrPicker(balancer.ErrNoSubConnAvailable)
|
return base.NewErrPickerV2(balancer.ErrNoSubConnAvailable)
|
||||||
}
|
}
|
||||||
var scs []balancer.SubConn
|
var scs []balancer.SubConn
|
||||||
for _, sc := range readySCs {
|
for sc := range info.ReadySCs {
|
||||||
scs = append(scs, sc)
|
scs = append(scs, sc)
|
||||||
}
|
}
|
||||||
return &rrPicker{
|
return &rrPicker{
|
||||||
|
@ -74,10 +72,10 @@ type rrPicker struct {
|
||||||
next int
|
next int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *rrPicker) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) {
|
func (p *rrPicker) Pick(balancer.PickInfo) (balancer.PickResult, error) {
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
sc := p.subConns[p.next]
|
sc := p.subConns[p.next]
|
||||||
p.next = (p.next + 1) % len(p.subConns)
|
p.next = (p.next + 1) % len(p.subConns)
|
||||||
p.mu.Unlock()
|
p.mu.Unlock()
|
||||||
return sc, nil, nil
|
return balancer.PickResult{SubConn: sc}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@ 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/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
|
"google.golang.org/grpc/internal/buffer"
|
||||||
|
"google.golang.org/grpc/internal/grpcsync"
|
||||||
"google.golang.org/grpc/resolver"
|
"google.golang.org/grpc/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,64 +34,17 @@ import (
|
||||||
type scStateUpdate struct {
|
type scStateUpdate struct {
|
||||||
sc balancer.SubConn
|
sc balancer.SubConn
|
||||||
state connectivity.State
|
state connectivity.State
|
||||||
}
|
err error
|
||||||
|
|
||||||
// scStateUpdateBuffer is an unbounded channel for scStateChangeTuple.
|
|
||||||
// TODO make a general purpose buffer that uses interface{}.
|
|
||||||
type scStateUpdateBuffer struct {
|
|
||||||
c chan *scStateUpdate
|
|
||||||
mu sync.Mutex
|
|
||||||
backlog []*scStateUpdate
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSCStateUpdateBuffer() *scStateUpdateBuffer {
|
|
||||||
return &scStateUpdateBuffer{
|
|
||||||
c: make(chan *scStateUpdate, 1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *scStateUpdateBuffer) put(t *scStateUpdate) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
if len(b.backlog) == 0 {
|
|
||||||
select {
|
|
||||||
case b.c <- t:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.backlog = append(b.backlog, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *scStateUpdateBuffer) load() {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
if len(b.backlog) > 0 {
|
|
||||||
select {
|
|
||||||
case b.c <- b.backlog[0]:
|
|
||||||
b.backlog[0] = nil
|
|
||||||
b.backlog = b.backlog[1:]
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get returns the channel that the scStateUpdate will be sent to.
|
|
||||||
//
|
|
||||||
// Upon receiving, the caller should call load to send another
|
|
||||||
// scStateChangeTuple onto the channel if there is any.
|
|
||||||
func (b *scStateUpdateBuffer) get() <-chan *scStateUpdate {
|
|
||||||
return b.c
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ccBalancerWrapper is a wrapper on top of cc for balancers.
|
// ccBalancerWrapper is a wrapper on top of cc for balancers.
|
||||||
// It implements balancer.ClientConn interface.
|
// It implements balancer.ClientConn interface.
|
||||||
type ccBalancerWrapper struct {
|
type ccBalancerWrapper struct {
|
||||||
cc *ClientConn
|
cc *ClientConn
|
||||||
|
balancerMu sync.Mutex // synchronizes calls to the balancer
|
||||||
balancer balancer.Balancer
|
balancer balancer.Balancer
|
||||||
stateChangeQueue *scStateUpdateBuffer
|
scBuffer *buffer.Unbounded
|
||||||
resolverUpdateCh chan *resolver.State
|
done *grpcsync.Event
|
||||||
done chan struct{}
|
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
subConns map[*acBalancerWrapper]struct{}
|
subConns map[*acBalancerWrapper]struct{}
|
||||||
|
@ -98,9 +53,8 @@ type ccBalancerWrapper struct {
|
||||||
func newCCBalancerWrapper(cc *ClientConn, b balancer.Builder, bopts balancer.BuildOptions) *ccBalancerWrapper {
|
func newCCBalancerWrapper(cc *ClientConn, b balancer.Builder, bopts balancer.BuildOptions) *ccBalancerWrapper {
|
||||||
ccb := &ccBalancerWrapper{
|
ccb := &ccBalancerWrapper{
|
||||||
cc: cc,
|
cc: cc,
|
||||||
stateChangeQueue: newSCStateUpdateBuffer(),
|
scBuffer: buffer.NewUnbounded(),
|
||||||
resolverUpdateCh: make(chan *resolver.State, 1),
|
done: grpcsync.NewEvent(),
|
||||||
done: make(chan struct{}),
|
|
||||||
subConns: make(map[*acBalancerWrapper]struct{}),
|
subConns: make(map[*acBalancerWrapper]struct{}),
|
||||||
}
|
}
|
||||||
go ccb.watcher()
|
go ccb.watcher()
|
||||||
|
@ -113,36 +67,23 @@ func newCCBalancerWrapper(cc *ClientConn, b balancer.Builder, bopts balancer.Bui
|
||||||
func (ccb *ccBalancerWrapper) watcher() {
|
func (ccb *ccBalancerWrapper) watcher() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case t := <-ccb.stateChangeQueue.get():
|
case t := <-ccb.scBuffer.Get():
|
||||||
ccb.stateChangeQueue.load()
|
ccb.scBuffer.Load()
|
||||||
select {
|
if ccb.done.HasFired() {
|
||||||
case <-ccb.done:
|
break
|
||||||
ccb.balancer.Close()
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
|
ccb.balancerMu.Lock()
|
||||||
|
su := t.(*scStateUpdate)
|
||||||
if ub, ok := ccb.balancer.(balancer.V2Balancer); ok {
|
if ub, ok := ccb.balancer.(balancer.V2Balancer); ok {
|
||||||
ub.UpdateSubConnState(t.sc, balancer.SubConnState{ConnectivityState: t.state})
|
ub.UpdateSubConnState(su.sc, balancer.SubConnState{ConnectivityState: su.state, ConnectionError: su.err})
|
||||||
} else {
|
} else {
|
||||||
ccb.balancer.HandleSubConnStateChange(t.sc, t.state)
|
ccb.balancer.HandleSubConnStateChange(su.sc, su.state)
|
||||||
}
|
}
|
||||||
case s := <-ccb.resolverUpdateCh:
|
ccb.balancerMu.Unlock()
|
||||||
select {
|
case <-ccb.done.Done():
|
||||||
case <-ccb.done:
|
|
||||||
ccb.balancer.Close()
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
if ub, ok := ccb.balancer.(balancer.V2Balancer); ok {
|
|
||||||
ub.UpdateResolverState(*s)
|
|
||||||
} else {
|
|
||||||
ccb.balancer.HandleResolvedAddrs(s.Addresses, nil)
|
|
||||||
}
|
|
||||||
case <-ccb.done:
|
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
if ccb.done.HasFired() {
|
||||||
case <-ccb.done:
|
|
||||||
ccb.balancer.Close()
|
ccb.balancer.Close()
|
||||||
ccb.mu.Lock()
|
ccb.mu.Lock()
|
||||||
scs := ccb.subConns
|
scs := ccb.subConns
|
||||||
|
@ -151,17 +92,17 @@ func (ccb *ccBalancerWrapper) watcher() {
|
||||||
for acbw := range scs {
|
for acbw := range scs {
|
||||||
ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
|
ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
|
||||||
}
|
}
|
||||||
|
ccb.UpdateState(balancer.State{ConnectivityState: connectivity.Connecting, Picker: nil})
|
||||||
return
|
return
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ccb *ccBalancerWrapper) close() {
|
func (ccb *ccBalancerWrapper) close() {
|
||||||
close(ccb.done)
|
ccb.done.Fire()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State) {
|
func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State, err error) {
|
||||||
// When updating addresses for a SubConn, if the address in use is not in
|
// When updating addresses for a SubConn, if the address in use is not in
|
||||||
// the new addresses, the old ac will be tearDown() and a new ac will be
|
// the new addresses, the old ac will be tearDown() and a new ac will be
|
||||||
// created. tearDown() generates a state change with Shutdown state, we
|
// created. tearDown() generates a state change with Shutdown state, we
|
||||||
|
@ -172,29 +113,29 @@ func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s co
|
||||||
if sc == nil {
|
if sc == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ccb.stateChangeQueue.put(&scStateUpdate{
|
ccb.scBuffer.Put(&scStateUpdate{
|
||||||
sc: sc,
|
sc: sc,
|
||||||
state: s,
|
state: s,
|
||||||
|
err: err,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ccb *ccBalancerWrapper) updateResolverState(s resolver.State) {
|
func (ccb *ccBalancerWrapper) updateClientConnState(ccs *balancer.ClientConnState) error {
|
||||||
if ccb.cc.curBalancerName != grpclbName {
|
ccb.balancerMu.Lock()
|
||||||
// Filter any grpclb addresses since we don't have the grpclb balancer.
|
defer ccb.balancerMu.Unlock()
|
||||||
for i := 0; i < len(s.Addresses); {
|
if ub, ok := ccb.balancer.(balancer.V2Balancer); ok {
|
||||||
if s.Addresses[i].Type == resolver.GRPCLB {
|
return ub.UpdateClientConnState(*ccs)
|
||||||
copy(s.Addresses[i:], s.Addresses[i+1:])
|
|
||||||
s.Addresses = s.Addresses[:len(s.Addresses)-1]
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
i++
|
ccb.balancer.HandleResolvedAddrs(ccs.ResolverState.Addresses, nil)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ccb *ccBalancerWrapper) resolverError(err error) {
|
||||||
|
if ub, ok := ccb.balancer.(balancer.V2Balancer); ok {
|
||||||
|
ccb.balancerMu.Lock()
|
||||||
|
ub.ResolverError(err)
|
||||||
|
ccb.balancerMu.Unlock()
|
||||||
}
|
}
|
||||||
select {
|
|
||||||
case <-ccb.resolverUpdateCh:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
ccb.resolverUpdateCh <- &s
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
@ -247,7 +188,22 @@ func (ccb *ccBalancerWrapper) UpdateBalancerState(s connectivity.State, p balanc
|
||||||
ccb.cc.csMgr.updateState(s)
|
ccb.cc.csMgr.updateState(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ccb *ccBalancerWrapper) ResolveNow(o resolver.ResolveNowOption) {
|
func (ccb *ccBalancerWrapper) UpdateState(s balancer.State) {
|
||||||
|
ccb.mu.Lock()
|
||||||
|
defer ccb.mu.Unlock()
|
||||||
|
if ccb.subConns == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 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
|
||||||
|
// 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, and then call the "ready" picker after the picker gets updated.
|
||||||
|
ccb.cc.blockingpicker.updatePickerV2(s.Picker)
|
||||||
|
ccb.cc.csMgr.updateState(s.ConnectivityState)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ccb *ccBalancerWrapper) ResolveNow(o resolver.ResolveNowOptions) {
|
||||||
ccb.cc.resolveNow(o)
|
ccb.cc.resolveNow(o)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
package grpc
|
package grpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"google.golang.org/grpc/balancer"
|
"google.golang.org/grpc/balancer"
|
||||||
|
@ -49,7 +48,7 @@ func (bwb *balancerWrapperBuilder) Build(cc balancer.ClientConn, opts balancer.B
|
||||||
csEvltr: &balancer.ConnectivityStateEvaluator{},
|
csEvltr: &balancer.ConnectivityStateEvaluator{},
|
||||||
state: connectivity.Idle,
|
state: connectivity.Idle,
|
||||||
}
|
}
|
||||||
cc.UpdateBalancerState(connectivity.Idle, bw)
|
cc.UpdateState(balancer.State{ConnectivityState: connectivity.Idle, Picker: bw})
|
||||||
go bw.lbWatcher()
|
go bw.lbWatcher()
|
||||||
return bw
|
return bw
|
||||||
}
|
}
|
||||||
|
@ -243,7 +242,7 @@ func (bw *balancerWrapper) HandleSubConnStateChange(sc balancer.SubConn, s conne
|
||||||
if bw.state != sa {
|
if bw.state != sa {
|
||||||
bw.state = sa
|
bw.state = sa
|
||||||
}
|
}
|
||||||
bw.cc.UpdateBalancerState(bw.state, bw)
|
bw.cc.UpdateState(balancer.State{ConnectivityState: bw.state, Picker: bw})
|
||||||
if s == connectivity.Shutdown {
|
if s == connectivity.Shutdown {
|
||||||
// Remove state for this sc.
|
// Remove state for this sc.
|
||||||
delete(bw.connSt, sc)
|
delete(bw.connSt, sc)
|
||||||
|
@ -275,17 +274,17 @@ func (bw *balancerWrapper) Close() {
|
||||||
|
|
||||||
// The picker is the balancerWrapper itself.
|
// The picker is the balancerWrapper itself.
|
||||||
// It either blocks or returns error, consistent with v1 balancer Get().
|
// It either blocks or returns error, consistent with v1 balancer Get().
|
||||||
func (bw *balancerWrapper) Pick(ctx context.Context, opts balancer.PickOptions) (sc balancer.SubConn, done func(balancer.DoneInfo), err error) {
|
func (bw *balancerWrapper) Pick(info balancer.PickInfo) (result balancer.PickResult, err error) {
|
||||||
failfast := true // Default failfast is true.
|
failfast := true // Default failfast is true.
|
||||||
if ss, ok := rpcInfoFromContext(ctx); ok {
|
if ss, ok := rpcInfoFromContext(info.Ctx); ok {
|
||||||
failfast = ss.failfast
|
failfast = ss.failfast
|
||||||
}
|
}
|
||||||
a, p, err := bw.balancer.Get(ctx, BalancerGetOptions{BlockingWait: !failfast})
|
a, p, err := bw.balancer.Get(info.Ctx, BalancerGetOptions{BlockingWait: !failfast})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return balancer.PickResult{}, toRPCErr(err)
|
||||||
}
|
}
|
||||||
if p != nil {
|
if p != nil {
|
||||||
done = func(balancer.DoneInfo) { p() }
|
result.Done = func(balancer.DoneInfo) { p() }
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p()
|
p()
|
||||||
|
@ -297,38 +296,39 @@ func (bw *balancerWrapper) Pick(ctx context.Context, opts balancer.PickOptions)
|
||||||
defer bw.mu.Unlock()
|
defer bw.mu.Unlock()
|
||||||
if bw.pickfirst {
|
if bw.pickfirst {
|
||||||
// Get the first sc in conns.
|
// Get the first sc in conns.
|
||||||
for _, sc := range bw.conns {
|
for _, result.SubConn = range bw.conns {
|
||||||
return sc, done, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
return nil, nil, balancer.ErrNoSubConnAvailable
|
return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
|
||||||
}
|
}
|
||||||
sc, ok1 := bw.conns[resolver.Address{
|
var ok1 bool
|
||||||
|
result.SubConn, ok1 = bw.conns[resolver.Address{
|
||||||
Addr: a.Addr,
|
Addr: a.Addr,
|
||||||
Type: resolver.Backend,
|
Type: resolver.Backend,
|
||||||
ServerName: "",
|
ServerName: "",
|
||||||
Metadata: a.Metadata,
|
Metadata: a.Metadata,
|
||||||
}]
|
}]
|
||||||
s, ok2 := bw.connSt[sc]
|
s, ok2 := bw.connSt[result.SubConn]
|
||||||
if !ok1 || !ok2 {
|
if !ok1 || !ok2 {
|
||||||
// This can only happen due to a race where Get() returned an address
|
// This can only happen due to a race where Get() returned an address
|
||||||
// that was subsequently removed by Notify. In this case we should
|
// that was subsequently removed by Notify. In this case we should
|
||||||
// retry always.
|
// retry always.
|
||||||
return nil, nil, balancer.ErrNoSubConnAvailable
|
return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
|
||||||
}
|
}
|
||||||
switch s.s {
|
switch s.s {
|
||||||
case connectivity.Ready, connectivity.Idle:
|
case connectivity.Ready, connectivity.Idle:
|
||||||
return sc, done, nil
|
return result, nil
|
||||||
case connectivity.Shutdown, connectivity.TransientFailure:
|
case connectivity.Shutdown, connectivity.TransientFailure:
|
||||||
// If the returned sc has been shut down or is in transient failure,
|
// If the returned sc has been shut down or is in transient failure,
|
||||||
// return error, and this RPC will fail or wait for another picker (if
|
// return error, and this RPC will fail or wait for another picker (if
|
||||||
// non-failfast).
|
// non-failfast).
|
||||||
return nil, nil, balancer.ErrTransientFailure
|
return balancer.PickResult{}, balancer.ErrTransientFailure
|
||||||
default:
|
default:
|
||||||
// For other states (connecting or unknown), the v1 balancer would
|
// For other states (connecting or unknown), the v1 balancer would
|
||||||
// traditionally wait until ready and then issue the RPC. Returning
|
// traditionally wait until ready and then issue the RPC. Returning
|
||||||
// ErrNoSubConnAvailable will be a slight improvement in that it will
|
// ErrNoSubConnAvailable will be a slight improvement in that it will
|
||||||
// allow the balancer to choose another address in case others are
|
// allow the balancer to choose another address in case others are
|
||||||
// connected.
|
// connected.
|
||||||
return nil, nil, balancer.ErrNoSubConnAvailable
|
return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,21 +31,23 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"google.golang.org/grpc/balancer"
|
"google.golang.org/grpc/balancer"
|
||||||
_ "google.golang.org/grpc/balancer/roundrobin" // To register roundrobin.
|
"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/credentials"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
"google.golang.org/grpc/internal/backoff"
|
"google.golang.org/grpc/internal/backoff"
|
||||||
"google.golang.org/grpc/internal/channelz"
|
"google.golang.org/grpc/internal/channelz"
|
||||||
"google.golang.org/grpc/internal/envconfig"
|
|
||||||
"google.golang.org/grpc/internal/grpcsync"
|
"google.golang.org/grpc/internal/grpcsync"
|
||||||
"google.golang.org/grpc/internal/transport"
|
"google.golang.org/grpc/internal/transport"
|
||||||
"google.golang.org/grpc/keepalive"
|
"google.golang.org/grpc/keepalive"
|
||||||
"google.golang.org/grpc/resolver"
|
"google.golang.org/grpc/resolver"
|
||||||
_ "google.golang.org/grpc/resolver/dns" // To register dns resolver.
|
"google.golang.org/grpc/serviceconfig"
|
||||||
_ "google.golang.org/grpc/resolver/passthrough" // To register passthrough resolver.
|
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
_ "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.
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -186,11 +188,11 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
}
|
}
|
||||||
|
|
||||||
if cc.dopts.defaultServiceConfigRawJSON != nil {
|
if cc.dopts.defaultServiceConfigRawJSON != nil {
|
||||||
sc, err := parseServiceConfig(*cc.dopts.defaultServiceConfigRawJSON)
|
scpr := parseServiceConfig(*cc.dopts.defaultServiceConfigRawJSON)
|
||||||
if err != nil {
|
if scpr.Err != nil {
|
||||||
return nil, fmt.Errorf("%s: %v", invalidDefaultServiceConfigErrPrefix, err)
|
return nil, fmt.Errorf("%s: %v", invalidDefaultServiceConfigErrPrefix, scpr.Err)
|
||||||
}
|
}
|
||||||
cc.dopts.defaultServiceConfig = sc
|
cc.dopts.defaultServiceConfig, _ = scpr.Config.(*ServiceConfig)
|
||||||
}
|
}
|
||||||
cc.mkp = cc.dopts.copts.KeepaliveParams
|
cc.mkp = cc.dopts.copts.KeepaliveParams
|
||||||
|
|
||||||
|
@ -235,16 +237,14 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cc.dopts.bs == nil {
|
if cc.dopts.bs == nil {
|
||||||
cc.dopts.bs = backoff.Exponential{
|
cc.dopts.bs = backoff.DefaultExponential
|
||||||
MaxDelay: DefaultBackoffConfig.MaxDelay,
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if cc.dopts.resolverBuilder == nil {
|
// Determine the resolver to use.
|
||||||
// Only try to parse target when resolver builder is not already set.
|
|
||||||
cc.parsedTarget = parseTarget(cc.target)
|
cc.parsedTarget = parseTarget(cc.target)
|
||||||
grpclog.Infof("parsed scheme: %q", cc.parsedTarget.Scheme)
|
grpclog.Infof("parsed scheme: %q", cc.parsedTarget.Scheme)
|
||||||
cc.dopts.resolverBuilder = resolver.Get(cc.parsedTarget.Scheme)
|
resolverBuilder := cc.getResolver(cc.parsedTarget.Scheme)
|
||||||
if cc.dopts.resolverBuilder == nil {
|
if resolverBuilder == nil {
|
||||||
// If resolver builder is still nil, the parsed target's scheme is
|
// If resolver builder is still nil, the parsed target's scheme is
|
||||||
// not registered. Fallback to default resolver and set Endpoint to
|
// not registered. Fallback to default resolver and set Endpoint to
|
||||||
// the original target.
|
// the original target.
|
||||||
|
@ -253,11 +253,12 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
Scheme: resolver.GetDefaultScheme(),
|
Scheme: resolver.GetDefaultScheme(),
|
||||||
Endpoint: target,
|
Endpoint: target,
|
||||||
}
|
}
|
||||||
cc.dopts.resolverBuilder = resolver.Get(cc.parsedTarget.Scheme)
|
resolverBuilder = cc.getResolver(cc.parsedTarget.Scheme)
|
||||||
|
if resolverBuilder == nil {
|
||||||
|
return nil, fmt.Errorf("could not get resolver for default scheme: %q", cc.parsedTarget.Scheme)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
cc.parsedTarget = resolver.Target{Endpoint: target}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
creds := cc.dopts.copts.TransportCredentials
|
creds := cc.dopts.copts.TransportCredentials
|
||||||
if creds != nil && creds.Info().ServerName != "" {
|
if creds != nil && creds.Info().ServerName != "" {
|
||||||
cc.authority = creds.Info().ServerName
|
cc.authority = creds.Info().ServerName
|
||||||
|
@ -297,14 +298,14 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the resolver.
|
// Build the resolver.
|
||||||
rWrapper, err := newCCResolverWrapper(cc)
|
rWrapper, err := newCCResolverWrapper(cc, resolverBuilder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to build resolver: %v", err)
|
return nil, fmt.Errorf("failed to build resolver: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cc.mu.Lock()
|
cc.mu.Lock()
|
||||||
cc.resolverWrapper = rWrapper
|
cc.resolverWrapper = rWrapper
|
||||||
cc.mu.Unlock()
|
cc.mu.Unlock()
|
||||||
|
|
||||||
// A blocking dial blocks until the clientConn is ready.
|
// A blocking dial blocks until the clientConn is ready.
|
||||||
if cc.dopts.block {
|
if cc.dopts.block {
|
||||||
for {
|
for {
|
||||||
|
@ -443,7 +444,32 @@ func (csm *connectivityStateManager) getNotifyChan() <-chan struct{} {
|
||||||
return csm.notifyChan
|
return csm.notifyChan
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientConn represents a client connection to an RPC server.
|
// ClientConnInterface defines the functions clients need to perform unary and
|
||||||
|
// streaming RPCs. It is implemented by *ClientConn, and is only intended to
|
||||||
|
// be referenced by generated code.
|
||||||
|
type ClientConnInterface interface {
|
||||||
|
// Invoke performs a unary RPC and returns after the response is received
|
||||||
|
// into reply.
|
||||||
|
Invoke(ctx context.Context, method string, args interface{}, reply interface{}, opts ...CallOption) error
|
||||||
|
// NewStream begins a streaming RPC.
|
||||||
|
NewStream(ctx context.Context, desc *StreamDesc, method string, opts ...CallOption) (ClientStream, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert *ClientConn implements ClientConnInterface.
|
||||||
|
var _ ClientConnInterface = (*ClientConn)(nil)
|
||||||
|
|
||||||
|
// ClientConn represents a virtual connection to a conceptual endpoint, to
|
||||||
|
// perform RPCs.
|
||||||
|
//
|
||||||
|
// A ClientConn is free to have zero or more actual connections to the endpoint
|
||||||
|
// based on configuration, load, etc. It is also free to determine which actual
|
||||||
|
// endpoints to use and may change it every RPC, permitting client-side load
|
||||||
|
// balancing.
|
||||||
|
//
|
||||||
|
// A ClientConn encapsulates a range of functionality including name
|
||||||
|
// resolution, TCP connection establishment (with retries and backoff) and TLS
|
||||||
|
// handshakes. It also handles errors on established connections by
|
||||||
|
// re-resolving the name and reconnecting.
|
||||||
type ClientConn struct {
|
type ClientConn struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
|
@ -532,84 +558,104 @@ func (cc *ClientConn) waitForResolvedAddrs(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// gRPC should resort to default service config when:
|
var emptyServiceConfig *ServiceConfig
|
||||||
// * resolver service config is disabled
|
|
||||||
// * or, resolver does not return a service config or returns an invalid one.
|
func init() {
|
||||||
func (cc *ClientConn) fallbackToDefaultServiceConfig(sc string) bool {
|
cfg := parseServiceConfig("{}")
|
||||||
if cc.dopts.disableServiceConfig {
|
if cfg.Err != nil {
|
||||||
return true
|
panic(fmt.Sprintf("impossible error parsing empty service config: %v", cfg.Err))
|
||||||
}
|
}
|
||||||
// The logic below is temporary, will be removed once we change the resolver.State ServiceConfig field type.
|
emptyServiceConfig = cfg.Config.(*ServiceConfig)
|
||||||
// Right now, we assume that empty service config string means resolver does not return a config.
|
|
||||||
if sc == "" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// TODO: the logic below is temporary. Once we finish the logic to validate service config
|
|
||||||
// in resolver, we will replace the logic below.
|
|
||||||
_, err := parseServiceConfig(sc)
|
|
||||||
return err != nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cc *ClientConn) updateResolverState(s resolver.State) error {
|
func (cc *ClientConn) maybeApplyDefaultServiceConfig(addrs []resolver.Address) {
|
||||||
|
if cc.sc != nil {
|
||||||
|
cc.applyServiceConfigAndBalancer(cc.sc, addrs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cc.dopts.defaultServiceConfig != nil {
|
||||||
|
cc.applyServiceConfigAndBalancer(cc.dopts.defaultServiceConfig, addrs)
|
||||||
|
} else {
|
||||||
|
cc.applyServiceConfigAndBalancer(emptyServiceConfig, addrs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *ClientConn) updateResolverState(s resolver.State, err error) error {
|
||||||
|
defer cc.firstResolveEvent.Fire()
|
||||||
cc.mu.Lock()
|
cc.mu.Lock()
|
||||||
defer cc.mu.Unlock()
|
|
||||||
// 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.
|
||||||
if cc.conns == nil {
|
if cc.conns == nil {
|
||||||
|
cc.mu.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if cc.fallbackToDefaultServiceConfig(s.ServiceConfig) {
|
|
||||||
if cc.dopts.defaultServiceConfig != nil && cc.sc == nil {
|
|
||||||
cc.applyServiceConfig(cc.dopts.defaultServiceConfig)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO: the parsing logic below will be moved inside resolver.
|
|
||||||
sc, err := parseServiceConfig(s.ServiceConfig)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
// May need to apply the initial service config in case the resolver
|
||||||
}
|
// doesn't support service configs, or doesn't provide a service config
|
||||||
if cc.sc == nil || cc.sc.rawJSONString != s.ServiceConfig {
|
// with the new addresses.
|
||||||
cc.applyServiceConfig(sc)
|
cc.maybeApplyDefaultServiceConfig(nil)
|
||||||
}
|
|
||||||
|
if cc.balancerWrapper != nil {
|
||||||
|
cc.balancerWrapper.resolverError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the service config that will be sent to balancer.
|
// No addresses are valid with err set; return early.
|
||||||
if cc.sc != nil {
|
cc.mu.Unlock()
|
||||||
s.ServiceConfig = cc.sc.rawJSONString
|
return balancer.ErrBadResolverState
|
||||||
}
|
}
|
||||||
|
|
||||||
if cc.dopts.balancerBuilder == nil {
|
var ret error
|
||||||
// Only look at balancer types and switch balancer if balancer dial
|
if cc.dopts.disableServiceConfig || s.ServiceConfig == nil {
|
||||||
// option is not set.
|
cc.maybeApplyDefaultServiceConfig(s.Addresses)
|
||||||
var isGRPCLB bool
|
// TODO: do we need to apply a failing LB policy if there is no
|
||||||
for _, a := range s.Addresses {
|
// default, per the error handling design?
|
||||||
if a.Type == resolver.GRPCLB {
|
|
||||||
isGRPCLB = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var newBalancerName string
|
|
||||||
// TODO: use new loadBalancerConfig field with appropriate priority.
|
|
||||||
if isGRPCLB {
|
|
||||||
newBalancerName = grpclbName
|
|
||||||
} else if cc.sc != nil && cc.sc.LB != nil {
|
|
||||||
newBalancerName = *cc.sc.LB
|
|
||||||
} else {
|
} else {
|
||||||
newBalancerName = PickFirstBalancerName
|
if sc, ok := s.ServiceConfig.Config.(*ServiceConfig); s.ServiceConfig.Err == nil && ok {
|
||||||
|
cc.applyServiceConfigAndBalancer(sc, s.Addresses)
|
||||||
|
} else {
|
||||||
|
ret = balancer.ErrBadResolverState
|
||||||
|
if cc.balancerWrapper == nil {
|
||||||
|
var err error
|
||||||
|
if s.ServiceConfig.Err != nil {
|
||||||
|
err = status.Errorf(codes.Unavailable, "error parsing service config: %v", s.ServiceConfig.Err)
|
||||||
|
} else {
|
||||||
|
err = status.Errorf(codes.Unavailable, "illegal service config type: %T", s.ServiceConfig.Config)
|
||||||
|
}
|
||||||
|
cc.blockingpicker.updatePicker(base.NewErrPicker(err))
|
||||||
|
cc.csMgr.updateState(connectivity.TransientFailure)
|
||||||
|
cc.mu.Unlock()
|
||||||
|
return ret
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cc.switchBalancer(newBalancerName)
|
|
||||||
} else if cc.balancerWrapper == nil {
|
|
||||||
// Balancer dial option was set, and this is the first time handling
|
|
||||||
// resolved addresses. Build a balancer with dopts.balancerBuilder.
|
|
||||||
cc.curBalancerName = cc.dopts.balancerBuilder.Name()
|
|
||||||
cc.balancerWrapper = newCCBalancerWrapper(cc, cc.dopts.balancerBuilder, cc.balancerBuildOpts)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cc.balancerWrapper.updateResolverState(s)
|
var balCfg serviceconfig.LoadBalancingConfig
|
||||||
cc.firstResolveEvent.Fire()
|
if cc.dopts.balancerBuilder == nil && cc.sc != nil && cc.sc.lbConfig != nil {
|
||||||
return nil
|
balCfg = cc.sc.lbConfig.cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
cbn := cc.curBalancerName
|
||||||
|
bw := cc.balancerWrapper
|
||||||
|
cc.mu.Unlock()
|
||||||
|
if cbn != grpclbName {
|
||||||
|
// Filter any grpclb addresses since we don't have the grpclb balancer.
|
||||||
|
for i := 0; i < len(s.Addresses); {
|
||||||
|
if s.Addresses[i].Type == resolver.GRPCLB {
|
||||||
|
copy(s.Addresses[i:], s.Addresses[i+1:])
|
||||||
|
s.Addresses = s.Addresses[:len(s.Addresses)-1]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uccsErr := bw.updateClientConnState(&balancer.ClientConnState{ResolverState: s, BalancerConfig: balCfg})
|
||||||
|
if ret == nil {
|
||||||
|
ret = uccsErr // prefer ErrBadResolver state since any other error is
|
||||||
|
// currently meaningless to the caller.
|
||||||
|
}
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// switchBalancer starts the switching from current balancer to the balancer
|
// switchBalancer starts the switching from current balancer to the balancer
|
||||||
|
@ -621,7 +667,7 @@ func (cc *ClientConn) updateResolverState(s resolver.State) error {
|
||||||
//
|
//
|
||||||
// Caller must hold cc.mu.
|
// Caller must hold cc.mu.
|
||||||
func (cc *ClientConn) switchBalancer(name string) {
|
func (cc *ClientConn) switchBalancer(name string) {
|
||||||
if strings.ToLower(cc.curBalancerName) == strings.ToLower(name) {
|
if strings.EqualFold(cc.curBalancerName, name) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -657,7 +703,7 @@ func (cc *ClientConn) switchBalancer(name string) {
|
||||||
cc.balancerWrapper = newCCBalancerWrapper(cc, builder, cc.balancerBuildOpts)
|
cc.balancerWrapper = newCCBalancerWrapper(cc, builder, cc.balancerBuildOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cc *ClientConn) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State) {
|
func (cc *ClientConn) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State, err error) {
|
||||||
cc.mu.Lock()
|
cc.mu.Lock()
|
||||||
if cc.conns == nil {
|
if cc.conns == nil {
|
||||||
cc.mu.Unlock()
|
cc.mu.Unlock()
|
||||||
|
@ -665,7 +711,7 @@ func (cc *ClientConn) handleSubConnStateChange(sc balancer.SubConn, s connectivi
|
||||||
}
|
}
|
||||||
// TODO(bar switching) send updates to all balancer wrappers when balancer
|
// TODO(bar switching) send updates to all balancer wrappers when balancer
|
||||||
// gracefully switching is supported.
|
// gracefully switching is supported.
|
||||||
cc.balancerWrapper.handleSubConnStateChange(sc, s)
|
cc.balancerWrapper.handleSubConnStateChange(sc, s, err)
|
||||||
cc.mu.Unlock()
|
cc.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -760,7 +806,9 @@ func (ac *addrConn) connect() error {
|
||||||
ac.mu.Unlock()
|
ac.mu.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ac.updateConnectivityState(connectivity.Connecting)
|
// Update connectivity state within the lock to prevent subsequent or
|
||||||
|
// concurrent calls from resetting the transport more than once.
|
||||||
|
ac.updateConnectivityState(connectivity.Connecting, nil)
|
||||||
ac.mu.Unlock()
|
ac.mu.Unlock()
|
||||||
|
|
||||||
// Start a goroutine connecting to the server asynchronously.
|
// Start a goroutine connecting to the server asynchronously.
|
||||||
|
@ -770,7 +818,16 @@ func (ac *addrConn) connect() error {
|
||||||
|
|
||||||
// tryUpdateAddrs tries to update ac.addrs with the new addresses list.
|
// tryUpdateAddrs tries to update ac.addrs with the new addresses list.
|
||||||
//
|
//
|
||||||
// It checks whether current connected address of ac is in the new addrs list.
|
// If ac is Connecting, it returns false. The caller should tear down the ac and
|
||||||
|
// create a new one. Note that the backoff will be reset when this happens.
|
||||||
|
//
|
||||||
|
// If ac is TransientFailure, it updates ac.addrs and returns true. The updated
|
||||||
|
// addresses will be picked up by retry in the next iteration after backoff.
|
||||||
|
//
|
||||||
|
// If ac is Shutdown or Idle, it updates ac.addrs and returns true.
|
||||||
|
//
|
||||||
|
// If ac is Ready, it checks whether current connected address of ac is in the
|
||||||
|
// new addrs list.
|
||||||
// - If true, it updates ac.addrs and returns true. The ac will keep using
|
// - If true, it updates ac.addrs and returns true. The ac will keep using
|
||||||
// the existing connection.
|
// the existing connection.
|
||||||
// - If false, it does nothing and returns false.
|
// - If false, it does nothing and returns false.
|
||||||
|
@ -778,17 +835,18 @@ func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool {
|
||||||
ac.mu.Lock()
|
ac.mu.Lock()
|
||||||
defer ac.mu.Unlock()
|
defer ac.mu.Unlock()
|
||||||
grpclog.Infof("addrConn: tryUpdateAddrs curAddr: %v, addrs: %v", ac.curAddr, addrs)
|
grpclog.Infof("addrConn: tryUpdateAddrs curAddr: %v, addrs: %v", ac.curAddr, addrs)
|
||||||
if ac.state == connectivity.Shutdown {
|
if ac.state == connectivity.Shutdown ||
|
||||||
|
ac.state == connectivity.TransientFailure ||
|
||||||
|
ac.state == connectivity.Idle {
|
||||||
ac.addrs = addrs
|
ac.addrs = addrs
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unless we're busy reconnecting already, let's reconnect from the top of
|
if ac.state == connectivity.Connecting {
|
||||||
// the list.
|
|
||||||
if ac.state != connectivity.Ready {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ac.state is Ready, try to find the connected address.
|
||||||
var curAddrFound bool
|
var curAddrFound bool
|
||||||
for _, a := range addrs {
|
for _, a := range addrs {
|
||||||
if reflect.DeepEqual(ac.curAddr, a) {
|
if reflect.DeepEqual(ac.curAddr, a) {
|
||||||
|
@ -836,7 +894,8 @@ func (cc *ClientConn) healthCheckConfig() *healthCheckConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cc *ClientConn) getTransport(ctx context.Context, failfast bool, method string) (transport.ClientTransport, func(balancer.DoneInfo), error) {
|
func (cc *ClientConn) getTransport(ctx context.Context, failfast bool, method string) (transport.ClientTransport, func(balancer.DoneInfo), error) {
|
||||||
t, done, err := cc.blockingpicker.pick(ctx, failfast, balancer.PickOptions{
|
t, done, err := cc.blockingpicker.pick(ctx, failfast, balancer.PickInfo{
|
||||||
|
Ctx: ctx,
|
||||||
FullMethodName: method,
|
FullMethodName: method,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -845,10 +904,10 @@ func (cc *ClientConn) getTransport(ctx context.Context, failfast bool, method st
|
||||||
return t, done, nil
|
return t, done, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cc *ClientConn) applyServiceConfig(sc *ServiceConfig) error {
|
func (cc *ClientConn) applyServiceConfigAndBalancer(sc *ServiceConfig, addrs []resolver.Address) {
|
||||||
if sc == nil {
|
if sc == nil {
|
||||||
// should never reach here.
|
// should never reach here.
|
||||||
return fmt.Errorf("got nil pointer for service config")
|
return
|
||||||
}
|
}
|
||||||
cc.sc = sc
|
cc.sc = sc
|
||||||
|
|
||||||
|
@ -864,10 +923,38 @@ func (cc *ClientConn) applyServiceConfig(sc *ServiceConfig) error {
|
||||||
cc.retryThrottler.Store((*retryThrottler)(nil))
|
cc.retryThrottler.Store((*retryThrottler)(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
if cc.dopts.balancerBuilder == nil {
|
||||||
|
// Only look at balancer types and switch balancer if balancer dial
|
||||||
|
// option is not set.
|
||||||
|
var newBalancerName string
|
||||||
|
if cc.sc != nil && cc.sc.lbConfig != nil {
|
||||||
|
newBalancerName = cc.sc.lbConfig.name
|
||||||
|
} else {
|
||||||
|
var isGRPCLB bool
|
||||||
|
for _, a := range addrs {
|
||||||
|
if a.Type == resolver.GRPCLB {
|
||||||
|
isGRPCLB = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isGRPCLB {
|
||||||
|
newBalancerName = grpclbName
|
||||||
|
} else if cc.sc != nil && cc.sc.LB != nil {
|
||||||
|
newBalancerName = *cc.sc.LB
|
||||||
|
} else {
|
||||||
|
newBalancerName = PickFirstBalancerName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cc.switchBalancer(newBalancerName)
|
||||||
|
} else if cc.balancerWrapper == nil {
|
||||||
|
// Balancer dial option was set, and this is the first time handling
|
||||||
|
// resolved addresses. Build a balancer with dopts.balancerBuilder.
|
||||||
|
cc.curBalancerName = cc.dopts.balancerBuilder.Name()
|
||||||
|
cc.balancerWrapper = newCCBalancerWrapper(cc, cc.dopts.balancerBuilder, cc.balancerBuildOpts)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cc *ClientConn) resolveNow(o resolver.ResolveNowOption) {
|
func (cc *ClientConn) resolveNow(o resolver.ResolveNowOptions) {
|
||||||
cc.mu.RLock()
|
cc.mu.RLock()
|
||||||
r := cc.resolverWrapper
|
r := cc.resolverWrapper
|
||||||
cc.mu.RUnlock()
|
cc.mu.RUnlock()
|
||||||
|
@ -889,8 +976,9 @@ func (cc *ClientConn) resolveNow(o resolver.ResolveNowOption) {
|
||||||
// This API is EXPERIMENTAL.
|
// This API is EXPERIMENTAL.
|
||||||
func (cc *ClientConn) ResetConnectBackoff() {
|
func (cc *ClientConn) ResetConnectBackoff() {
|
||||||
cc.mu.Lock()
|
cc.mu.Lock()
|
||||||
defer cc.mu.Unlock()
|
conns := cc.conns
|
||||||
for ac := range cc.conns {
|
cc.mu.Unlock()
|
||||||
|
for ac := range conns {
|
||||||
ac.resetConnectBackoff()
|
ac.resetConnectBackoff()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -976,7 +1064,7 @@ type addrConn struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: this requires a lock on ac.mu.
|
// Note: this requires a lock on ac.mu.
|
||||||
func (ac *addrConn) updateConnectivityState(s connectivity.State) {
|
func (ac *addrConn) updateConnectivityState(s connectivity.State, lastErr error) {
|
||||||
if ac.state == s {
|
if ac.state == s {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -989,7 +1077,7 @@ func (ac *addrConn) updateConnectivityState(s connectivity.State) {
|
||||||
Severity: channelz.CtINFO,
|
Severity: channelz.CtINFO,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ac.cc.handleSubConnStateChange(ac.acbw, s)
|
ac.cc.handleSubConnStateChange(ac.acbw, s, lastErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// adjustParams updates parameters used to create transports upon
|
// adjustParams updates parameters used to create transports upon
|
||||||
|
@ -1009,7 +1097,7 @@ func (ac *addrConn) adjustParams(r transport.GoAwayReason) {
|
||||||
func (ac *addrConn) resetTransport() {
|
func (ac *addrConn) resetTransport() {
|
||||||
for i := 0; ; i++ {
|
for i := 0; ; i++ {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
ac.cc.resolveNow(resolver.ResolveNowOption{})
|
ac.cc.resolveNow(resolver.ResolveNowOptions{})
|
||||||
}
|
}
|
||||||
|
|
||||||
ac.mu.Lock()
|
ac.mu.Lock()
|
||||||
|
@ -1037,6 +1125,9 @@ func (ac *addrConn) resetTransport() {
|
||||||
// The spec doesn't mention what should be done for multiple addresses.
|
// The spec doesn't mention what should be done for multiple addresses.
|
||||||
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md#proposed-backoff-algorithm
|
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md#proposed-backoff-algorithm
|
||||||
connectDeadline := time.Now().Add(dialDuration)
|
connectDeadline := time.Now().Add(dialDuration)
|
||||||
|
|
||||||
|
ac.updateConnectivityState(connectivity.Connecting, nil)
|
||||||
|
ac.transport = nil
|
||||||
ac.mu.Unlock()
|
ac.mu.Unlock()
|
||||||
|
|
||||||
newTr, addr, reconnect, err := ac.tryAllAddrs(addrs, connectDeadline)
|
newTr, addr, reconnect, err := ac.tryAllAddrs(addrs, connectDeadline)
|
||||||
|
@ -1048,7 +1139,7 @@ func (ac *addrConn) resetTransport() {
|
||||||
ac.mu.Unlock()
|
ac.mu.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ac.updateConnectivityState(connectivity.TransientFailure)
|
ac.updateConnectivityState(connectivity.TransientFailure, err)
|
||||||
|
|
||||||
// Backoff.
|
// Backoff.
|
||||||
b := ac.resetBackoff
|
b := ac.resetBackoff
|
||||||
|
@ -1071,55 +1162,32 @@ func (ac *addrConn) resetTransport() {
|
||||||
|
|
||||||
ac.mu.Lock()
|
ac.mu.Lock()
|
||||||
if ac.state == connectivity.Shutdown {
|
if ac.state == connectivity.Shutdown {
|
||||||
newTr.Close()
|
|
||||||
ac.mu.Unlock()
|
ac.mu.Unlock()
|
||||||
|
newTr.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ac.curAddr = addr
|
ac.curAddr = addr
|
||||||
ac.transport = newTr
|
ac.transport = newTr
|
||||||
ac.backoffIdx = 0
|
ac.backoffIdx = 0
|
||||||
|
|
||||||
healthCheckConfig := ac.cc.healthCheckConfig()
|
|
||||||
// LB channel health checking is only enabled when all the four requirements below are met:
|
|
||||||
// 1. it is not disabled by the user with the WithDisableHealthCheck DialOption,
|
|
||||||
// 2. the internal.HealthCheckFunc is set by importing the grpc/healthcheck package,
|
|
||||||
// 3. a service config with non-empty healthCheckConfig field is provided,
|
|
||||||
// 4. the current load balancer allows it.
|
|
||||||
hctx, hcancel := context.WithCancel(ac.ctx)
|
hctx, hcancel := context.WithCancel(ac.ctx)
|
||||||
healthcheckManagingState := false
|
ac.startHealthCheck(hctx)
|
||||||
if !ac.cc.dopts.disableHealthCheck && healthCheckConfig != nil && ac.scopts.HealthCheckEnabled {
|
|
||||||
if ac.cc.dopts.healthCheckFunc == nil {
|
|
||||||
// TODO: add a link to the health check doc in the error message.
|
|
||||||
grpclog.Error("the client side LB channel health check function has not been set.")
|
|
||||||
} else {
|
|
||||||
// TODO(deklerk) refactor to just return transport
|
|
||||||
go ac.startHealthCheck(hctx, newTr, addr, healthCheckConfig.ServiceName)
|
|
||||||
healthcheckManagingState = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !healthcheckManagingState {
|
|
||||||
ac.updateConnectivityState(connectivity.Ready)
|
|
||||||
}
|
|
||||||
ac.mu.Unlock()
|
ac.mu.Unlock()
|
||||||
|
|
||||||
// Block until the created transport is down. And when this happens,
|
// Block until the created transport is down. And when this happens,
|
||||||
// we restart from the top of the addr list.
|
// we restart from the top of the addr list.
|
||||||
<-reconnect.Done()
|
<-reconnect.Done()
|
||||||
hcancel()
|
hcancel()
|
||||||
|
// restart connecting - the top of the loop will set state to
|
||||||
// Need to reconnect after a READY, the addrConn enters
|
// CONNECTING. This is against the current connectivity semantics doc,
|
||||||
// TRANSIENT_FAILURE.
|
// however it allows for graceful behavior for RPCs not yet dispatched
|
||||||
|
// - unfortunate timing would otherwise lead to the RPC failing even
|
||||||
|
// though the TRANSIENT_FAILURE state (called for by the doc) would be
|
||||||
|
// instantaneous.
|
||||||
//
|
//
|
||||||
// This will set addrConn to TRANSIENT_FAILURE for a very short period
|
// Ideally we should transition to Idle here and block until there is
|
||||||
// of time, and turns CONNECTING. It seems reasonable to skip this, but
|
// RPC activity that leads to the balancer requesting a reconnect of
|
||||||
// READY-CONNECTING is not a valid transition.
|
// the associated SubConn.
|
||||||
ac.mu.Lock()
|
|
||||||
if ac.state == connectivity.Shutdown {
|
|
||||||
ac.mu.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ac.updateConnectivityState(connectivity.TransientFailure)
|
|
||||||
ac.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1127,14 +1195,13 @@ func (ac *addrConn) resetTransport() {
|
||||||
// first successful one. It returns the transport, the address and a Event in
|
// first successful one. It returns the transport, the address and a Event in
|
||||||
// the successful case. The Event fires when the returned transport disconnects.
|
// the successful case. The Event fires when the returned transport disconnects.
|
||||||
func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.Time) (transport.ClientTransport, resolver.Address, *grpcsync.Event, error) {
|
func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.Time) (transport.ClientTransport, resolver.Address, *grpcsync.Event, error) {
|
||||||
|
var firstConnErr error
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
ac.mu.Lock()
|
ac.mu.Lock()
|
||||||
if ac.state == connectivity.Shutdown {
|
if ac.state == connectivity.Shutdown {
|
||||||
ac.mu.Unlock()
|
ac.mu.Unlock()
|
||||||
return nil, resolver.Address{}, nil, errConnClosing
|
return nil, resolver.Address{}, nil, errConnClosing
|
||||||
}
|
}
|
||||||
ac.updateConnectivityState(connectivity.Connecting)
|
|
||||||
ac.transport = nil
|
|
||||||
|
|
||||||
ac.cc.mu.RLock()
|
ac.cc.mu.RLock()
|
||||||
ac.dopts.copts.KeepaliveParams = ac.cc.mkp
|
ac.dopts.copts.KeepaliveParams = ac.cc.mkp
|
||||||
|
@ -1157,11 +1224,14 @@ func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.T
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return newTr, addr, reconnect, nil
|
return newTr, addr, reconnect, nil
|
||||||
}
|
}
|
||||||
|
if firstConnErr == nil {
|
||||||
|
firstConnErr = err
|
||||||
|
}
|
||||||
ac.cc.blockingpicker.updateConnectionError(err)
|
ac.cc.blockingpicker.updateConnectionError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Couldn't connect to any address.
|
// Couldn't connect to any address.
|
||||||
return nil, resolver.Address{}, nil, fmt.Errorf("couldn't connect to any address")
|
return nil, resolver.Address{}, nil, firstConnErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// createTransport creates a connection to addr. It returns the transport and a
|
// createTransport creates a connection to addr. It returns the transport and a
|
||||||
|
@ -1172,20 +1242,47 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne
|
||||||
onCloseCalled := make(chan struct{})
|
onCloseCalled := make(chan struct{})
|
||||||
reconnect := grpcsync.NewEvent()
|
reconnect := grpcsync.NewEvent()
|
||||||
|
|
||||||
|
authority := ac.cc.authority
|
||||||
|
// addr.ServerName takes precedent over ClientConn authority, if present.
|
||||||
|
if addr.ServerName != "" {
|
||||||
|
authority = addr.ServerName
|
||||||
|
}
|
||||||
|
|
||||||
target := transport.TargetInfo{
|
target := transport.TargetInfo{
|
||||||
Addr: addr.Addr,
|
Addr: addr.Addr,
|
||||||
Metadata: addr.Metadata,
|
Metadata: addr.Metadata,
|
||||||
Authority: ac.cc.authority,
|
Authority: authority,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
once := sync.Once{}
|
||||||
onGoAway := func(r transport.GoAwayReason) {
|
onGoAway := func(r transport.GoAwayReason) {
|
||||||
ac.mu.Lock()
|
ac.mu.Lock()
|
||||||
ac.adjustParams(r)
|
ac.adjustParams(r)
|
||||||
|
once.Do(func() {
|
||||||
|
if ac.state == connectivity.Ready {
|
||||||
|
// Prevent this SubConn from being used for new RPCs by setting its
|
||||||
|
// state to Connecting.
|
||||||
|
//
|
||||||
|
// TODO: this should be Idle when grpc-go properly supports it.
|
||||||
|
ac.updateConnectivityState(connectivity.Connecting, nil)
|
||||||
|
}
|
||||||
|
})
|
||||||
ac.mu.Unlock()
|
ac.mu.Unlock()
|
||||||
reconnect.Fire()
|
reconnect.Fire()
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose := func() {
|
onClose := func() {
|
||||||
|
ac.mu.Lock()
|
||||||
|
once.Do(func() {
|
||||||
|
if ac.state == connectivity.Ready {
|
||||||
|
// Prevent this SubConn from being used for new RPCs by setting its
|
||||||
|
// state to Connecting.
|
||||||
|
//
|
||||||
|
// TODO: this should be Idle when grpc-go properly supports it.
|
||||||
|
ac.updateConnectivityState(connectivity.Connecting, nil)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
ac.mu.Unlock()
|
||||||
close(onCloseCalled)
|
close(onCloseCalled)
|
||||||
reconnect.Fire()
|
reconnect.Fire()
|
||||||
}
|
}
|
||||||
|
@ -1207,9 +1304,8 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if ac.dopts.reqHandshake == envconfig.RequireHandshakeOn {
|
|
||||||
select {
|
select {
|
||||||
case <-time.After(connectDeadline.Sub(time.Now())):
|
case <-time.After(time.Until(connectDeadline)):
|
||||||
// We didn't get the preface in time.
|
// We didn't get the preface in time.
|
||||||
newTr.Close()
|
newTr.Close()
|
||||||
grpclog.Warningf("grpc: addrConn.createTransport failed to connect to %v: didn't receive server preface in time. Reconnecting...", addr)
|
grpclog.Warningf("grpc: addrConn.createTransport failed to connect to %v: didn't receive server preface in time. Reconnecting...", addr)
|
||||||
|
@ -1221,33 +1317,72 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne
|
||||||
return nil, nil, errors.New("connection closed")
|
return nil, nil, errors.New("connection closed")
|
||||||
// TODO(deklerk) this should bail on ac.ctx.Done(). Add a test and fix.
|
// TODO(deklerk) this should bail on ac.ctx.Done(). Add a test and fix.
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return newTr, reconnect, nil
|
return newTr, reconnect, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ac *addrConn) startHealthCheck(ctx context.Context, newTr transport.ClientTransport, addr resolver.Address, serviceName string) {
|
// startHealthCheck starts the health checking stream (RPC) to watch the health
|
||||||
// Set up the health check helper functions
|
// stats of this connection if health checking is requested and configured.
|
||||||
newStream := func() (interface{}, error) {
|
//
|
||||||
return ac.newClientStream(ctx, &StreamDesc{ServerStreams: true}, "/grpc.health.v1.Health/Watch", newTr)
|
// LB channel health checking is enabled when all requirements below are met:
|
||||||
|
// 1. it is not disabled by the user with the WithDisableHealthCheck DialOption
|
||||||
|
// 2. internal.HealthCheckFunc is set by importing the grpc/healthcheck package
|
||||||
|
// 3. a service config with non-empty healthCheckConfig field is provided
|
||||||
|
// 4. the load balancer requests it
|
||||||
|
//
|
||||||
|
// It sets addrConn to READY if the health checking stream is not started.
|
||||||
|
//
|
||||||
|
// Caller must hold ac.mu.
|
||||||
|
func (ac *addrConn) startHealthCheck(ctx context.Context) {
|
||||||
|
var healthcheckManagingState bool
|
||||||
|
defer func() {
|
||||||
|
if !healthcheckManagingState {
|
||||||
|
ac.updateConnectivityState(connectivity.Ready, nil)
|
||||||
}
|
}
|
||||||
firstReady := true
|
}()
|
||||||
reportHealth := func(ok bool) {
|
|
||||||
ac.mu.Lock()
|
if ac.cc.dopts.disableHealthCheck {
|
||||||
defer ac.mu.Unlock()
|
|
||||||
if ac.transport != newTr {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ok {
|
healthCheckConfig := ac.cc.healthCheckConfig()
|
||||||
if firstReady {
|
if healthCheckConfig == nil {
|
||||||
firstReady = false
|
return
|
||||||
ac.curAddr = addr
|
|
||||||
}
|
}
|
||||||
ac.updateConnectivityState(connectivity.Ready)
|
if !ac.scopts.HealthCheckEnabled {
|
||||||
} else {
|
return
|
||||||
ac.updateConnectivityState(connectivity.TransientFailure)
|
|
||||||
}
|
}
|
||||||
|
healthCheckFunc := ac.cc.dopts.healthCheckFunc
|
||||||
|
if healthCheckFunc == nil {
|
||||||
|
// The health package is not imported to set health check function.
|
||||||
|
//
|
||||||
|
// TODO: add a link to the health check doc in the error message.
|
||||||
|
grpclog.Error("Health check is requested but health check function is not set.")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
err := ac.cc.dopts.healthCheckFunc(ctx, newStream, reportHealth, serviceName)
|
|
||||||
|
healthcheckManagingState = true
|
||||||
|
|
||||||
|
// Set up the health check helper functions.
|
||||||
|
currentTr := ac.transport
|
||||||
|
newStream := func(method string) (interface{}, error) {
|
||||||
|
ac.mu.Lock()
|
||||||
|
if ac.transport != currentTr {
|
||||||
|
ac.mu.Unlock()
|
||||||
|
return nil, status.Error(codes.Canceled, "the provided transport is no longer valid to use")
|
||||||
|
}
|
||||||
|
ac.mu.Unlock()
|
||||||
|
return newNonRetryClientStream(ctx, &StreamDesc{ServerStreams: true}, method, currentTr, ac)
|
||||||
|
}
|
||||||
|
setConnectivityState := func(s connectivity.State, lastErr error) {
|
||||||
|
ac.mu.Lock()
|
||||||
|
defer ac.mu.Unlock()
|
||||||
|
if ac.transport != currentTr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ac.updateConnectivityState(s, lastErr)
|
||||||
|
}
|
||||||
|
// Start the health checking stream.
|
||||||
|
go func() {
|
||||||
|
err := ac.cc.dopts.healthCheckFunc(ctx, newStream, setConnectivityState, healthCheckConfig.ServiceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if status.Code(err) == codes.Unimplemented {
|
if status.Code(err) == codes.Unimplemented {
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
|
@ -1261,6 +1396,7 @@ func (ac *addrConn) startHealthCheck(ctx context.Context, newTr transport.Client
|
||||||
grpclog.Errorf("HealthCheckFunc exits with unexpected error %v", err)
|
grpclog.Errorf("HealthCheckFunc exits with unexpected error %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ac *addrConn) resetConnectBackoff() {
|
func (ac *addrConn) resetConnectBackoff() {
|
||||||
|
@ -1307,8 +1443,8 @@ func (ac *addrConn) tearDown(err error) {
|
||||||
curTr := ac.transport
|
curTr := ac.transport
|
||||||
ac.transport = nil
|
ac.transport = nil
|
||||||
// We have to set the state to Shutdown before anything else to prevent races
|
// We have to set the state to Shutdown before anything else to prevent races
|
||||||
// between setting the state and logic that waits on context cancelation / etc.
|
// between setting the state and logic that waits on context cancellation / etc.
|
||||||
ac.updateConnectivityState(connectivity.Shutdown)
|
ac.updateConnectivityState(connectivity.Shutdown, nil)
|
||||||
ac.cancel()
|
ac.cancel()
|
||||||
ac.curAddr = resolver.Address{}
|
ac.curAddr = resolver.Address{}
|
||||||
if err == errConnDrain && curTr != nil {
|
if err == errConnDrain && curTr != nil {
|
||||||
|
@ -1331,7 +1467,7 @@ func (ac *addrConn) tearDown(err error) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
// TraceEvent needs to be called before RemoveEntry, as TraceEvent may add trace reference to
|
// TraceEvent needs to be called before RemoveEntry, as TraceEvent may add trace reference to
|
||||||
// the entity beng deleted, and thus prevent it from being deleted right away.
|
// the entity being deleted, and thus prevent it from being deleted right away.
|
||||||
channelz.RemoveEntry(ac.channelzID)
|
channelz.RemoveEntry(ac.channelzID)
|
||||||
}
|
}
|
||||||
ac.mu.Unlock()
|
ac.mu.Unlock()
|
||||||
|
@ -1421,3 +1557,12 @@ func (c *channelzChannel) ChannelzMetric() *channelz.ChannelInternalMetric {
|
||||||
// Deprecated: This error is never returned by grpc and should not be
|
// Deprecated: This error is never returned by grpc and should not be
|
||||||
// referenced by users.
|
// referenced by users.
|
||||||
var ErrClientConnTimeout = errors.New("grpc: timed out when dialing")
|
var ErrClientConnTimeout = errors.New("grpc: timed out when dialing")
|
||||||
|
|
||||||
|
func (cc *ClientConn) getResolver(scheme string) resolver.Builder {
|
||||||
|
for _, rb := range cc.dopts.resolvers {
|
||||||
|
if cc.parsedTarget.Scheme == rb.Scheme() {
|
||||||
|
return rb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resolver.Get(cc.parsedTarget.Scheme)
|
||||||
|
}
|
||||||
|
|
|
@ -24,16 +24,12 @@ package credentials // import "google.golang.org/grpc/credentials"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
"google.golang.org/grpc/credentials/internal"
|
"google.golang.org/grpc/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PerRPCCredentials defines the common interface for the credentials which need to
|
// PerRPCCredentials defines the common interface for the credentials which need to
|
||||||
|
@ -45,7 +41,8 @@ type PerRPCCredentials interface {
|
||||||
// context. If a status code is returned, it will be used as the status
|
// context. If a status code is returned, it will be used as the status
|
||||||
// for the RPC. uri is the URI of the entry point for the request.
|
// for the RPC. uri is the URI of the entry point for the request.
|
||||||
// When supported by the underlying implementation, ctx can be used for
|
// When supported by the underlying implementation, ctx can be used for
|
||||||
// timeout and cancellation.
|
// timeout and cancellation. Additionally, RequestInfo data will be
|
||||||
|
// available via ctx to this call.
|
||||||
// TODO(zhaoq): Define the set of the qualified keys instead of leaving
|
// TODO(zhaoq): Define the set of the qualified keys instead of leaving
|
||||||
// it as an arbitrary string.
|
// it as an arbitrary string.
|
||||||
GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
|
GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
|
||||||
|
@ -54,6 +51,48 @@ type PerRPCCredentials interface {
|
||||||
RequireTransportSecurity() bool
|
RequireTransportSecurity() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SecurityLevel defines the protection level on an established connection.
|
||||||
|
//
|
||||||
|
// This API is experimental.
|
||||||
|
type SecurityLevel int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NoSecurity indicates a connection is insecure.
|
||||||
|
// The zero SecurityLevel value is invalid for backward compatibility.
|
||||||
|
NoSecurity SecurityLevel = iota + 1
|
||||||
|
// IntegrityOnly indicates a connection only provides integrity protection.
|
||||||
|
IntegrityOnly
|
||||||
|
// PrivacyAndIntegrity indicates a connection provides both privacy and integrity protection.
|
||||||
|
PrivacyAndIntegrity
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns SecurityLevel in a string format.
|
||||||
|
func (s SecurityLevel) String() string {
|
||||||
|
switch s {
|
||||||
|
case NoSecurity:
|
||||||
|
return "NoSecurity"
|
||||||
|
case IntegrityOnly:
|
||||||
|
return "IntegrityOnly"
|
||||||
|
case PrivacyAndIntegrity:
|
||||||
|
return "PrivacyAndIntegrity"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("invalid SecurityLevel: %v", int(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommonAuthInfo contains authenticated information common to AuthInfo implementations.
|
||||||
|
// It should be embedded in a struct implementing AuthInfo to provide additional information
|
||||||
|
// about the credentials.
|
||||||
|
//
|
||||||
|
// This API is experimental.
|
||||||
|
type CommonAuthInfo struct {
|
||||||
|
SecurityLevel SecurityLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCommonAuthInfo returns the pointer to CommonAuthInfo struct.
|
||||||
|
func (c *CommonAuthInfo) GetCommonAuthInfo() *CommonAuthInfo {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
// ProtocolInfo provides information regarding the gRPC wire protocol version,
|
// ProtocolInfo provides information regarding the gRPC wire protocol version,
|
||||||
// security protocol, security protocol version in use, server name, etc.
|
// security protocol, security protocol version in use, server name, etc.
|
||||||
type ProtocolInfo struct {
|
type ProtocolInfo struct {
|
||||||
|
@ -68,6 +107,8 @@ type ProtocolInfo struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthInfo defines the common interface for the auth information the users are interested in.
|
// AuthInfo defines the common interface for the auth information the users are interested in.
|
||||||
|
// A struct that implements AuthInfo should embed CommonAuthInfo by including additional
|
||||||
|
// information about the credentials in it.
|
||||||
type AuthInfo interface {
|
type AuthInfo interface {
|
||||||
AuthType() string
|
AuthType() string
|
||||||
}
|
}
|
||||||
|
@ -82,7 +123,8 @@ type TransportCredentials interface {
|
||||||
// ClientHandshake does the authentication handshake specified by the corresponding
|
// ClientHandshake does the authentication handshake specified by the corresponding
|
||||||
// authentication protocol on rawConn for clients. It returns the authenticated
|
// authentication protocol on rawConn for clients. It returns the authenticated
|
||||||
// connection and the corresponding auth information about the connection.
|
// connection and the corresponding auth information about the connection.
|
||||||
// Implementations must use the provided context to implement timely cancellation.
|
// The auth information should embed CommonAuthInfo to return additional information about
|
||||||
|
// the credentials. Implementations must use the provided context to implement timely cancellation.
|
||||||
// gRPC will try to reconnect if the error returned is a temporary error
|
// gRPC will try to reconnect if the error returned is a temporary error
|
||||||
// (io.EOF, context.DeadlineExceeded or err.Temporary() == true).
|
// (io.EOF, context.DeadlineExceeded or err.Temporary() == true).
|
||||||
// If the returned error is a wrapper error, implementations should make sure that
|
// If the returned error is a wrapper error, implementations should make sure that
|
||||||
|
@ -92,7 +134,8 @@ type TransportCredentials interface {
|
||||||
ClientHandshake(context.Context, string, net.Conn) (net.Conn, AuthInfo, error)
|
ClientHandshake(context.Context, string, net.Conn) (net.Conn, AuthInfo, error)
|
||||||
// ServerHandshake does the authentication handshake for servers. It returns
|
// ServerHandshake does the authentication handshake for servers. It returns
|
||||||
// the authenticated connection and the corresponding auth information about
|
// the authenticated connection and the corresponding auth information about
|
||||||
// the connection.
|
// the connection. The auth information should embed CommonAuthInfo to return additional information
|
||||||
|
// about the credentials.
|
||||||
//
|
//
|
||||||
// If the returned net.Conn is closed, it MUST close the net.Conn provided.
|
// If the returned net.Conn is closed, it MUST close the net.Conn provided.
|
||||||
ServerHandshake(net.Conn) (net.Conn, AuthInfo, error)
|
ServerHandshake(net.Conn) (net.Conn, AuthInfo, error)
|
||||||
|
@ -125,145 +168,63 @@ type Bundle interface {
|
||||||
NewWithMode(mode string) (Bundle, error)
|
NewWithMode(mode string) (Bundle, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLSInfo contains the auth information for a TLS authenticated connection.
|
// RequestInfo contains request data attached to the context passed to GetRequestMetadata calls.
|
||||||
// It implements the AuthInfo interface.
|
//
|
||||||
type TLSInfo struct {
|
// This API is experimental.
|
||||||
State tls.ConnectionState
|
type RequestInfo struct {
|
||||||
|
// The method passed to Invoke or NewStream for this RPC. (For proto methods, this has the format "/some.Service/Method")
|
||||||
|
Method string
|
||||||
|
// AuthInfo contains the information from a security handshake (TransportCredentials.ClientHandshake, TransportCredentials.ServerHandshake)
|
||||||
|
AuthInfo AuthInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthType returns the type of TLSInfo as a string.
|
// requestInfoKey is a struct to be used as the key when attaching a RequestInfo to a context object.
|
||||||
func (t TLSInfo) AuthType() string {
|
type requestInfoKey struct{}
|
||||||
return "tls"
|
|
||||||
|
// RequestInfoFromContext extracts the RequestInfo from the context if it exists.
|
||||||
|
//
|
||||||
|
// This API is experimental.
|
||||||
|
func RequestInfoFromContext(ctx context.Context) (ri RequestInfo, ok bool) {
|
||||||
|
ri, ok = ctx.Value(requestInfoKey{}).(RequestInfo)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSecurityValue returns security info requested by channelz.
|
// CheckSecurityLevel checks if a connection's security level is greater than or equal to the specified one.
|
||||||
func (t TLSInfo) GetSecurityValue() ChannelzSecurityValue {
|
// It returns success if 1) the condition is satisified or 2) AuthInfo struct does not implement GetCommonAuthInfo() method
|
||||||
v := &TLSChannelzSecurityValue{
|
// or 3) CommonAuthInfo.SecurityLevel has an invalid zero value. For 2) and 3), it is for the purpose of backward-compatibility.
|
||||||
StandardName: cipherSuiteLookup[t.State.CipherSuite],
|
//
|
||||||
|
// This API is experimental.
|
||||||
|
func CheckSecurityLevel(ctx context.Context, level SecurityLevel) error {
|
||||||
|
type internalInfo interface {
|
||||||
|
GetCommonAuthInfo() *CommonAuthInfo
|
||||||
}
|
}
|
||||||
// Currently there's no way to get LocalCertificate info from tls package.
|
ri, _ := RequestInfoFromContext(ctx)
|
||||||
if len(t.State.PeerCertificates) > 0 {
|
if ri.AuthInfo == nil {
|
||||||
v.RemoteCertificate = t.State.PeerCertificates[0].Raw
|
return errors.New("unable to obtain SecurityLevel from context")
|
||||||
}
|
}
|
||||||
return v
|
if ci, ok := ri.AuthInfo.(internalInfo); ok {
|
||||||
|
// CommonAuthInfo.SecurityLevel has an invalid value.
|
||||||
|
if ci.GetCommonAuthInfo().SecurityLevel == 0 {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
if ci.GetCommonAuthInfo().SecurityLevel < level {
|
||||||
// tlsCreds is the credentials required for authenticating a connection using TLS.
|
return fmt.Errorf("requires SecurityLevel %v; connection has %v", level, ci.GetCommonAuthInfo().SecurityLevel)
|
||||||
type tlsCreds struct {
|
|
||||||
// TLS configuration
|
|
||||||
config *tls.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c tlsCreds) Info() ProtocolInfo {
|
|
||||||
return ProtocolInfo{
|
|
||||||
SecurityProtocol: "tls",
|
|
||||||
SecurityVersion: "1.2",
|
|
||||||
ServerName: c.config.ServerName,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// The condition is satisfied or AuthInfo struct does not implement GetCommonAuthInfo() method.
|
||||||
func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (_ net.Conn, _ AuthInfo, err error) {
|
|
||||||
// use local cfg to avoid clobbering ServerName if using multiple endpoints
|
|
||||||
cfg := cloneTLSConfig(c.config)
|
|
||||||
if cfg.ServerName == "" {
|
|
||||||
colonPos := strings.LastIndex(authority, ":")
|
|
||||||
if colonPos == -1 {
|
|
||||||
colonPos = len(authority)
|
|
||||||
}
|
|
||||||
cfg.ServerName = authority[:colonPos]
|
|
||||||
}
|
|
||||||
conn := tls.Client(rawConn, cfg)
|
|
||||||
errChannel := make(chan error, 1)
|
|
||||||
go func() {
|
|
||||||
errChannel <- conn.Handshake()
|
|
||||||
}()
|
|
||||||
select {
|
|
||||||
case err := <-errChannel:
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
case <-ctx.Done():
|
|
||||||
return nil, nil, ctx.Err()
|
|
||||||
}
|
|
||||||
return internal.WrapSyscallConn(rawConn, conn), TLSInfo{conn.ConnectionState()}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) {
|
|
||||||
conn := tls.Server(rawConn, c.config)
|
|
||||||
if err := conn.Handshake(); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return internal.WrapSyscallConn(rawConn, conn), TLSInfo{conn.ConnectionState()}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *tlsCreds) Clone() TransportCredentials {
|
|
||||||
return NewTLS(c.config)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *tlsCreds) OverrideServerName(serverNameOverride string) error {
|
|
||||||
c.config.ServerName = serverNameOverride
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const alpnProtoStrH2 = "h2"
|
func init() {
|
||||||
|
internal.NewRequestInfoContext = func(ctx context.Context, ri RequestInfo) context.Context {
|
||||||
func appendH2ToNextProtos(ps []string) []string {
|
return context.WithValue(ctx, requestInfoKey{}, ri)
|
||||||
for _, p := range ps {
|
|
||||||
if p == alpnProtoStrH2 {
|
|
||||||
return ps
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret := make([]string, 0, len(ps)+1)
|
|
||||||
ret = append(ret, ps...)
|
|
||||||
return append(ret, alpnProtoStrH2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTLS uses c to construct a TransportCredentials based on TLS.
|
|
||||||
func NewTLS(c *tls.Config) TransportCredentials {
|
|
||||||
tc := &tlsCreds{cloneTLSConfig(c)}
|
|
||||||
tc.config.NextProtos = appendH2ToNextProtos(tc.config.NextProtos)
|
|
||||||
return tc
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewClientTLSFromCert constructs TLS credentials from the input certificate for client.
|
|
||||||
// serverNameOverride is for testing only. If set to a non empty string,
|
|
||||||
// it will override the virtual host name of authority (e.g. :authority header field) in requests.
|
|
||||||
func NewClientTLSFromCert(cp *x509.CertPool, serverNameOverride string) TransportCredentials {
|
|
||||||
return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp})
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewClientTLSFromFile constructs TLS credentials from the input certificate file for client.
|
|
||||||
// serverNameOverride is for testing only. If set to a non empty string,
|
|
||||||
// it will override the virtual host name of authority (e.g. :authority header field) in requests.
|
|
||||||
func NewClientTLSFromFile(certFile, serverNameOverride string) (TransportCredentials, error) {
|
|
||||||
b, err := ioutil.ReadFile(certFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
cp := x509.NewCertPool()
|
|
||||||
if !cp.AppendCertsFromPEM(b) {
|
|
||||||
return nil, fmt.Errorf("credentials: failed to append certificates")
|
|
||||||
}
|
|
||||||
return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewServerTLSFromCert constructs TLS credentials from the input certificate for server.
|
|
||||||
func NewServerTLSFromCert(cert *tls.Certificate) TransportCredentials {
|
|
||||||
return NewTLS(&tls.Config{Certificates: []tls.Certificate{*cert}})
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewServerTLSFromFile constructs TLS credentials from the input certificate file and key
|
|
||||||
// file for server.
|
|
||||||
func NewServerTLSFromFile(certFile, keyFile string) (TransportCredentials, error) {
|
|
||||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChannelzSecurityInfo defines the interface that security protocols should implement
|
// ChannelzSecurityInfo defines the interface that security protocols should implement
|
||||||
// in order to provide security info to channelz.
|
// in order to provide security info to channelz.
|
||||||
|
//
|
||||||
|
// This API is experimental.
|
||||||
type ChannelzSecurityInfo interface {
|
type ChannelzSecurityInfo interface {
|
||||||
GetSecurityValue() ChannelzSecurityValue
|
GetSecurityValue() ChannelzSecurityValue
|
||||||
}
|
}
|
||||||
|
@ -271,68 +232,20 @@ type ChannelzSecurityInfo interface {
|
||||||
// ChannelzSecurityValue defines the interface that GetSecurityValue() return value
|
// ChannelzSecurityValue defines the interface that GetSecurityValue() return value
|
||||||
// should satisfy. This interface should only be satisfied by *TLSChannelzSecurityValue
|
// should satisfy. This interface should only be satisfied by *TLSChannelzSecurityValue
|
||||||
// and *OtherChannelzSecurityValue.
|
// and *OtherChannelzSecurityValue.
|
||||||
|
//
|
||||||
|
// This API is experimental.
|
||||||
type ChannelzSecurityValue interface {
|
type ChannelzSecurityValue interface {
|
||||||
isChannelzSecurityValue()
|
isChannelzSecurityValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLSChannelzSecurityValue defines the struct that TLS protocol should return
|
|
||||||
// from GetSecurityValue(), containing security info like cipher and certificate used.
|
|
||||||
type TLSChannelzSecurityValue struct {
|
|
||||||
StandardName string
|
|
||||||
LocalCertificate []byte
|
|
||||||
RemoteCertificate []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*TLSChannelzSecurityValue) isChannelzSecurityValue() {}
|
|
||||||
|
|
||||||
// OtherChannelzSecurityValue defines the struct that non-TLS protocol should return
|
// OtherChannelzSecurityValue defines the struct that non-TLS protocol should return
|
||||||
// from GetSecurityValue(), which contains protocol specific security info. Note
|
// from GetSecurityValue(), which contains protocol specific security info. Note
|
||||||
// the Value field will be sent to users of channelz requesting channel info, and
|
// the Value field will be sent to users of channelz requesting channel info, and
|
||||||
// thus sensitive info should better be avoided.
|
// thus sensitive info should better be avoided.
|
||||||
|
//
|
||||||
|
// This API is experimental.
|
||||||
type OtherChannelzSecurityValue struct {
|
type OtherChannelzSecurityValue struct {
|
||||||
|
ChannelzSecurityValue
|
||||||
Name string
|
Name string
|
||||||
Value proto.Message
|
Value proto.Message
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*OtherChannelzSecurityValue) isChannelzSecurityValue() {}
|
|
||||||
|
|
||||||
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",
|
|
||||||
}
|
|
||||||
|
|
||||||
// cloneTLSConfig returns a shallow clone of the exported
|
|
||||||
// fields of cfg, ignoring the unexported sync.Once, which
|
|
||||||
// contains a mutex and must not be copied.
|
|
||||||
//
|
|
||||||
// If cfg is nil, a new zero tls.Config is returned.
|
|
||||||
//
|
|
||||||
// TODO: inline this function if possible.
|
|
||||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
|
||||||
if cfg == nil {
|
|
||||||
return &tls.Config{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cfg.Clone()
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,225 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2014 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 credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/credentials/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TLSInfo contains the auth information for a TLS authenticated connection.
|
||||||
|
// It implements the AuthInfo interface.
|
||||||
|
type TLSInfo struct {
|
||||||
|
State tls.ConnectionState
|
||||||
|
CommonAuthInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthType returns the type of TLSInfo as a string.
|
||||||
|
func (t TLSInfo) AuthType() string {
|
||||||
|
return "tls"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSecurityValue returns security info requested by channelz.
|
||||||
|
func (t TLSInfo) GetSecurityValue() ChannelzSecurityValue {
|
||||||
|
v := &TLSChannelzSecurityValue{
|
||||||
|
StandardName: cipherSuiteLookup[t.State.CipherSuite],
|
||||||
|
}
|
||||||
|
// Currently there's no way to get LocalCertificate info from tls package.
|
||||||
|
if len(t.State.PeerCertificates) > 0 {
|
||||||
|
v.RemoteCertificate = t.State.PeerCertificates[0].Raw
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// tlsCreds is the credentials required for authenticating a connection using TLS.
|
||||||
|
type tlsCreds struct {
|
||||||
|
// TLS configuration
|
||||||
|
config *tls.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c tlsCreds) Info() ProtocolInfo {
|
||||||
|
return ProtocolInfo{
|
||||||
|
SecurityProtocol: "tls",
|
||||||
|
SecurityVersion: "1.2",
|
||||||
|
ServerName: c.config.ServerName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (_ net.Conn, _ AuthInfo, err error) {
|
||||||
|
// use local cfg to avoid clobbering ServerName if using multiple endpoints
|
||||||
|
cfg := cloneTLSConfig(c.config)
|
||||||
|
if cfg.ServerName == "" {
|
||||||
|
serverName, _, err := net.SplitHostPort(authority)
|
||||||
|
if err != nil {
|
||||||
|
// If the authority had no host port or if the authority cannot be parsed, use it as-is.
|
||||||
|
serverName = authority
|
||||||
|
}
|
||||||
|
cfg.ServerName = serverName
|
||||||
|
}
|
||||||
|
conn := tls.Client(rawConn, cfg)
|
||||||
|
errChannel := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
errChannel <- conn.Handshake()
|
||||||
|
close(errChannel)
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case err := <-errChannel:
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
conn.Close()
|
||||||
|
return nil, nil, ctx.Err()
|
||||||
|
}
|
||||||
|
return internal.WrapSyscallConn(rawConn, conn), TLSInfo{conn.ConnectionState(), CommonAuthInfo{PrivacyAndIntegrity}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) {
|
||||||
|
conn := tls.Server(rawConn, c.config)
|
||||||
|
if err := conn.Handshake(); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return internal.WrapSyscallConn(rawConn, conn), TLSInfo{conn.ConnectionState(), CommonAuthInfo{PrivacyAndIntegrity}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tlsCreds) Clone() TransportCredentials {
|
||||||
|
return NewTLS(c.config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tlsCreds) OverrideServerName(serverNameOverride string) error {
|
||||||
|
c.config.ServerName = serverNameOverride
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const alpnProtoStrH2 = "h2"
|
||||||
|
|
||||||
|
func appendH2ToNextProtos(ps []string) []string {
|
||||||
|
for _, p := range ps {
|
||||||
|
if p == alpnProtoStrH2 {
|
||||||
|
return ps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret := make([]string, 0, len(ps)+1)
|
||||||
|
ret = append(ret, ps...)
|
||||||
|
return append(ret, alpnProtoStrH2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTLS uses c to construct a TransportCredentials based on TLS.
|
||||||
|
func NewTLS(c *tls.Config) TransportCredentials {
|
||||||
|
tc := &tlsCreds{cloneTLSConfig(c)}
|
||||||
|
tc.config.NextProtos = appendH2ToNextProtos(tc.config.NextProtos)
|
||||||
|
return tc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientTLSFromCert constructs TLS credentials from the input certificate for client.
|
||||||
|
// serverNameOverride is for testing only. If set to a non empty string,
|
||||||
|
// it will override the virtual host name of authority (e.g. :authority header field) in requests.
|
||||||
|
func NewClientTLSFromCert(cp *x509.CertPool, serverNameOverride string) TransportCredentials {
|
||||||
|
return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientTLSFromFile constructs TLS credentials from the input certificate file for client.
|
||||||
|
// serverNameOverride is for testing only. If set to a non empty string,
|
||||||
|
// it will override the virtual host name of authority (e.g. :authority header field) in requests.
|
||||||
|
func NewClientTLSFromFile(certFile, serverNameOverride string) (TransportCredentials, error) {
|
||||||
|
b, err := ioutil.ReadFile(certFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cp := x509.NewCertPool()
|
||||||
|
if !cp.AppendCertsFromPEM(b) {
|
||||||
|
return nil, fmt.Errorf("credentials: failed to append certificates")
|
||||||
|
}
|
||||||
|
return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServerTLSFromCert constructs TLS credentials from the input certificate for server.
|
||||||
|
func NewServerTLSFromCert(cert *tls.Certificate) TransportCredentials {
|
||||||
|
return NewTLS(&tls.Config{Certificates: []tls.Certificate{*cert}})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServerTLSFromFile constructs TLS credentials from the input certificate file and key
|
||||||
|
// file for server.
|
||||||
|
func NewServerTLSFromFile(certFile, keyFile string) (TransportCredentials, error) {
|
||||||
|
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLSChannelzSecurityValue defines the struct that TLS protocol should return
|
||||||
|
// from GetSecurityValue(), containing security info like cipher and certificate used.
|
||||||
|
//
|
||||||
|
// This API is EXPERIMENTAL.
|
||||||
|
type TLSChannelzSecurityValue struct {
|
||||||
|
ChannelzSecurityValue
|
||||||
|
StandardName string
|
||||||
|
LocalCertificate []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",
|
||||||
|
}
|
||||||
|
|
||||||
|
// cloneTLSConfig returns a shallow clone of the exported
|
||||||
|
// fields of cfg, ignoring the unexported sync.Once, which
|
||||||
|
// contains a mutex and must not be copied.
|
||||||
|
//
|
||||||
|
// If cfg is nil, a new zero tls.Config is returned.
|
||||||
|
//
|
||||||
|
// TODO: inline this function if possible.
|
||||||
|
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||||
|
if cfg == nil {
|
||||||
|
return &tls.Config{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg.Clone()
|
||||||
|
}
|
|
@ -24,11 +24,12 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/backoff"
|
||||||
"google.golang.org/grpc/balancer"
|
"google.golang.org/grpc/balancer"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
"google.golang.org/grpc/internal"
|
"google.golang.org/grpc/internal"
|
||||||
"google.golang.org/grpc/internal/backoff"
|
internalbackoff "google.golang.org/grpc/internal/backoff"
|
||||||
"google.golang.org/grpc/internal/envconfig"
|
"google.golang.org/grpc/internal/envconfig"
|
||||||
"google.golang.org/grpc/internal/transport"
|
"google.golang.org/grpc/internal/transport"
|
||||||
"google.golang.org/grpc/keepalive"
|
"google.golang.org/grpc/keepalive"
|
||||||
|
@ -47,7 +48,7 @@ type dialOptions struct {
|
||||||
|
|
||||||
cp Compressor
|
cp Compressor
|
||||||
dc Decompressor
|
dc Decompressor
|
||||||
bs backoff.Strategy
|
bs internalbackoff.Strategy
|
||||||
block bool
|
block bool
|
||||||
insecure bool
|
insecure bool
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
|
@ -58,9 +59,6 @@ type dialOptions struct {
|
||||||
// This is used by v1 balancer dial option WithBalancer to support v1
|
// This is used by v1 balancer dial option WithBalancer to support v1
|
||||||
// balancer, and also by WithBalancerName dial option.
|
// balancer, and also by WithBalancerName dial option.
|
||||||
balancerBuilder balancer.Builder
|
balancerBuilder balancer.Builder
|
||||||
// This is to support grpclb.
|
|
||||||
resolverBuilder resolver.Builder
|
|
||||||
reqHandshake envconfig.RequireHandshakeSetting
|
|
||||||
channelzParentID int64
|
channelzParentID int64
|
||||||
disableServiceConfig bool
|
disableServiceConfig bool
|
||||||
disableRetry bool
|
disableRetry bool
|
||||||
|
@ -69,6 +67,11 @@ type dialOptions struct {
|
||||||
minConnectTimeout func() time.Duration
|
minConnectTimeout func() time.Duration
|
||||||
defaultServiceConfig *ServiceConfig // defaultServiceConfig is parsed from defaultServiceConfigRawJSON.
|
defaultServiceConfig *ServiceConfig // defaultServiceConfig is parsed from defaultServiceConfigRawJSON.
|
||||||
defaultServiceConfigRawJSON *string
|
defaultServiceConfigRawJSON *string
|
||||||
|
// This is used by ccResolverWrapper to backoff between successive calls to
|
||||||
|
// resolver.ResolveNow(). The user will have no need to configure this, but
|
||||||
|
// we need to be able to configure this in tests.
|
||||||
|
resolveNowBackoff func(int) time.Duration
|
||||||
|
resolvers []resolver.Builder
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialOption configures how we set up the connection.
|
// DialOption configures how we set up the connection.
|
||||||
|
@ -100,17 +103,6 @@ func newFuncDialOption(f func(*dialOptions)) *funcDialOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithWaitForHandshake blocks until the initial settings frame is received from
|
|
||||||
// the server before assigning RPCs to the connection.
|
|
||||||
//
|
|
||||||
// Deprecated: this is the default behavior, and this option will be removed
|
|
||||||
// after the 1.18 release.
|
|
||||||
func WithWaitForHandshake() DialOption {
|
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
|
||||||
o.reqHandshake = envconfig.RequireHandshakeOn
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithWriteBufferSize determines how much data can be batched before doing a
|
// WithWriteBufferSize determines how much data can be batched before doing a
|
||||||
// write on the wire. The corresponding memory allocation for this buffer will
|
// write on the wire. The corresponding memory allocation for this buffer will
|
||||||
// be twice the size to keep syscalls low. The default value for this buffer is
|
// be twice the size to keep syscalls low. The default value for this buffer is
|
||||||
|
@ -156,7 +148,8 @@ func WithInitialConnWindowSize(s int32) DialOption {
|
||||||
// WithMaxMsgSize returns a DialOption which sets the maximum message size the
|
// WithMaxMsgSize returns a DialOption which sets the maximum message size the
|
||||||
// client can receive.
|
// client can receive.
|
||||||
//
|
//
|
||||||
// Deprecated: use WithDefaultCallOptions(MaxCallRecvMsgSize(s)) instead.
|
// Deprecated: use WithDefaultCallOptions(MaxCallRecvMsgSize(s)) instead. Will
|
||||||
|
// be supported throughout 1.x.
|
||||||
func WithMaxMsgSize(s int) DialOption {
|
func WithMaxMsgSize(s int) DialOption {
|
||||||
return WithDefaultCallOptions(MaxCallRecvMsgSize(s))
|
return WithDefaultCallOptions(MaxCallRecvMsgSize(s))
|
||||||
}
|
}
|
||||||
|
@ -172,7 +165,8 @@ func WithDefaultCallOptions(cos ...CallOption) DialOption {
|
||||||
// WithCodec returns a DialOption which sets a codec for message marshaling and
|
// WithCodec returns a DialOption which sets a codec for message marshaling and
|
||||||
// unmarshaling.
|
// unmarshaling.
|
||||||
//
|
//
|
||||||
// Deprecated: use WithDefaultCallOptions(ForceCodec(_)) instead.
|
// Deprecated: use WithDefaultCallOptions(ForceCodec(_)) instead. Will be
|
||||||
|
// supported throughout 1.x.
|
||||||
func WithCodec(c Codec) DialOption {
|
func WithCodec(c Codec) DialOption {
|
||||||
return WithDefaultCallOptions(CallCustomCodec(c))
|
return WithDefaultCallOptions(CallCustomCodec(c))
|
||||||
}
|
}
|
||||||
|
@ -181,7 +175,7 @@ func WithCodec(c Codec) DialOption {
|
||||||
// message compression. It has lower priority than the compressor set by the
|
// message compression. It has lower priority than the compressor set by the
|
||||||
// UseCompressor CallOption.
|
// UseCompressor CallOption.
|
||||||
//
|
//
|
||||||
// Deprecated: use UseCompressor instead.
|
// Deprecated: use UseCompressor instead. Will be supported throughout 1.x.
|
||||||
func WithCompressor(cp Compressor) DialOption {
|
func WithCompressor(cp Compressor) DialOption {
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.cp = cp
|
o.cp = cp
|
||||||
|
@ -196,7 +190,8 @@ func WithCompressor(cp Compressor) DialOption {
|
||||||
// message. If no compressor is registered for the encoding, an Unimplemented
|
// message. If no compressor is registered for the encoding, an Unimplemented
|
||||||
// status error will be returned.
|
// status error will be returned.
|
||||||
//
|
//
|
||||||
// Deprecated: use encoding.RegisterCompressor instead.
|
// Deprecated: use encoding.RegisterCompressor instead. Will be supported
|
||||||
|
// throughout 1.x.
|
||||||
func WithDecompressor(dc Decompressor) DialOption {
|
func WithDecompressor(dc Decompressor) DialOption {
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.dc = dc
|
o.dc = dc
|
||||||
|
@ -207,7 +202,7 @@ func WithDecompressor(dc Decompressor) DialOption {
|
||||||
// Name resolver will be ignored if this DialOption is specified.
|
// Name resolver will be ignored if this DialOption is specified.
|
||||||
//
|
//
|
||||||
// Deprecated: use the new balancer APIs in balancer package and
|
// Deprecated: use the new balancer APIs in balancer package and
|
||||||
// WithBalancerName.
|
// WithBalancerName. Will be removed in a future 1.x release.
|
||||||
func WithBalancer(b Balancer) DialOption {
|
func WithBalancer(b Balancer) DialOption {
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.balancerBuilder = &balancerWrapperBuilder{
|
o.balancerBuilder = &balancerWrapperBuilder{
|
||||||
|
@ -223,7 +218,8 @@ func WithBalancer(b Balancer) DialOption {
|
||||||
// The balancer cannot be overridden by balancer option specified by service
|
// The balancer cannot be overridden by balancer option specified by service
|
||||||
// config.
|
// config.
|
||||||
//
|
//
|
||||||
// This is an EXPERIMENTAL API.
|
// Deprecated: use WithDefaultServiceConfig and WithDisableServiceConfig
|
||||||
|
// instead. Will be removed in a future 1.x release.
|
||||||
func WithBalancerName(balancerName string) DialOption {
|
func WithBalancerName(balancerName string) DialOption {
|
||||||
builder := balancer.Get(balancerName)
|
builder := balancer.Get(balancerName)
|
||||||
if builder == nil {
|
if builder == nil {
|
||||||
|
@ -234,27 +230,41 @@ func WithBalancerName(balancerName string) DialOption {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// withResolverBuilder is only for grpclb.
|
|
||||||
func withResolverBuilder(b resolver.Builder) DialOption {
|
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
|
||||||
o.resolverBuilder = b
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithServiceConfig returns a DialOption which has a channel to read the
|
// WithServiceConfig returns a DialOption which has a channel to read the
|
||||||
// service configuration.
|
// service configuration.
|
||||||
//
|
//
|
||||||
// Deprecated: service config should be received through name resolver, as
|
// Deprecated: service config should be received through name resolver or via
|
||||||
// specified here.
|
// WithDefaultServiceConfig, as specified at
|
||||||
// https://github.com/grpc/grpc/blob/master/doc/service_config.md
|
// 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 {
|
func WithServiceConfig(c <-chan ServiceConfig) DialOption {
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.scChan = c
|
o.scChan = c
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithConnectParams configures the dialer to use the provided ConnectParams.
|
||||||
|
//
|
||||||
|
// The backoff configuration specified as part of the ConnectParams overrides
|
||||||
|
// all defaults specified in
|
||||||
|
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md. Consider
|
||||||
|
// using the backoff.DefaultConfig as a base, in cases where you want to
|
||||||
|
// override only a subset of the backoff configuration.
|
||||||
|
//
|
||||||
|
// This API is EXPERIMENTAL.
|
||||||
|
func WithConnectParams(p ConnectParams) DialOption {
|
||||||
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
|
o.bs = internalbackoff.Exponential{Config: p.Backoff}
|
||||||
|
o.minConnectTimeout = func() time.Duration {
|
||||||
|
return p.MinConnectTimeout
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// WithBackoffMaxDelay configures the dialer to use the provided maximum delay
|
// WithBackoffMaxDelay configures the dialer to use the provided maximum delay
|
||||||
// when backing off after failed connection attempts.
|
// when backing off after failed connection attempts.
|
||||||
|
//
|
||||||
|
// Deprecated: use WithConnectParams instead. Will be supported throughout 1.x.
|
||||||
func WithBackoffMaxDelay(md time.Duration) DialOption {
|
func WithBackoffMaxDelay(md time.Duration) DialOption {
|
||||||
return WithBackoffConfig(BackoffConfig{MaxDelay: md})
|
return WithBackoffConfig(BackoffConfig{MaxDelay: md})
|
||||||
}
|
}
|
||||||
|
@ -262,19 +272,18 @@ func WithBackoffMaxDelay(md time.Duration) DialOption {
|
||||||
// WithBackoffConfig configures the dialer to use the provided backoff
|
// WithBackoffConfig configures the dialer to use the provided backoff
|
||||||
// parameters after connection failures.
|
// parameters after connection failures.
|
||||||
//
|
//
|
||||||
// Use WithBackoffMaxDelay until more parameters on BackoffConfig are opened up
|
// Deprecated: use WithConnectParams instead. Will be supported throughout 1.x.
|
||||||
// for use.
|
|
||||||
func WithBackoffConfig(b BackoffConfig) DialOption {
|
func WithBackoffConfig(b BackoffConfig) DialOption {
|
||||||
return withBackoff(backoff.Exponential{
|
bc := backoff.DefaultConfig
|
||||||
MaxDelay: b.MaxDelay,
|
bc.MaxDelay = b.MaxDelay
|
||||||
})
|
return withBackoff(internalbackoff.Exponential{Config: bc})
|
||||||
}
|
}
|
||||||
|
|
||||||
// withBackoff sets the backoff strategy used for connectRetryNum after a failed
|
// withBackoff sets the backoff strategy used for connectRetryNum after a failed
|
||||||
// connection attempt.
|
// connection attempt.
|
||||||
//
|
//
|
||||||
// This can be exported if arbitrary backoff strategies are allowed by gRPC.
|
// This can be exported if arbitrary backoff strategies are allowed by gRPC.
|
||||||
func withBackoff(bs backoff.Strategy) DialOption {
|
func withBackoff(bs internalbackoff.Strategy) DialOption {
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.bs = bs
|
o.bs = bs
|
||||||
})
|
})
|
||||||
|
@ -329,7 +338,8 @@ func WithCredentialsBundle(b credentials.Bundle) DialOption {
|
||||||
// WithTimeout returns a DialOption that configures a timeout for dialing a
|
// WithTimeout returns a DialOption that configures a timeout for dialing a
|
||||||
// ClientConn initially. This is valid if and only if WithBlock() is present.
|
// ClientConn initially. This is valid if and only if WithBlock() is present.
|
||||||
//
|
//
|
||||||
// Deprecated: use DialContext and context.WithTimeout instead.
|
// Deprecated: use DialContext instead of Dial and context.WithTimeout
|
||||||
|
// instead. Will be supported throughout 1.x.
|
||||||
func WithTimeout(d time.Duration) DialOption {
|
func WithTimeout(d time.Duration) DialOption {
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.timeout = d
|
o.timeout = d
|
||||||
|
@ -347,7 +357,6 @@ func WithContextDialer(f func(context.Context, string) (net.Conn, error)) DialOp
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
internal.WithResolverBuilder = withResolverBuilder
|
|
||||||
internal.WithHealthCheckFunc = withHealthCheckFunc
|
internal.WithHealthCheckFunc = withHealthCheckFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,7 +365,8 @@ func init() {
|
||||||
// is returned by f, gRPC checks the error's Temporary() method to decide if it
|
// is 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.
|
||||||
//
|
//
|
||||||
// Deprecated: use WithContextDialer instead
|
// Deprecated: use WithContextDialer instead. Will be supported throughout
|
||||||
|
// 1.x.
|
||||||
func WithDialer(f func(string, time.Duration) (net.Conn, error)) DialOption {
|
func WithDialer(f func(string, time.Duration) (net.Conn, error)) DialOption {
|
||||||
return WithContextDialer(
|
return WithContextDialer(
|
||||||
func(ctx context.Context, addr string) (net.Conn, error) {
|
func(ctx context.Context, addr string) (net.Conn, error) {
|
||||||
|
@ -460,6 +470,8 @@ func WithAuthority(a string) DialOption {
|
||||||
// WithChannelzParentID returns a DialOption that specifies the channelz ID of
|
// WithChannelzParentID returns a DialOption that specifies the channelz ID of
|
||||||
// current ClientConn's parent. This function is used in nested channel creation
|
// current ClientConn's parent. This function is used in nested channel creation
|
||||||
// (e.g. grpclb dial).
|
// (e.g. grpclb dial).
|
||||||
|
//
|
||||||
|
// This API is EXPERIMENTAL.
|
||||||
func WithChannelzParentID(id int64) DialOption {
|
func WithChannelzParentID(id int64) DialOption {
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.channelzParentID = id
|
o.channelzParentID = id
|
||||||
|
@ -480,8 +492,10 @@ func WithDisableServiceConfig() DialOption {
|
||||||
|
|
||||||
// WithDefaultServiceConfig returns a DialOption that configures the default
|
// WithDefaultServiceConfig returns a DialOption that configures the default
|
||||||
// service config, which will be used in cases where:
|
// service config, which will be used in cases where:
|
||||||
// 1. WithDisableServiceConfig is called.
|
//
|
||||||
// 2. Resolver does not return service config or if the resolver gets and invalid config.
|
// 1. WithDisableServiceConfig is also used.
|
||||||
|
// 2. Resolver does not return a service config or if the resolver returns an
|
||||||
|
// invalid service config.
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// This API is EXPERIMENTAL.
|
||||||
func WithDefaultServiceConfig(s string) DialOption {
|
func WithDefaultServiceConfig(s string) DialOption {
|
||||||
|
@ -537,12 +551,12 @@ func withHealthCheckFunc(f internal.HealthChecker) DialOption {
|
||||||
func defaultDialOptions() dialOptions {
|
func defaultDialOptions() dialOptions {
|
||||||
return dialOptions{
|
return dialOptions{
|
||||||
disableRetry: !envconfig.Retry,
|
disableRetry: !envconfig.Retry,
|
||||||
reqHandshake: envconfig.RequireHandshake,
|
|
||||||
healthCheckFunc: internal.HealthCheckFunc,
|
healthCheckFunc: internal.HealthCheckFunc,
|
||||||
copts: transport.ConnectOptions{
|
copts: transport.ConnectOptions{
|
||||||
WriteBufferSize: defaultWriteBufSize,
|
WriteBufferSize: defaultWriteBufSize,
|
||||||
ReadBufferSize: defaultReadBufSize,
|
ReadBufferSize: defaultReadBufSize,
|
||||||
},
|
},
|
||||||
|
resolveNowBackoff: internalbackoff.DefaultExponential.Backoff,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,3 +570,25 @@ func withMinConnectDeadline(f func() time.Duration) DialOption {
|
||||||
o.minConnectTimeout = f
|
o.minConnectTimeout = f
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// withResolveNowBackoff specifies the function that clientconn uses to backoff
|
||||||
|
// between successive calls to resolver.ResolveNow().
|
||||||
|
//
|
||||||
|
// For testing purpose only.
|
||||||
|
func withResolveNowBackoff(f func(int) time.Duration) DialOption {
|
||||||
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
|
o.resolveNowBackoff = f
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithResolvers allows a list of resolver implementations to be registered
|
||||||
|
// locally with the ClientConn without needing to be globally registered via
|
||||||
|
// resolver.Register. They will be matched against the scheme used for the
|
||||||
|
// current Dial only, and will take precedence over the global registry.
|
||||||
|
//
|
||||||
|
// This API is EXPERIMENTAL.
|
||||||
|
func WithResolvers(rs ...resolver.Builder) DialOption {
|
||||||
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
|
o.resolvers = append(o.resolvers, rs...)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -46,6 +46,10 @@ type Compressor interface {
|
||||||
// coding header. The result must be static; the result cannot change
|
// coding header. The result must be static; the result cannot change
|
||||||
// between calls.
|
// between calls.
|
||||||
Name() string
|
Name() string
|
||||||
|
// EXPERIMENTAL: if a Compressor implements
|
||||||
|
// DecompressedSize(compressedBytes []byte) int, gRPC will call it
|
||||||
|
// to determine the size of the buffer allocated for the result of decompression.
|
||||||
|
// Return -1 to indicate unknown size.
|
||||||
}
|
}
|
||||||
|
|
||||||
var registeredCompressor = make(map[string]Compressor)
|
var registeredCompressor = make(map[string]Compressor)
|
||||||
|
|
|
@ -1,20 +1,16 @@
|
||||||
module google.golang.org/grpc
|
module google.golang.org/grpc
|
||||||
|
|
||||||
|
go 1.11
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.26.0 // indirect
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473
|
||||||
github.com/BurntSushi/toml v0.3.1 // indirect
|
github.com/envoyproxy/protoc-gen-validate v0.1.0
|
||||||
github.com/client9/misspell v0.3.4
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
|
||||||
github.com/golang/mock v1.1.1
|
github.com/golang/mock v1.1.1
|
||||||
github.com/golang/protobuf v1.2.0
|
github.com/golang/protobuf v1.3.2
|
||||||
github.com/google/go-cmp v0.2.0
|
github.com/google/go-cmp v0.2.0
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f // indirect
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55
|
||||||
google.golang.org/appengine v1.1.0 // indirect
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8
|
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,35 +1,53 @@
|
||||||
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
|
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473 h1:4cmBvAEBNJaGARUEs3/suWRyfyBfhf7I60WBZq+bv2w=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
|
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
|
||||||
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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd h1:/e+gpKk9r3dJobndpTytxS2gOy6m5uvpg+ISQoEcusQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
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/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099 h1:XJP7lxbSxWLOMNdBE4B/STaqVy6L73o0knwj2vIlxnw=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
|
|
@ -89,7 +89,7 @@ func Fatal(args ...interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fatalf logs to the FATAL log. Arguments are handled in the manner of fmt.Printf.
|
// Fatalf logs to the FATAL log. Arguments are handled in the manner of fmt.Printf.
|
||||||
// It calles os.Exit() with exit code 1.
|
// It calls os.Exit() with exit code 1.
|
||||||
func Fatalf(format string, args ...interface{}) {
|
func Fatalf(format string, args ...interface{}) {
|
||||||
logger.Fatalf(format, args...)
|
logger.Fatalf(format, args...)
|
||||||
// Make sure fatal logs will exit.
|
// Make sure fatal logs will exit.
|
||||||
|
|
|
@ -26,16 +26,16 @@ import (
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/connectivity"
|
||||||
healthpb "google.golang.org/grpc/health/grpc_health_v1"
|
healthpb "google.golang.org/grpc/health/grpc_health_v1"
|
||||||
"google.golang.org/grpc/internal"
|
"google.golang.org/grpc/internal"
|
||||||
"google.golang.org/grpc/internal/backoff"
|
"google.golang.org/grpc/internal/backoff"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
const maxDelay = 120 * time.Second
|
var (
|
||||||
|
backoffStrategy = backoff.DefaultExponential
|
||||||
var backoffStrategy = backoff.Exponential{MaxDelay: maxDelay}
|
backoffFunc = func(ctx context.Context, retries int) bool {
|
||||||
var backoffFunc = func(ctx context.Context, retries int) bool {
|
|
||||||
d := backoffStrategy.Backoff(retries)
|
d := backoffStrategy.Backoff(retries)
|
||||||
timer := time.NewTimer(d)
|
timer := time.NewTimer(d)
|
||||||
select {
|
select {
|
||||||
|
@ -46,12 +46,17 @@ var backoffFunc = func(ctx context.Context, retries int) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
internal.HealthCheckFunc = clientHealthCheck
|
internal.HealthCheckFunc = clientHealthCheck
|
||||||
}
|
}
|
||||||
|
|
||||||
func clientHealthCheck(ctx context.Context, newStream func() (interface{}, error), reportHealth func(bool), service string) error {
|
const healthCheckMethod = "/grpc.health.v1.Health/Watch"
|
||||||
|
|
||||||
|
// This function implements the protocol defined at:
|
||||||
|
// https://github.com/grpc/grpc/blob/master/doc/health-checking.md
|
||||||
|
func clientHealthCheck(ctx context.Context, newStream func(string) (interface{}, error), setConnectivityState func(connectivity.State, error), service string) error {
|
||||||
tryCnt := 0
|
tryCnt := 0
|
||||||
|
|
||||||
retryConnection:
|
retryConnection:
|
||||||
|
@ -65,7 +70,8 @@ retryConnection:
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
rawS, err := newStream()
|
setConnectivityState(connectivity.Connecting, nil)
|
||||||
|
rawS, err := newStream(healthCheckMethod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue retryConnection
|
continue retryConnection
|
||||||
}
|
}
|
||||||
|
@ -73,7 +79,7 @@ retryConnection:
|
||||||
s, ok := rawS.(grpc.ClientStream)
|
s, ok := rawS.(grpc.ClientStream)
|
||||||
// Ideally, this should never happen. But if it happens, the server is marked as healthy for LBing purposes.
|
// Ideally, this should never happen. But if it happens, the server is marked as healthy for LBing purposes.
|
||||||
if !ok {
|
if !ok {
|
||||||
reportHealth(true)
|
setConnectivityState(connectivity.Ready, nil)
|
||||||
return fmt.Errorf("newStream returned %v (type %T); want grpc.ClientStream", rawS, rawS)
|
return fmt.Errorf("newStream returned %v (type %T); want grpc.ClientStream", rawS, rawS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,19 +95,23 @@ retryConnection:
|
||||||
|
|
||||||
// Reports healthy for the LBing purposes if health check is not implemented in the server.
|
// Reports healthy for the LBing purposes if health check is not implemented in the server.
|
||||||
if status.Code(err) == codes.Unimplemented {
|
if status.Code(err) == codes.Unimplemented {
|
||||||
reportHealth(true)
|
setConnectivityState(connectivity.Ready, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reports unhealthy if server's Watch method gives an error other than UNIMPLEMENTED.
|
// Reports unhealthy if server's Watch method gives an error other than UNIMPLEMENTED.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reportHealth(false)
|
setConnectivityState(connectivity.TransientFailure, fmt.Errorf("connection active but received health check RPC error: %v", err))
|
||||||
continue retryConnection
|
continue retryConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
// As a message has been received, removes the need for backoff for the next retry by reseting the try count.
|
// As a message has been received, removes the need for backoff for the next retry by resetting the try count.
|
||||||
tryCnt = 0
|
tryCnt = 0
|
||||||
reportHealth(resp.Status == healthpb.HealthCheckResponse_SERVING)
|
if resp.Status == healthpb.HealthCheckResponse_SERVING {
|
||||||
|
setConnectivityState(connectivity.Ready, nil)
|
||||||
|
} else {
|
||||||
|
setConnectivityState(connectivity.TransientFailure, fmt.Errorf("connection active but health check failed. status=%s", resp.Status))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// source: grpc/health/v1/health.proto
|
// source: grpc/health/v1/health.proto
|
||||||
|
|
||||||
package grpc_health_v1 // import "google.golang.org/grpc/health/grpc_health_v1"
|
package grpc_health_v1
|
||||||
|
|
||||||
import proto "github.com/golang/protobuf/proto"
|
|
||||||
import fmt "fmt"
|
|
||||||
import math "math"
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
context "golang.org/x/net/context"
|
context "context"
|
||||||
|
fmt "fmt"
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
grpc "google.golang.org/grpc"
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
math "math"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
@ -21,7 +22,7 @@ var _ = math.Inf
|
||||||
// is compatible with the proto package it is being compiled against.
|
// is compatible with the proto package it is being compiled against.
|
||||||
// A compilation error at this line likely means your copy of the
|
// A compilation error at this line likely means your copy of the
|
||||||
// proto package needs to be updated.
|
// proto package needs to be updated.
|
||||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
|
|
||||||
type HealthCheckResponse_ServingStatus int32
|
type HealthCheckResponse_ServingStatus int32
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ var HealthCheckResponse_ServingStatus_name = map[int32]string{
|
||||||
2: "NOT_SERVING",
|
2: "NOT_SERVING",
|
||||||
3: "SERVICE_UNKNOWN",
|
3: "SERVICE_UNKNOWN",
|
||||||
}
|
}
|
||||||
|
|
||||||
var HealthCheckResponse_ServingStatus_value = map[string]int32{
|
var HealthCheckResponse_ServingStatus_value = map[string]int32{
|
||||||
"UNKNOWN": 0,
|
"UNKNOWN": 0,
|
||||||
"SERVING": 1,
|
"SERVING": 1,
|
||||||
|
@ -48,8 +50,9 @@ var HealthCheckResponse_ServingStatus_value = map[string]int32{
|
||||||
func (x HealthCheckResponse_ServingStatus) String() string {
|
func (x HealthCheckResponse_ServingStatus) String() string {
|
||||||
return proto.EnumName(HealthCheckResponse_ServingStatus_name, int32(x))
|
return proto.EnumName(HealthCheckResponse_ServingStatus_name, int32(x))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (HealthCheckResponse_ServingStatus) EnumDescriptor() ([]byte, []int) {
|
func (HealthCheckResponse_ServingStatus) EnumDescriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_health_6b1a06aa67f91efd, []int{1, 0}
|
return fileDescriptor_e265fd9d4e077217, []int{1, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
type HealthCheckRequest struct {
|
type HealthCheckRequest struct {
|
||||||
|
@ -63,16 +66,17 @@ func (m *HealthCheckRequest) Reset() { *m = HealthCheckRequest{} }
|
||||||
func (m *HealthCheckRequest) String() string { return proto.CompactTextString(m) }
|
func (m *HealthCheckRequest) String() string { return proto.CompactTextString(m) }
|
||||||
func (*HealthCheckRequest) ProtoMessage() {}
|
func (*HealthCheckRequest) ProtoMessage() {}
|
||||||
func (*HealthCheckRequest) Descriptor() ([]byte, []int) {
|
func (*HealthCheckRequest) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_health_6b1a06aa67f91efd, []int{0}
|
return fileDescriptor_e265fd9d4e077217, []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *HealthCheckRequest) XXX_Unmarshal(b []byte) error {
|
func (m *HealthCheckRequest) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_HealthCheckRequest.Unmarshal(m, b)
|
return xxx_messageInfo_HealthCheckRequest.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
func (m *HealthCheckRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
func (m *HealthCheckRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
return xxx_messageInfo_HealthCheckRequest.Marshal(b, m, deterministic)
|
return xxx_messageInfo_HealthCheckRequest.Marshal(b, m, deterministic)
|
||||||
}
|
}
|
||||||
func (dst *HealthCheckRequest) XXX_Merge(src proto.Message) {
|
func (m *HealthCheckRequest) XXX_Merge(src proto.Message) {
|
||||||
xxx_messageInfo_HealthCheckRequest.Merge(dst, src)
|
xxx_messageInfo_HealthCheckRequest.Merge(m, src)
|
||||||
}
|
}
|
||||||
func (m *HealthCheckRequest) XXX_Size() int {
|
func (m *HealthCheckRequest) XXX_Size() int {
|
||||||
return xxx_messageInfo_HealthCheckRequest.Size(m)
|
return xxx_messageInfo_HealthCheckRequest.Size(m)
|
||||||
|
@ -101,16 +105,17 @@ func (m *HealthCheckResponse) Reset() { *m = HealthCheckResponse{} }
|
||||||
func (m *HealthCheckResponse) String() string { return proto.CompactTextString(m) }
|
func (m *HealthCheckResponse) String() string { return proto.CompactTextString(m) }
|
||||||
func (*HealthCheckResponse) ProtoMessage() {}
|
func (*HealthCheckResponse) ProtoMessage() {}
|
||||||
func (*HealthCheckResponse) Descriptor() ([]byte, []int) {
|
func (*HealthCheckResponse) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_health_6b1a06aa67f91efd, []int{1}
|
return fileDescriptor_e265fd9d4e077217, []int{1}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *HealthCheckResponse) XXX_Unmarshal(b []byte) error {
|
func (m *HealthCheckResponse) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_HealthCheckResponse.Unmarshal(m, b)
|
return xxx_messageInfo_HealthCheckResponse.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
func (m *HealthCheckResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
func (m *HealthCheckResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
return xxx_messageInfo_HealthCheckResponse.Marshal(b, m, deterministic)
|
return xxx_messageInfo_HealthCheckResponse.Marshal(b, m, deterministic)
|
||||||
}
|
}
|
||||||
func (dst *HealthCheckResponse) XXX_Merge(src proto.Message) {
|
func (m *HealthCheckResponse) XXX_Merge(src proto.Message) {
|
||||||
xxx_messageInfo_HealthCheckResponse.Merge(dst, src)
|
xxx_messageInfo_HealthCheckResponse.Merge(m, src)
|
||||||
}
|
}
|
||||||
func (m *HealthCheckResponse) XXX_Size() int {
|
func (m *HealthCheckResponse) XXX_Size() int {
|
||||||
return xxx_messageInfo_HealthCheckResponse.Size(m)
|
return xxx_messageInfo_HealthCheckResponse.Size(m)
|
||||||
|
@ -129,9 +134,34 @@ func (m *HealthCheckResponse) GetStatus() HealthCheckResponse_ServingStatus {
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
proto.RegisterEnum("grpc.health.v1.HealthCheckResponse_ServingStatus", HealthCheckResponse_ServingStatus_name, HealthCheckResponse_ServingStatus_value)
|
||||||
proto.RegisterType((*HealthCheckRequest)(nil), "grpc.health.v1.HealthCheckRequest")
|
proto.RegisterType((*HealthCheckRequest)(nil), "grpc.health.v1.HealthCheckRequest")
|
||||||
proto.RegisterType((*HealthCheckResponse)(nil), "grpc.health.v1.HealthCheckResponse")
|
proto.RegisterType((*HealthCheckResponse)(nil), "grpc.health.v1.HealthCheckResponse")
|
||||||
proto.RegisterEnum("grpc.health.v1.HealthCheckResponse_ServingStatus", HealthCheckResponse_ServingStatus_name, HealthCheckResponse_ServingStatus_value)
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("grpc/health/v1/health.proto", fileDescriptor_e265fd9d4e077217) }
|
||||||
|
|
||||||
|
var fileDescriptor_e265fd9d4e077217 = []byte{
|
||||||
|
// 297 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4e, 0x2f, 0x2a, 0x48,
|
||||||
|
0xd6, 0xcf, 0x48, 0x4d, 0xcc, 0x29, 0xc9, 0xd0, 0x2f, 0x33, 0x84, 0xb2, 0xf4, 0x0a, 0x8a, 0xf2,
|
||||||
|
0x4b, 0xf2, 0x85, 0xf8, 0x40, 0x92, 0x7a, 0x50, 0xa1, 0x32, 0x43, 0x25, 0x3d, 0x2e, 0x21, 0x0f,
|
||||||
|
0x30, 0xc7, 0x39, 0x23, 0x35, 0x39, 0x3b, 0x28, 0xb5, 0xb0, 0x34, 0xb5, 0xb8, 0x44, 0x48, 0x82,
|
||||||
|
0x8b, 0xbd, 0x38, 0xb5, 0xa8, 0x2c, 0x33, 0x39, 0x55, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, 0x08,
|
||||||
|
0xc6, 0x55, 0xda, 0xc8, 0xc8, 0x25, 0x8c, 0xa2, 0xa1, 0xb8, 0x20, 0x3f, 0xaf, 0x38, 0x55, 0xc8,
|
||||||
|
0x93, 0x8b, 0xad, 0xb8, 0x24, 0xb1, 0xa4, 0xb4, 0x18, 0xac, 0x81, 0xcf, 0xc8, 0x50, 0x0f, 0xd5,
|
||||||
|
0x22, 0x3d, 0x2c, 0x9a, 0xf4, 0x82, 0x41, 0x86, 0xe6, 0xa5, 0x07, 0x83, 0x35, 0x06, 0x41, 0x0d,
|
||||||
|
0x50, 0xf2, 0xe7, 0xe2, 0x45, 0x91, 0x10, 0xe2, 0xe6, 0x62, 0x0f, 0xf5, 0xf3, 0xf6, 0xf3, 0x0f,
|
||||||
|
0xf7, 0x13, 0x60, 0x00, 0x71, 0x82, 0x5d, 0x83, 0xc2, 0x3c, 0xfd, 0xdc, 0x05, 0x18, 0x85, 0xf8,
|
||||||
|
0xb9, 0xb8, 0xfd, 0xfc, 0x43, 0xe2, 0x61, 0x02, 0x4c, 0x42, 0xc2, 0x5c, 0xfc, 0x60, 0x8e, 0xb3,
|
||||||
|
0x6b, 0x3c, 0x4c, 0x0b, 0xb3, 0xd1, 0x3a, 0x46, 0x2e, 0x36, 0x88, 0xf5, 0x42, 0x01, 0x5c, 0xac,
|
||||||
|
0x60, 0x27, 0x08, 0x29, 0xe1, 0x75, 0x1f, 0x38, 0x14, 0xa4, 0x94, 0x89, 0xf0, 0x83, 0x50, 0x10,
|
||||||
|
0x17, 0x6b, 0x78, 0x62, 0x49, 0x72, 0x06, 0xd5, 0x4c, 0x34, 0x60, 0x74, 0x4a, 0xe4, 0x12, 0xcc,
|
||||||
|
0xcc, 0x47, 0x53, 0xea, 0xc4, 0x0d, 0x51, 0x1b, 0x00, 0x8a, 0xc6, 0x00, 0xc6, 0x28, 0x9d, 0xf4,
|
||||||
|
0xfc, 0xfc, 0xf4, 0x9c, 0x54, 0xbd, 0xf4, 0xfc, 0x9c, 0xc4, 0xbc, 0x74, 0xbd, 0xfc, 0xa2, 0x74,
|
||||||
|
0x7d, 0xe4, 0x78, 0x07, 0xb1, 0xe3, 0x21, 0xec, 0xf8, 0x32, 0xc3, 0x55, 0x4c, 0x7c, 0xee, 0x20,
|
||||||
|
0xd3, 0x20, 0x46, 0xe8, 0x85, 0x19, 0x26, 0xb1, 0x81, 0x93, 0x83, 0x31, 0x20, 0x00, 0x00, 0xff,
|
||||||
|
0xff, 0x12, 0x7d, 0x96, 0xcb, 0x2d, 0x02, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
@ -239,6 +269,17 @@ type HealthServer interface {
|
||||||
Watch(*HealthCheckRequest, Health_WatchServer) error
|
Watch(*HealthCheckRequest, Health_WatchServer) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnimplementedHealthServer can be embedded to have forward compatible implementations.
|
||||||
|
type UnimplementedHealthServer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*UnimplementedHealthServer) Check(ctx context.Context, req *HealthCheckRequest) (*HealthCheckResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method Check not implemented")
|
||||||
|
}
|
||||||
|
func (*UnimplementedHealthServer) Watch(req *HealthCheckRequest, srv Health_WatchServer) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method Watch not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
func RegisterHealthServer(s *grpc.Server, srv HealthServer) {
|
func RegisterHealthServer(s *grpc.Server, srv HealthServer) {
|
||||||
s.RegisterService(&_Health_serviceDesc, srv)
|
s.RegisterService(&_Health_serviceDesc, srv)
|
||||||
}
|
}
|
||||||
|
@ -300,28 +341,3 @@ var _Health_serviceDesc = grpc.ServiceDesc{
|
||||||
},
|
},
|
||||||
Metadata: "grpc/health/v1/health.proto",
|
Metadata: "grpc/health/v1/health.proto",
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { proto.RegisterFile("grpc/health/v1/health.proto", fileDescriptor_health_6b1a06aa67f91efd) }
|
|
||||||
|
|
||||||
var fileDescriptor_health_6b1a06aa67f91efd = []byte{
|
|
||||||
// 297 bytes of a gzipped FileDescriptorProto
|
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4e, 0x2f, 0x2a, 0x48,
|
|
||||||
0xd6, 0xcf, 0x48, 0x4d, 0xcc, 0x29, 0xc9, 0xd0, 0x2f, 0x33, 0x84, 0xb2, 0xf4, 0x0a, 0x8a, 0xf2,
|
|
||||||
0x4b, 0xf2, 0x85, 0xf8, 0x40, 0x92, 0x7a, 0x50, 0xa1, 0x32, 0x43, 0x25, 0x3d, 0x2e, 0x21, 0x0f,
|
|
||||||
0x30, 0xc7, 0x39, 0x23, 0x35, 0x39, 0x3b, 0x28, 0xb5, 0xb0, 0x34, 0xb5, 0xb8, 0x44, 0x48, 0x82,
|
|
||||||
0x8b, 0xbd, 0x38, 0xb5, 0xa8, 0x2c, 0x33, 0x39, 0x55, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, 0x08,
|
|
||||||
0xc6, 0x55, 0xda, 0xc8, 0xc8, 0x25, 0x8c, 0xa2, 0xa1, 0xb8, 0x20, 0x3f, 0xaf, 0x38, 0x55, 0xc8,
|
|
||||||
0x93, 0x8b, 0xad, 0xb8, 0x24, 0xb1, 0xa4, 0xb4, 0x18, 0xac, 0x81, 0xcf, 0xc8, 0x50, 0x0f, 0xd5,
|
|
||||||
0x22, 0x3d, 0x2c, 0x9a, 0xf4, 0x82, 0x41, 0x86, 0xe6, 0xa5, 0x07, 0x83, 0x35, 0x06, 0x41, 0x0d,
|
|
||||||
0x50, 0xf2, 0xe7, 0xe2, 0x45, 0x91, 0x10, 0xe2, 0xe6, 0x62, 0x0f, 0xf5, 0xf3, 0xf6, 0xf3, 0x0f,
|
|
||||||
0xf7, 0x13, 0x60, 0x00, 0x71, 0x82, 0x5d, 0x83, 0xc2, 0x3c, 0xfd, 0xdc, 0x05, 0x18, 0x85, 0xf8,
|
|
||||||
0xb9, 0xb8, 0xfd, 0xfc, 0x43, 0xe2, 0x61, 0x02, 0x4c, 0x42, 0xc2, 0x5c, 0xfc, 0x60, 0x8e, 0xb3,
|
|
||||||
0x6b, 0x3c, 0x4c, 0x0b, 0xb3, 0xd1, 0x3a, 0x46, 0x2e, 0x36, 0x88, 0xf5, 0x42, 0x01, 0x5c, 0xac,
|
|
||||||
0x60, 0x27, 0x08, 0x29, 0xe1, 0x75, 0x1f, 0x38, 0x14, 0xa4, 0x94, 0x89, 0xf0, 0x83, 0x50, 0x10,
|
|
||||||
0x17, 0x6b, 0x78, 0x62, 0x49, 0x72, 0x06, 0xd5, 0x4c, 0x34, 0x60, 0x74, 0x4a, 0xe4, 0x12, 0xcc,
|
|
||||||
0xcc, 0x47, 0x53, 0xea, 0xc4, 0x0d, 0x51, 0x1b, 0x00, 0x8a, 0xc6, 0x00, 0xc6, 0x28, 0x9d, 0xf4,
|
|
||||||
0xfc, 0xfc, 0xf4, 0x9c, 0x54, 0xbd, 0xf4, 0xfc, 0x9c, 0xc4, 0xbc, 0x74, 0xbd, 0xfc, 0xa2, 0x74,
|
|
||||||
0x7d, 0xe4, 0x78, 0x07, 0xb1, 0xe3, 0x21, 0xec, 0xf8, 0x32, 0xc3, 0x55, 0x4c, 0x7c, 0xee, 0x20,
|
|
||||||
0xd3, 0x20, 0x46, 0xe8, 0x85, 0x19, 0x26, 0xb1, 0x81, 0x93, 0x83, 0x31, 0x20, 0x00, 0x00, 0xff,
|
|
||||||
0xff, 0x12, 0x7d, 0x96, 0xcb, 0x2d, 0x02, 0x00, 0x00,
|
|
||||||
}
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ import (
|
||||||
|
|
||||||
// Server implements `service Health`.
|
// Server implements `service Health`.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
mu sync.Mutex
|
mu sync.RWMutex
|
||||||
// If shutdown is true, it's expected all serving status is NOT_SERVING, and
|
// If shutdown is true, it's expected all serving status is NOT_SERVING, and
|
||||||
// will stay in NOT_SERVING.
|
// will stay in NOT_SERVING.
|
||||||
shutdown bool
|
shutdown bool
|
||||||
|
@ -54,8 +54,8 @@ func NewServer() *Server {
|
||||||
|
|
||||||
// Check implements `service Health`.
|
// Check implements `service Health`.
|
||||||
func (s *Server) Check(ctx context.Context, in *healthpb.HealthCheckRequest) (*healthpb.HealthCheckResponse, error) {
|
func (s *Server) Check(ctx context.Context, in *healthpb.HealthCheckRequest) (*healthpb.HealthCheckResponse, error) {
|
||||||
s.mu.Lock()
|
s.mu.RLock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.RUnlock()
|
||||||
if servingStatus, ok := s.statusMap[in.Service]; ok {
|
if servingStatus, ok := s.statusMap[in.Service]; ok {
|
||||||
return &healthpb.HealthCheckResponse{
|
return &healthpb.HealthCheckResponse{
|
||||||
Status: servingStatus,
|
Status: servingStatus,
|
||||||
|
@ -139,7 +139,7 @@ func (s *Server) setServingStatusLocked(service string, servingStatus healthpb.H
|
||||||
// Shutdown sets all serving status to NOT_SERVING, and configures the server to
|
// Shutdown sets all serving status to NOT_SERVING, and configures the server to
|
||||||
// ignore all future status changes.
|
// ignore all future status changes.
|
||||||
//
|
//
|
||||||
// This changes serving status for all services. To set status for a perticular
|
// This changes serving status for all services. To set status for a particular
|
||||||
// services, call SetServingStatus().
|
// services, call SetServingStatus().
|
||||||
func (s *Server) Shutdown() {
|
func (s *Server) Shutdown() {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
|
@ -153,7 +153,7 @@ func (s *Server) Shutdown() {
|
||||||
// Resume sets all serving status to SERVING, and configures the server to
|
// Resume sets all serving status to SERVING, and configures the server to
|
||||||
// accept all future status changes.
|
// accept all future status changes.
|
||||||
//
|
//
|
||||||
// This changes serving status for all services. To set status for a perticular
|
// This changes serving status for all services. To set status for a particular
|
||||||
// services, call SetServingStatus().
|
// services, call SetServingStatus().
|
||||||
func (s *Server) Resume() {
|
func (s *Server) Resume() {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
|
|
|
@ -25,44 +25,39 @@ package backoff
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
grpcbackoff "google.golang.org/grpc/backoff"
|
||||||
"google.golang.org/grpc/internal/grpcrand"
|
"google.golang.org/grpc/internal/grpcrand"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Strategy defines the methodology for backing off after a grpc connection
|
// Strategy defines the methodology for backing off after a grpc connection
|
||||||
// failure.
|
// failure.
|
||||||
//
|
|
||||||
type Strategy interface {
|
type Strategy interface {
|
||||||
// Backoff returns the amount of time to wait before the next retry given
|
// Backoff returns the amount of time to wait before the next retry given
|
||||||
// the number of consecutive failures.
|
// the number of consecutive failures.
|
||||||
Backoff(retries int) time.Duration
|
Backoff(retries int) time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
// DefaultExponential is an exponential backoff implementation using the
|
||||||
// baseDelay is the amount of time to wait before retrying after the first
|
// default values for all the configurable knobs defined in
|
||||||
// failure.
|
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
|
||||||
baseDelay = 1.0 * time.Second
|
var DefaultExponential = Exponential{Config: grpcbackoff.DefaultConfig}
|
||||||
// factor is applied to the backoff after each retry.
|
|
||||||
factor = 1.6
|
|
||||||
// jitter provides a range to randomize backoff delays.
|
|
||||||
jitter = 0.2
|
|
||||||
)
|
|
||||||
|
|
||||||
// Exponential implements exponential backoff algorithm as defined in
|
// Exponential implements exponential backoff algorithm as defined in
|
||||||
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
|
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
|
||||||
type Exponential struct {
|
type Exponential struct {
|
||||||
// MaxDelay is the upper bound of backoff delay.
|
// Config contains all options to configure the backoff algorithm.
|
||||||
MaxDelay time.Duration
|
Config grpcbackoff.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backoff returns the amount of time to wait before the next retry given the
|
// Backoff returns the amount of time to wait before the next retry given the
|
||||||
// number of retries.
|
// number of retries.
|
||||||
func (bc Exponential) Backoff(retries int) time.Duration {
|
func (bc Exponential) Backoff(retries int) time.Duration {
|
||||||
if retries == 0 {
|
if retries == 0 {
|
||||||
return baseDelay
|
return bc.Config.BaseDelay
|
||||||
}
|
}
|
||||||
backoff, max := float64(baseDelay), float64(bc.MaxDelay)
|
backoff, max := float64(bc.Config.BaseDelay), float64(bc.Config.MaxDelay)
|
||||||
for backoff < max && retries > 0 {
|
for backoff < max && retries > 0 {
|
||||||
backoff *= factor
|
backoff *= bc.Config.Multiplier
|
||||||
retries--
|
retries--
|
||||||
}
|
}
|
||||||
if backoff > max {
|
if backoff > max {
|
||||||
|
@ -70,7 +65,7 @@ func (bc Exponential) Backoff(retries int) time.Duration {
|
||||||
}
|
}
|
||||||
// Randomize backoff delays so that if a cluster of requests start at
|
// Randomize backoff delays so that if a cluster of requests start at
|
||||||
// the same time, they won't operate in lockstep.
|
// the same time, they won't operate in lockstep.
|
||||||
backoff *= 1 + jitter*(grpcrand.Float64()*2-1)
|
backoff *= 1 + bc.Config.Jitter*(grpcrand.Float64()*2-1)
|
||||||
if backoff < 0 {
|
if backoff < 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ type Logger interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// binLogger is the global binary logger for the binary. One of this should be
|
// binLogger is the global binary logger for the binary. One of this should be
|
||||||
// built at init time from the configuration (environment varialbe or flags).
|
// built at init time from the configuration (environment variable or flags).
|
||||||
//
|
//
|
||||||
// It is used to get a methodLogger for each individual method.
|
// It is used to get a methodLogger for each individual method.
|
||||||
var binLogger Logger
|
var binLogger Logger
|
||||||
|
@ -98,7 +98,7 @@ func (l *logger) setDefaultMethodLogger(ml *methodLoggerConfig) error {
|
||||||
// New methodLogger with same service overrides the old one.
|
// New methodLogger with same service overrides the old one.
|
||||||
func (l *logger) setServiceMethodLogger(service string, ml *methodLoggerConfig) error {
|
func (l *logger) setServiceMethodLogger(service string, ml *methodLoggerConfig) error {
|
||||||
if _, ok := l.services[service]; ok {
|
if _, ok := l.services[service]; ok {
|
||||||
return fmt.Errorf("conflicting rules for service %v found", service)
|
return fmt.Errorf("conflicting service rules for service %v found", service)
|
||||||
}
|
}
|
||||||
if l.services == nil {
|
if l.services == nil {
|
||||||
l.services = make(map[string]*methodLoggerConfig)
|
l.services = make(map[string]*methodLoggerConfig)
|
||||||
|
@ -112,10 +112,10 @@ func (l *logger) setServiceMethodLogger(service string, ml *methodLoggerConfig)
|
||||||
// New methodLogger with same method overrides the old one.
|
// New methodLogger with same method overrides the old one.
|
||||||
func (l *logger) setMethodMethodLogger(method string, ml *methodLoggerConfig) error {
|
func (l *logger) setMethodMethodLogger(method string, ml *methodLoggerConfig) error {
|
||||||
if _, ok := l.blacklist[method]; ok {
|
if _, ok := l.blacklist[method]; ok {
|
||||||
return fmt.Errorf("conflicting rules for method %v found", method)
|
return fmt.Errorf("conflicting blacklist rules for method %v found", method)
|
||||||
}
|
}
|
||||||
if _, ok := l.methods[method]; ok {
|
if _, ok := l.methods[method]; ok {
|
||||||
return fmt.Errorf("conflicting rules for method %v found", method)
|
return fmt.Errorf("conflicting method rules for method %v found", method)
|
||||||
}
|
}
|
||||||
if l.methods == nil {
|
if l.methods == nil {
|
||||||
l.methods = make(map[string]*methodLoggerConfig)
|
l.methods = make(map[string]*methodLoggerConfig)
|
||||||
|
@ -127,10 +127,10 @@ func (l *logger) setMethodMethodLogger(method string, ml *methodLoggerConfig) er
|
||||||
// Set blacklist method for "-service/method".
|
// Set blacklist method for "-service/method".
|
||||||
func (l *logger) setBlacklist(method string) error {
|
func (l *logger) setBlacklist(method string) error {
|
||||||
if _, ok := l.blacklist[method]; ok {
|
if _, ok := l.blacklist[method]; ok {
|
||||||
return fmt.Errorf("conflicting rules for method %v found", method)
|
return fmt.Errorf("conflicting blacklist rules for method %v found", method)
|
||||||
}
|
}
|
||||||
if _, ok := l.methods[method]; ok {
|
if _, ok := l.methods[method]; ok {
|
||||||
return fmt.Errorf("conflicting rules for method %v found", method)
|
return fmt.Errorf("conflicting method rules for method %v found", method)
|
||||||
}
|
}
|
||||||
if l.blacklist == nil {
|
if l.blacklist == nil {
|
||||||
l.blacklist = make(map[string]struct{})
|
l.blacklist = make(map[string]struct{})
|
||||||
|
|
|
@ -43,7 +43,7 @@ import (
|
||||||
// Foo.
|
// Foo.
|
||||||
//
|
//
|
||||||
// If two configs exist for one certain method or service, the one specified
|
// If two configs exist for one certain method or service, the one specified
|
||||||
// later overrides the privous config.
|
// later overrides the previous config.
|
||||||
func NewLoggerFromConfigString(s string) Logger {
|
func NewLoggerFromConfigString(s string) Logger {
|
||||||
if s == "" {
|
if s == "" {
|
||||||
return nil
|
return nil
|
||||||
|
@ -74,7 +74,7 @@ func (l *logger) fillMethodLoggerWithConfigString(config string) error {
|
||||||
return fmt.Errorf("invalid config: %q, %v", config, err)
|
return fmt.Errorf("invalid config: %q, %v", config, err)
|
||||||
}
|
}
|
||||||
if m == "*" {
|
if m == "*" {
|
||||||
return fmt.Errorf("invalid config: %q, %v", config, "* not allowd in blacklist config")
|
return fmt.Errorf("invalid config: %q, %v", config, "* not allowed in blacklist config")
|
||||||
}
|
}
|
||||||
if suffix != "" {
|
if suffix != "" {
|
||||||
return fmt.Errorf("invalid config: %q, %v", config, "header/message limit not allowed in blacklist config")
|
return fmt.Errorf("invalid config: %q, %v", config, "header/message limit not allowed in blacklist config")
|
||||||
|
|
|
@ -63,7 +63,7 @@ func (ns *noopSink) Close() error { return nil }
|
||||||
|
|
||||||
// newWriterSink creates a binary log sink with the given writer.
|
// newWriterSink creates a binary log sink with the given writer.
|
||||||
//
|
//
|
||||||
// Write() marshalls the proto message and writes it to the given writer. Each
|
// Write() marshals the proto message and writes it to the given writer. Each
|
||||||
// message is prefixed with a 4 byte big endian unsigned integer as the length.
|
// message is prefixed with a 4 byte big endian unsigned integer as the length.
|
||||||
//
|
//
|
||||||
// No buffer is done, Close() doesn't try to close the writer.
|
// No buffer is done, Close() doesn't try to close the writer.
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 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 buffer provides an implementation of an unbounded buffer.
|
||||||
|
package buffer
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
// Unbounded is an implementation of an unbounded buffer which does not use
|
||||||
|
// extra goroutines. This is typically used for passing updates from one entity
|
||||||
|
// to another within gRPC.
|
||||||
|
//
|
||||||
|
// All methods on this type are thread-safe and don't block on anything except
|
||||||
|
// the underlying mutex used for synchronization.
|
||||||
|
//
|
||||||
|
// Unbounded supports values of any type to be stored in it by using a channel
|
||||||
|
// of `interface{}`. This means that a call to Put() incurs an extra memory
|
||||||
|
// allocation, and also that users need a type assertion while reading. For
|
||||||
|
// performance critical code paths, using Unbounded is strongly discouraged and
|
||||||
|
// defining a new type specific implementation of this buffer is preferred. See
|
||||||
|
// internal/transport/transport.go for an example of this.
|
||||||
|
type Unbounded struct {
|
||||||
|
c chan interface{}
|
||||||
|
mu sync.Mutex
|
||||||
|
backlog []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUnbounded returns a new instance of Unbounded.
|
||||||
|
func NewUnbounded() *Unbounded {
|
||||||
|
return &Unbounded{c: make(chan interface{}, 1)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put adds t to the unbounded buffer.
|
||||||
|
func (b *Unbounded) Put(t interface{}) {
|
||||||
|
b.mu.Lock()
|
||||||
|
if len(b.backlog) == 0 {
|
||||||
|
select {
|
||||||
|
case b.c <- t:
|
||||||
|
b.mu.Unlock()
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.backlog = append(b.backlog, t)
|
||||||
|
b.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load sends the earliest buffered data, if any, onto the read channel
|
||||||
|
// returned by Get(). Users are expected to call this every time they read a
|
||||||
|
// value from the read channel.
|
||||||
|
func (b *Unbounded) Load() {
|
||||||
|
b.mu.Lock()
|
||||||
|
if len(b.backlog) > 0 {
|
||||||
|
select {
|
||||||
|
case b.c <- b.backlog[0]:
|
||||||
|
b.backlog[0] = nil
|
||||||
|
b.backlog = b.backlog[1:]
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a read channel on which values added to the buffer, via Put(),
|
||||||
|
// are sent on.
|
||||||
|
//
|
||||||
|
// Upon reading a value from this channel, users are expected to call Load() to
|
||||||
|
// send the next buffered value onto the channel if there is any.
|
||||||
|
func (b *Unbounded) Get() <-chan interface{} {
|
||||||
|
return b.c
|
||||||
|
}
|
|
@ -27,38 +27,12 @@ import (
|
||||||
const (
|
const (
|
||||||
prefix = "GRPC_GO_"
|
prefix = "GRPC_GO_"
|
||||||
retryStr = prefix + "RETRY"
|
retryStr = prefix + "RETRY"
|
||||||
requireHandshakeStr = prefix + "REQUIRE_HANDSHAKE"
|
txtErrIgnoreStr = prefix + "IGNORE_TXT_ERRORS"
|
||||||
)
|
|
||||||
|
|
||||||
// RequireHandshakeSetting describes the settings for handshaking.
|
|
||||||
type RequireHandshakeSetting int
|
|
||||||
|
|
||||||
const (
|
|
||||||
// RequireHandshakeOn indicates to wait for handshake before considering a
|
|
||||||
// connection ready/successful.
|
|
||||||
RequireHandshakeOn RequireHandshakeSetting = iota
|
|
||||||
// RequireHandshakeOff indicates to not wait for handshake before
|
|
||||||
// considering a connection ready/successful.
|
|
||||||
RequireHandshakeOff
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Retry is set if retry is explicitly enabled via "GRPC_GO_RETRY=on".
|
// Retry is set if retry is explicitly enabled via "GRPC_GO_RETRY=on".
|
||||||
Retry = strings.EqualFold(os.Getenv(retryStr), "on")
|
Retry = strings.EqualFold(os.Getenv(retryStr), "on")
|
||||||
// RequireHandshake is set based upon the GRPC_GO_REQUIRE_HANDSHAKE
|
// TXTErrIgnore is set if TXT errors should be ignored ("GRPC_GO_IGNORE_TXT_ERRORS" is not "false").
|
||||||
// environment variable.
|
TXTErrIgnore = !strings.EqualFold(os.Getenv(retryStr), "false")
|
||||||
//
|
|
||||||
// Will be removed after the 1.18 release.
|
|
||||||
RequireHandshake = RequireHandshakeOn
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
switch strings.ToLower(os.Getenv(requireHandshakeStr)) {
|
|
||||||
case "on":
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
RequireHandshake = RequireHandshakeOn
|
|
||||||
case "off":
|
|
||||||
RequireHandshake = RequireHandshakeOff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -23,12 +23,12 @@ package internal
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/connectivity"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// WithResolverBuilder is exported by dialoptions.go
|
// WithHealthCheckFunc is set by dialoptions.go
|
||||||
WithResolverBuilder interface{} // func (resolver.Builder) grpc.DialOption
|
|
||||||
// WithHealthCheckFunc is not exported by dialoptions.go
|
|
||||||
WithHealthCheckFunc interface{} // func (HealthChecker) DialOption
|
WithHealthCheckFunc interface{} // func (HealthChecker) DialOption
|
||||||
// HealthCheckFunc is used to provide client-side LB channel health checking
|
// HealthCheckFunc is used to provide client-side LB channel health checking
|
||||||
HealthCheckFunc HealthChecker
|
HealthCheckFunc HealthChecker
|
||||||
|
@ -37,10 +37,28 @@ var (
|
||||||
// KeepaliveMinPingTime is the minimum ping interval. This must be 10s by
|
// KeepaliveMinPingTime is the minimum ping interval. This must be 10s by
|
||||||
// default, but tests may wish to set it lower for convenience.
|
// default, but tests may wish to set it lower for convenience.
|
||||||
KeepaliveMinPingTime = 10 * time.Second
|
KeepaliveMinPingTime = 10 * time.Second
|
||||||
|
// StatusRawProto is exported by status/status.go. This func returns a
|
||||||
|
// pointer to the wrapped Status proto for a given status.Status without a
|
||||||
|
// call to proto.Clone(). The returned Status proto should not be mutated by
|
||||||
|
// the caller.
|
||||||
|
StatusRawProto interface{} // func (*status.Status) *spb.Status
|
||||||
|
// NewRequestInfoContext creates a new context based on the argument context attaching
|
||||||
|
// the passed in RequestInfo to the new context.
|
||||||
|
NewRequestInfoContext interface{} // func(context.Context, credentials.RequestInfo) context.Context
|
||||||
|
// ParseServiceConfigForTesting is for creating a fake
|
||||||
|
// ClientConn for resolver testing only
|
||||||
|
ParseServiceConfigForTesting interface{} // func(string) *serviceconfig.ParseResult
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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.
|
||||||
type HealthChecker func(ctx context.Context, newStream func() (interface{}, error), reportHealth func(bool), serviceName string) error
|
//
|
||||||
|
// The implementation is expected to create a health checking RPC stream by
|
||||||
|
// calling newStream(), watch for the health status of serviceName, and report
|
||||||
|
// it's health back by calling setConnectivityState().
|
||||||
|
//
|
||||||
|
// The health checking protocol is defined at:
|
||||||
|
// https://github.com/grpc/grpc/blob/master/doc/health-checking.md
|
||||||
|
type HealthChecker func(ctx context.Context, newStream func(string) (interface{}, error), setConnectivityState func(connectivity.State, error), serviceName string) error
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// CredsBundleModeFallback switches GoogleDefaultCreds to fallback mode.
|
// CredsBundleModeFallback switches GoogleDefaultCreds to fallback mode.
|
||||||
|
|
|
@ -33,18 +33,22 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
"google.golang.org/grpc/internal/backoff"
|
"google.golang.org/grpc/internal/envconfig"
|
||||||
"google.golang.org/grpc/internal/grpcrand"
|
"google.golang.org/grpc/internal/grpcrand"
|
||||||
"google.golang.org/grpc/resolver"
|
"google.golang.org/grpc/resolver"
|
||||||
|
"google.golang.org/grpc/serviceconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// EnableSRVLookups controls whether the DNS resolver attempts to fetch gRPCLB
|
||||||
|
// addresses from SRV records. Must not be changed after init time.
|
||||||
|
var EnableSRVLookups = false
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
resolver.Register(NewBuilder())
|
resolver.Register(NewBuilder())
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultPort = "443"
|
defaultPort = "443"
|
||||||
defaultFreq = time.Minute * 30
|
|
||||||
defaultDNSSvrPort = "53"
|
defaultDNSSvrPort = "53"
|
||||||
golang = "GO"
|
golang = "GO"
|
||||||
// txtPrefix is the prefix string to be prepended to the host name for txt record lookup.
|
// txtPrefix is the prefix string to be prepended to the host name for txt record lookup.
|
||||||
|
@ -94,47 +98,33 @@ var customAuthorityResolver = func(authority string) (netResolver, error) {
|
||||||
|
|
||||||
// NewBuilder creates a dnsBuilder which is used to factory DNS resolvers.
|
// NewBuilder creates a dnsBuilder which is used to factory DNS resolvers.
|
||||||
func NewBuilder() resolver.Builder {
|
func NewBuilder() resolver.Builder {
|
||||||
return &dnsBuilder{minFreq: defaultFreq}
|
return &dnsBuilder{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type dnsBuilder struct {
|
type dnsBuilder struct{}
|
||||||
// minimum frequency of polling the DNS server.
|
|
||||||
minFreq time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build creates and starts a DNS resolver that watches the name resolution of the target.
|
// Build creates and starts a DNS resolver that watches the name resolution of the target.
|
||||||
func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) {
|
func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
|
||||||
host, port, err := parseTarget(target.Endpoint, defaultPort)
|
host, port, err := parseTarget(target.Endpoint, defaultPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// IP address.
|
// IP address.
|
||||||
if net.ParseIP(host) != nil {
|
if ipAddr, ok := formatIP(host); ok {
|
||||||
host, _ = formatIP(host)
|
addr := []resolver.Address{{Addr: ipAddr + ":" + port}}
|
||||||
addr := []resolver.Address{{Addr: host + ":" + port}}
|
cc.UpdateState(resolver.State{Addresses: addr})
|
||||||
i := &ipResolver{
|
return deadResolver{}, nil
|
||||||
cc: cc,
|
|
||||||
ip: addr,
|
|
||||||
rn: make(chan struct{}, 1),
|
|
||||||
q: make(chan struct{}),
|
|
||||||
}
|
|
||||||
cc.NewAddress(addr)
|
|
||||||
go i.watcher()
|
|
||||||
return i, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNS address (non-IP).
|
// DNS address (non-IP).
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
d := &dnsResolver{
|
d := &dnsResolver{
|
||||||
freq: b.minFreq,
|
|
||||||
backoff: backoff.Exponential{MaxDelay: b.minFreq},
|
|
||||||
host: host,
|
host: host,
|
||||||
port: port,
|
port: port,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
cc: cc,
|
cc: cc,
|
||||||
t: time.NewTimer(0),
|
|
||||||
rn: make(chan struct{}, 1),
|
rn: make(chan struct{}, 1),
|
||||||
disableServiceConfig: opts.DisableServiceConfig,
|
disableServiceConfig: opts.DisableServiceConfig,
|
||||||
}
|
}
|
||||||
|
@ -150,6 +140,7 @@ func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts
|
||||||
|
|
||||||
d.wg.Add(1)
|
d.wg.Add(1)
|
||||||
go d.watcher()
|
go d.watcher()
|
||||||
|
d.ResolveNow(resolver.ResolveNowOptions{})
|
||||||
return d, nil
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,44 +155,15 @@ type netResolver interface {
|
||||||
LookupTXT(ctx context.Context, name string) (txts []string, err error)
|
LookupTXT(ctx context.Context, name string) (txts []string, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ipResolver watches for the name resolution update for an IP address.
|
// deadResolver is a resolver that does nothing.
|
||||||
type ipResolver struct {
|
type deadResolver struct{}
|
||||||
cc resolver.ClientConn
|
|
||||||
ip []resolver.Address
|
|
||||||
// rn channel is used by ResolveNow() to force an immediate resolution of the target.
|
|
||||||
rn chan struct{}
|
|
||||||
q chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResolveNow resend the address it stores, no resolution is needed.
|
func (deadResolver) ResolveNow(resolver.ResolveNowOptions) {}
|
||||||
func (i *ipResolver) ResolveNow(opt resolver.ResolveNowOption) {
|
|
||||||
select {
|
|
||||||
case i.rn <- struct{}{}:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the ipResolver.
|
func (deadResolver) Close() {}
|
||||||
func (i *ipResolver) Close() {
|
|
||||||
close(i.q)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ipResolver) watcher() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-i.rn:
|
|
||||||
i.cc.NewAddress(i.ip)
|
|
||||||
case <-i.q:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// dnsResolver watches for the name resolution update for a non-IP target.
|
// dnsResolver watches for the name resolution update for a non-IP target.
|
||||||
type dnsResolver struct {
|
type dnsResolver struct {
|
||||||
freq time.Duration
|
|
||||||
backoff backoff.Exponential
|
|
||||||
retryCount int
|
|
||||||
host string
|
host string
|
||||||
port string
|
port string
|
||||||
resolver netResolver
|
resolver netResolver
|
||||||
|
@ -210,7 +172,6 @@ type dnsResolver struct {
|
||||||
cc resolver.ClientConn
|
cc resolver.ClientConn
|
||||||
// rn channel is used by ResolveNow() to force an immediate resolution of the target.
|
// rn channel is used by ResolveNow() to force an immediate resolution of the target.
|
||||||
rn chan struct{}
|
rn chan struct{}
|
||||||
t *time.Timer
|
|
||||||
// wg is used to enforce Close() to return after the watcher() goroutine has finished.
|
// wg is used to enforce Close() to return after the watcher() goroutine has finished.
|
||||||
// Otherwise, data race will be possible. [Race Example] in dns_resolver_test we
|
// Otherwise, data race will be possible. [Race Example] in dns_resolver_test we
|
||||||
// replace the real lookup functions with mocked ones to facilitate testing.
|
// replace the real lookup functions with mocked ones to facilitate testing.
|
||||||
|
@ -222,7 +183,7 @@ type dnsResolver struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveNow invoke an immediate resolution of the target that this dnsResolver watches.
|
// ResolveNow invoke an immediate resolution of the target that this dnsResolver watches.
|
||||||
func (d *dnsResolver) ResolveNow(opt resolver.ResolveNowOption) {
|
func (d *dnsResolver) ResolveNow(resolver.ResolveNowOptions) {
|
||||||
select {
|
select {
|
||||||
case d.rn <- struct{}{}:
|
case d.rn <- struct{}{}:
|
||||||
default:
|
default:
|
||||||
|
@ -233,7 +194,6 @@ func (d *dnsResolver) ResolveNow(opt resolver.ResolveNowOption) {
|
||||||
func (d *dnsResolver) Close() {
|
func (d *dnsResolver) Close() {
|
||||||
d.cancel()
|
d.cancel()
|
||||||
d.wg.Wait()
|
d.wg.Wait()
|
||||||
d.t.Stop()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dnsResolver) watcher() {
|
func (d *dnsResolver) watcher() {
|
||||||
|
@ -242,27 +202,15 @@ func (d *dnsResolver) watcher() {
|
||||||
select {
|
select {
|
||||||
case <-d.ctx.Done():
|
case <-d.ctx.Done():
|
||||||
return
|
return
|
||||||
case <-d.t.C:
|
|
||||||
case <-d.rn:
|
case <-d.rn:
|
||||||
if !d.t.Stop() {
|
|
||||||
// Before resetting a timer, it should be stopped to prevent racing with
|
|
||||||
// reads on it's channel.
|
|
||||||
<-d.t.C
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result, sc := d.lookup()
|
state, err := d.lookup()
|
||||||
// Next lookup should happen within an interval defined by d.freq. It may be
|
if err != nil {
|
||||||
// more often due to exponential retry on empty address list.
|
d.cc.ReportError(err)
|
||||||
if len(result) == 0 {
|
|
||||||
d.retryCount++
|
|
||||||
d.t.Reset(d.backoff.Backoff(d.retryCount))
|
|
||||||
} else {
|
} else {
|
||||||
d.retryCount = 0
|
d.cc.UpdateState(*state)
|
||||||
d.t.Reset(d.freq)
|
|
||||||
}
|
}
|
||||||
d.cc.NewServiceConfig(sc)
|
|
||||||
d.cc.NewAddress(result)
|
|
||||||
|
|
||||||
// Sleep to prevent excessive re-resolutions. Incoming resolution requests
|
// Sleep to prevent excessive re-resolutions. Incoming resolution requests
|
||||||
// will be queued in d.rn.
|
// will be queued in d.rn.
|
||||||
|
@ -276,37 +224,68 @@ func (d *dnsResolver) watcher() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dnsResolver) lookupSRV() []resolver.Address {
|
func (d *dnsResolver) lookupSRV() ([]resolver.Address, error) {
|
||||||
|
if !EnableSRVLookups {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
var newAddrs []resolver.Address
|
var newAddrs []resolver.Address
|
||||||
_, srvs, err := d.resolver.LookupSRV(d.ctx, "grpclb", "tcp", d.host)
|
_, srvs, err := d.resolver.LookupSRV(d.ctx, "grpclb", "tcp", d.host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Infof("grpc: failed dns SRV record lookup due to %v.\n", err)
|
err = handleDNSError(err, "SRV") // may become nil
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, s := range srvs {
|
for _, s := range srvs {
|
||||||
lbAddrs, err := d.resolver.LookupHost(d.ctx, s.Target)
|
lbAddrs, err := d.resolver.LookupHost(d.ctx, s.Target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Infof("grpc: failed load balancer address dns lookup due to %v.\n", err)
|
err = handleDNSError(err, "A") // may become nil
|
||||||
|
if err == nil {
|
||||||
|
// If there are other SRV records, look them up and ignore this
|
||||||
|
// one that does not exist.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
for _, a := range lbAddrs {
|
for _, a := range lbAddrs {
|
||||||
a, ok := formatIP(a)
|
ip, ok := formatIP(a)
|
||||||
if !ok {
|
if !ok {
|
||||||
grpclog.Errorf("grpc: failed IP parsing due to %v.\n", err)
|
return nil, fmt.Errorf("dns: error parsing A record IP address %v", a)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
addr := a + ":" + strconv.Itoa(int(s.Port))
|
addr := ip + ":" + strconv.Itoa(int(s.Port))
|
||||||
newAddrs = append(newAddrs, resolver.Address{Addr: addr, Type: resolver.GRPCLB, ServerName: s.Target})
|
newAddrs = append(newAddrs, resolver.Address{Addr: addr, Type: resolver.GRPCLB, ServerName: s.Target})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newAddrs
|
return newAddrs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dnsResolver) lookupTXT() string {
|
var filterError = func(err error) error {
|
||||||
|
if dnsErr, ok := err.(*net.DNSError); ok && !dnsErr.IsTimeout && !dnsErr.IsTemporary {
|
||||||
|
// Timeouts and temporary errors should be communicated to gRPC to
|
||||||
|
// attempt another DNS query (with backoff). Other errors should be
|
||||||
|
// suppressed (they may represent the absence of a TXT record).
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleDNSError(err error, lookupType string) error {
|
||||||
|
err = filterError(err)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("dns: %v record lookup error: %v", lookupType, err)
|
||||||
|
grpclog.Infoln(err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dnsResolver) lookupTXT() *serviceconfig.ParseResult {
|
||||||
ss, err := d.resolver.LookupTXT(d.ctx, txtPrefix+d.host)
|
ss, err := d.resolver.LookupTXT(d.ctx, txtPrefix+d.host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Infof("grpc: failed dns TXT record lookup due to %v.\n", err)
|
if envconfig.TXTErrIgnore {
|
||||||
return ""
|
return nil
|
||||||
|
}
|
||||||
|
if err = handleDNSError(err, "TXT"); err != nil {
|
||||||
|
return &serviceconfig.ParseResult{Err: err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
var res string
|
var res string
|
||||||
for _, s := range ss {
|
for _, s := range ss {
|
||||||
|
@ -315,40 +294,45 @@ func (d *dnsResolver) lookupTXT() string {
|
||||||
|
|
||||||
// TXT record must have "grpc_config=" attribute in order to be used as service config.
|
// TXT record must have "grpc_config=" attribute in order to be used as service config.
|
||||||
if !strings.HasPrefix(res, txtAttribute) {
|
if !strings.HasPrefix(res, txtAttribute) {
|
||||||
grpclog.Warningf("grpc: TXT record %v missing %v attribute", res, txtAttribute)
|
grpclog.Warningf("dns: TXT record %v missing %v attribute", res, txtAttribute)
|
||||||
return ""
|
// This is not an error; it is the equivalent of not having a service config.
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return strings.TrimPrefix(res, txtAttribute)
|
sc := canaryingSC(strings.TrimPrefix(res, txtAttribute))
|
||||||
|
return d.cc.ParseServiceConfig(sc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dnsResolver) lookupHost() []resolver.Address {
|
func (d *dnsResolver) lookupHost() ([]resolver.Address, error) {
|
||||||
var newAddrs []resolver.Address
|
var newAddrs []resolver.Address
|
||||||
addrs, err := d.resolver.LookupHost(d.ctx, d.host)
|
addrs, err := d.resolver.LookupHost(d.ctx, d.host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Warningf("grpc: failed dns A record lookup due to %v.\n", err)
|
err = handleDNSError(err, "A")
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, a := range addrs {
|
for _, a := range addrs {
|
||||||
a, ok := formatIP(a)
|
ip, ok := formatIP(a)
|
||||||
if !ok {
|
if !ok {
|
||||||
grpclog.Errorf("grpc: failed IP parsing due to %v.\n", err)
|
return nil, fmt.Errorf("dns: error parsing A record IP address %v", a)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
addr := a + ":" + d.port
|
addr := ip + ":" + d.port
|
||||||
newAddrs = append(newAddrs, resolver.Address{Addr: addr})
|
newAddrs = append(newAddrs, resolver.Address{Addr: addr})
|
||||||
}
|
}
|
||||||
return newAddrs
|
return newAddrs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dnsResolver) lookup() ([]resolver.Address, string) {
|
func (d *dnsResolver) lookup() (*resolver.State, error) {
|
||||||
newAddrs := d.lookupSRV()
|
srv, srvErr := d.lookupSRV()
|
||||||
// Support fallback to non-balancer address.
|
addrs, hostErr := d.lookupHost()
|
||||||
newAddrs = append(newAddrs, d.lookupHost()...)
|
if hostErr != nil && (srvErr != nil || len(srv) == 0) {
|
||||||
if d.disableServiceConfig {
|
return nil, hostErr
|
||||||
return newAddrs, ""
|
|
||||||
}
|
}
|
||||||
sc := d.lookupTXT()
|
state := &resolver.State{
|
||||||
return newAddrs, canaryingSC(sc)
|
Addresses: append(addrs, srv...),
|
||||||
|
}
|
||||||
|
if !d.disableServiceConfig {
|
||||||
|
state.ServiceConfig = d.lookupTXT()
|
||||||
|
}
|
||||||
|
return state, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// formatIP returns ok = false if addr is not a valid textual representation of an IP address.
|
// formatIP returns ok = false if addr is not a valid textual representation of an IP address.
|
||||||
|
@ -434,12 +418,12 @@ func canaryingSC(js string) string {
|
||||||
var rcs []rawChoice
|
var rcs []rawChoice
|
||||||
err := json.Unmarshal([]byte(js), &rcs)
|
err := json.Unmarshal([]byte(js), &rcs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Warningf("grpc: failed to parse service config json string due to %v.\n", err)
|
grpclog.Warningf("dns: error parsing service config json: %v", err)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
cliHostname, err := os.Hostname()
|
cliHostname, err := os.Hostname()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Warningf("grpc: failed to get client hostname due to %v.\n", err)
|
grpclog.Warningf("dns: error getting client hostname: %v", err)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
var sc string
|
var sc string
|
|
@ -0,0 +1,33 @@
|
||||||
|
// +build go1.13
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2019 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
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
filterError = func(err error) error {
|
||||||
|
if dnsErr, ok := err.(*net.DNSError); ok && dnsErr.IsNotFound {
|
||||||
|
// The name does not exist; not an error.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,7 +26,7 @@ const scheme = "passthrough"
|
||||||
|
|
||||||
type passthroughBuilder struct{}
|
type passthroughBuilder struct{}
|
||||||
|
|
||||||
func (*passthroughBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) {
|
func (*passthroughBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
|
||||||
r := &passthroughResolver{
|
r := &passthroughResolver{
|
||||||
target: target,
|
target: target,
|
||||||
cc: cc,
|
cc: cc,
|
||||||
|
@ -48,7 +48,7 @@ func (r *passthroughResolver) start() {
|
||||||
r.cc.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: r.target.Endpoint}}})
|
r.cc.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: r.target.Endpoint}}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*passthroughResolver) ResolveNow(o resolver.ResolveNowOption) {}
|
func (*passthroughResolver) ResolveNow(o resolver.ResolveNowOptions) {}
|
||||||
|
|
||||||
func (*passthroughResolver) Close() {}
|
func (*passthroughResolver) Close() {}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"golang.org/x/net/http2/hpack"
|
"golang.org/x/net/http2/hpack"
|
||||||
|
@ -84,24 +85,40 @@ func (il *itemList) isEmpty() bool {
|
||||||
// the control buffer of transport. They represent different aspects of
|
// the control buffer of transport. They represent different aspects of
|
||||||
// control tasks, e.g., flow control, settings, streaming resetting, etc.
|
// control tasks, e.g., flow control, settings, streaming resetting, etc.
|
||||||
|
|
||||||
|
// maxQueuedTransportResponseFrames is the most queued "transport response"
|
||||||
|
// frames we will buffer before preventing new reads from occurring on the
|
||||||
|
// transport. These are control frames sent in response to client requests,
|
||||||
|
// such as RST_STREAM due to bad headers or settings acks.
|
||||||
|
const maxQueuedTransportResponseFrames = 50
|
||||||
|
|
||||||
|
type cbItem interface {
|
||||||
|
isTransportResponseFrame() bool
|
||||||
|
}
|
||||||
|
|
||||||
// registerStream is used to register an incoming stream with loopy writer.
|
// registerStream is used to register an incoming stream with loopy writer.
|
||||||
type registerStream struct {
|
type registerStream struct {
|
||||||
streamID uint32
|
streamID uint32
|
||||||
wq *writeQuota
|
wq *writeQuota
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*registerStream) isTransportResponseFrame() bool { return false }
|
||||||
|
|
||||||
// headerFrame is also used to register stream on the client-side.
|
// headerFrame is also used to register stream on the client-side.
|
||||||
type headerFrame struct {
|
type headerFrame struct {
|
||||||
streamID uint32
|
streamID uint32
|
||||||
hf []hpack.HeaderField
|
hf []hpack.HeaderField
|
||||||
endStream bool // Valid on server side.
|
endStream bool // Valid on server side.
|
||||||
initStream func(uint32) (bool, error) // Used only on the client side.
|
initStream func(uint32) error // Used only on the client side.
|
||||||
onWrite func()
|
onWrite func()
|
||||||
wq *writeQuota // write quota for the stream created.
|
wq *writeQuota // write quota for the stream created.
|
||||||
cleanup *cleanupStream // Valid on the server side.
|
cleanup *cleanupStream // Valid on the server side.
|
||||||
onOrphaned func(error) // Valid on client-side
|
onOrphaned func(error) // Valid on client-side
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *headerFrame) isTransportResponseFrame() bool {
|
||||||
|
return h.cleanup != nil && h.cleanup.rst // Results in a RST_STREAM
|
||||||
|
}
|
||||||
|
|
||||||
type cleanupStream struct {
|
type cleanupStream struct {
|
||||||
streamID uint32
|
streamID uint32
|
||||||
rst bool
|
rst bool
|
||||||
|
@ -109,6 +126,8 @@ type cleanupStream struct {
|
||||||
onWrite func()
|
onWrite func()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *cleanupStream) isTransportResponseFrame() bool { return c.rst } // Results in a RST_STREAM
|
||||||
|
|
||||||
type dataFrame struct {
|
type dataFrame struct {
|
||||||
streamID uint32
|
streamID uint32
|
||||||
endStream bool
|
endStream bool
|
||||||
|
@ -119,27 +138,41 @@ type dataFrame struct {
|
||||||
onEachWrite func()
|
onEachWrite func()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*dataFrame) isTransportResponseFrame() bool { return false }
|
||||||
|
|
||||||
type incomingWindowUpdate struct {
|
type incomingWindowUpdate struct {
|
||||||
streamID uint32
|
streamID uint32
|
||||||
increment uint32
|
increment uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*incomingWindowUpdate) isTransportResponseFrame() bool { return false }
|
||||||
|
|
||||||
type outgoingWindowUpdate struct {
|
type outgoingWindowUpdate struct {
|
||||||
streamID uint32
|
streamID uint32
|
||||||
increment uint32
|
increment uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*outgoingWindowUpdate) isTransportResponseFrame() bool {
|
||||||
|
return false // window updates are throttled by thresholds
|
||||||
|
}
|
||||||
|
|
||||||
type incomingSettings struct {
|
type incomingSettings struct {
|
||||||
ss []http2.Setting
|
ss []http2.Setting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*incomingSettings) isTransportResponseFrame() bool { return true } // Results in a settings ACK
|
||||||
|
|
||||||
type outgoingSettings struct {
|
type outgoingSettings struct {
|
||||||
ss []http2.Setting
|
ss []http2.Setting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*outgoingSettings) isTransportResponseFrame() bool { return false }
|
||||||
|
|
||||||
type incomingGoAway struct {
|
type incomingGoAway struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*incomingGoAway) isTransportResponseFrame() bool { return false }
|
||||||
|
|
||||||
type goAway struct {
|
type goAway struct {
|
||||||
code http2.ErrCode
|
code http2.ErrCode
|
||||||
debugData []byte
|
debugData []byte
|
||||||
|
@ -147,15 +180,21 @@ type goAway struct {
|
||||||
closeConn bool
|
closeConn bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*goAway) isTransportResponseFrame() bool { return false }
|
||||||
|
|
||||||
type ping struct {
|
type ping struct {
|
||||||
ack bool
|
ack bool
|
||||||
data [8]byte
|
data [8]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*ping) isTransportResponseFrame() bool { return true }
|
||||||
|
|
||||||
type outFlowControlSizeRequest struct {
|
type outFlowControlSizeRequest struct {
|
||||||
resp chan uint32
|
resp chan uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*outFlowControlSizeRequest) isTransportResponseFrame() bool { return false }
|
||||||
|
|
||||||
type outStreamState int
|
type outStreamState int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -238,6 +277,14 @@ type controlBuffer struct {
|
||||||
consumerWaiting bool
|
consumerWaiting bool
|
||||||
list *itemList
|
list *itemList
|
||||||
err error
|
err error
|
||||||
|
|
||||||
|
// transportResponseFrames counts the number of queued items that represent
|
||||||
|
// the response of an action initiated by the peer. trfChan is created
|
||||||
|
// when transportResponseFrames >= maxQueuedTransportResponseFrames and is
|
||||||
|
// closed and nilled when transportResponseFrames drops below the
|
||||||
|
// threshold. Both fields are protected by mu.
|
||||||
|
transportResponseFrames int
|
||||||
|
trfChan atomic.Value // *chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newControlBuffer(done <-chan struct{}) *controlBuffer {
|
func newControlBuffer(done <-chan struct{}) *controlBuffer {
|
||||||
|
@ -248,12 +295,24 @@ func newControlBuffer(done <-chan struct{}) *controlBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *controlBuffer) put(it interface{}) error {
|
// throttle blocks if there are too many incomingSettings/cleanupStreams in the
|
||||||
|
// controlbuf.
|
||||||
|
func (c *controlBuffer) throttle() {
|
||||||
|
ch, _ := c.trfChan.Load().(*chan struct{})
|
||||||
|
if ch != nil {
|
||||||
|
select {
|
||||||
|
case <-*ch:
|
||||||
|
case <-c.done:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *controlBuffer) put(it cbItem) error {
|
||||||
_, err := c.executeAndPut(nil, it)
|
_, err := c.executeAndPut(nil, it)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *controlBuffer) executeAndPut(f func(it interface{}) bool, it interface{}) (bool, error) {
|
func (c *controlBuffer) executeAndPut(f func(it interface{}) bool, it cbItem) (bool, error) {
|
||||||
var wakeUp bool
|
var wakeUp bool
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
if c.err != nil {
|
if c.err != nil {
|
||||||
|
@ -271,6 +330,15 @@ func (c *controlBuffer) executeAndPut(f func(it interface{}) bool, it interface{
|
||||||
c.consumerWaiting = false
|
c.consumerWaiting = false
|
||||||
}
|
}
|
||||||
c.list.enqueue(it)
|
c.list.enqueue(it)
|
||||||
|
if it.isTransportResponseFrame() {
|
||||||
|
c.transportResponseFrames++
|
||||||
|
if c.transportResponseFrames == maxQueuedTransportResponseFrames {
|
||||||
|
// We are adding the frame that puts us over the threshold; create
|
||||||
|
// a throttling channel.
|
||||||
|
ch := make(chan struct{})
|
||||||
|
c.trfChan.Store(&ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
if wakeUp {
|
if wakeUp {
|
||||||
select {
|
select {
|
||||||
|
@ -304,7 +372,17 @@ func (c *controlBuffer) get(block bool) (interface{}, error) {
|
||||||
return nil, c.err
|
return nil, c.err
|
||||||
}
|
}
|
||||||
if !c.list.isEmpty() {
|
if !c.list.isEmpty() {
|
||||||
h := c.list.dequeue()
|
h := c.list.dequeue().(cbItem)
|
||||||
|
if h.isTransportResponseFrame() {
|
||||||
|
if c.transportResponseFrames == maxQueuedTransportResponseFrames {
|
||||||
|
// We are removing the frame that put us over the
|
||||||
|
// threshold; close and clear the throttling channel.
|
||||||
|
ch := c.trfChan.Load().(*chan struct{})
|
||||||
|
close(*ch)
|
||||||
|
c.trfChan.Store((*chan struct{})(nil))
|
||||||
|
}
|
||||||
|
c.transportResponseFrames--
|
||||||
|
}
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
@ -559,21 +637,17 @@ func (l *loopyWriter) headerHandler(h *headerFrame) error {
|
||||||
|
|
||||||
func (l *loopyWriter) originateStream(str *outStream) error {
|
func (l *loopyWriter) originateStream(str *outStream) error {
|
||||||
hdr := str.itl.dequeue().(*headerFrame)
|
hdr := str.itl.dequeue().(*headerFrame)
|
||||||
sendPing, err := hdr.initStream(str.id)
|
if err := hdr.initStream(str.id); err != nil {
|
||||||
if err != nil {
|
|
||||||
if err == ErrConnClosing {
|
if err == ErrConnClosing {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Other errors(errStreamDrain) need not close transport.
|
// Other errors(errStreamDrain) need not close transport.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err = l.writeHeader(str.id, hdr.endStream, hdr.hf, hdr.onWrite); err != nil {
|
if err := l.writeHeader(str.id, hdr.endStream, hdr.hf, hdr.onWrite); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
l.estdStreams[str.id] = str
|
l.estdStreams[str.id] = str
|
||||||
if sendPing {
|
|
||||||
return l.pingHandler(&ping{data: [8]byte{}})
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -149,6 +149,7 @@ func (f *inFlow) maybeAdjust(n uint32) uint32 {
|
||||||
n = uint32(math.MaxInt32)
|
n = uint32(math.MaxInt32)
|
||||||
}
|
}
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
|
defer f.mu.Unlock()
|
||||||
// estSenderQuota is the receiver's view of the maximum number of bytes the sender
|
// estSenderQuota is the receiver's view of the maximum number of bytes the sender
|
||||||
// can send without a window update.
|
// can send without a window update.
|
||||||
estSenderQuota := int32(f.limit - (f.pendingData + f.pendingUpdate))
|
estSenderQuota := int32(f.limit - (f.pendingData + f.pendingUpdate))
|
||||||
|
@ -169,10 +170,8 @@ func (f *inFlow) maybeAdjust(n uint32) uint32 {
|
||||||
// is padded; We will fallback on the current available window(at least a 1/4th of the limit).
|
// is padded; We will fallback on the current available window(at least a 1/4th of the limit).
|
||||||
f.delta = n
|
f.delta = n
|
||||||
}
|
}
|
||||||
f.mu.Unlock()
|
|
||||||
return f.delta
|
return f.delta
|
||||||
}
|
}
|
||||||
f.mu.Unlock()
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
package transport
|
package transport
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -226,7 +227,9 @@ func (ht *serverHandlerTransport) WriteStatus(s *Stream, st *status.Status) erro
|
||||||
|
|
||||||
if err == nil { // transport has not been closed
|
if err == nil { // transport has not been closed
|
||||||
if ht.stats != nil {
|
if ht.stats != nil {
|
||||||
ht.stats.HandleRPC(s.Context(), &stats.OutTrailer{})
|
ht.stats.HandleRPC(s.Context(), &stats.OutTrailer{
|
||||||
|
Trailer: s.trailer.Copy(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ht.Close()
|
ht.Close()
|
||||||
|
@ -288,7 +291,9 @@ func (ht *serverHandlerTransport) WriteHeader(s *Stream, md metadata.MD) error {
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if ht.stats != nil {
|
if ht.stats != nil {
|
||||||
ht.stats.HandleRPC(s.Context(), &stats.OutHeader{})
|
ht.stats.HandleRPC(s.Context(), &stats.OutHeader{
|
||||||
|
Header: md.Copy(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
@ -333,7 +338,7 @@ func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream), trace
|
||||||
Addr: ht.RemoteAddr(),
|
Addr: ht.RemoteAddr(),
|
||||||
}
|
}
|
||||||
if req.TLS != nil {
|
if req.TLS != nil {
|
||||||
pr.AuthInfo = credentials.TLSInfo{State: *req.TLS}
|
pr.AuthInfo = credentials.TLSInfo{State: *req.TLS, CommonAuthInfo: credentials.CommonAuthInfo{credentials.PrivacyAndIntegrity}}
|
||||||
}
|
}
|
||||||
ctx = metadata.NewIncomingContext(ctx, ht.headerMD)
|
ctx = metadata.NewIncomingContext(ctx, ht.headerMD)
|
||||||
s.ctx = peer.NewContext(ctx, pr)
|
s.ctx = peer.NewContext(ctx, pr)
|
||||||
|
@ -347,7 +352,7 @@ func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream), trace
|
||||||
ht.stats.HandleRPC(s.ctx, inHeader)
|
ht.stats.HandleRPC(s.ctx, inHeader)
|
||||||
}
|
}
|
||||||
s.trReader = &transportReader{
|
s.trReader = &transportReader{
|
||||||
reader: &recvBufferReader{ctx: s.ctx, ctxDone: s.ctx.Done(), recv: s.buf},
|
reader: &recvBufferReader{ctx: s.ctx, ctxDone: s.ctx.Done(), recv: s.buf, freeBuffer: func(*bytes.Buffer) {}},
|
||||||
windowHandler: func(int) {},
|
windowHandler: func(int) {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,7 +366,7 @@ func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream), trace
|
||||||
for buf := make([]byte, readSize); ; {
|
for buf := make([]byte, readSize); ; {
|
||||||
n, err := req.Body.Read(buf)
|
n, err := req.Body.Read(buf)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
s.buf.put(recvMsg{data: buf[:n:n]})
|
s.buf.put(recvMsg{buffer: bytes.NewBuffer(buf[:n:n])})
|
||||||
buf = buf[n:]
|
buf = buf[n:]
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -35,6 +35,7 @@ import (
|
||||||
|
|
||||||
"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"
|
||||||
"google.golang.org/grpc/internal/syscall"
|
"google.golang.org/grpc/internal/syscall"
|
||||||
"google.golang.org/grpc/keepalive"
|
"google.golang.org/grpc/keepalive"
|
||||||
|
@ -44,8 +45,14 @@ import (
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// clientConnectionCounter counts the number of connections a client has
|
||||||
|
// initiated (equal to the number of http2Clients created). Must be accessed
|
||||||
|
// atomically.
|
||||||
|
var clientConnectionCounter uint64
|
||||||
|
|
||||||
// http2Client implements the ClientTransport interface with HTTP2.
|
// http2Client implements the ClientTransport interface with HTTP2.
|
||||||
type http2Client struct {
|
type http2Client struct {
|
||||||
|
lastRead int64 // Keep this field 64-bit aligned. Accessed atomically.
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
ctxDone <-chan struct{} // Cache the ctx.Done() chan.
|
ctxDone <-chan struct{} // Cache the ctx.Done() chan.
|
||||||
|
@ -62,8 +69,6 @@ type http2Client struct {
|
||||||
// goAway is closed to notify the upper layer (i.e., addrConn.transportMonitor)
|
// goAway is closed to notify the upper layer (i.e., addrConn.transportMonitor)
|
||||||
// that the server sent GoAway on this transport.
|
// that the server sent GoAway on this transport.
|
||||||
goAway chan struct{}
|
goAway chan struct{}
|
||||||
// awakenKeepalive is used to wake up keepalive when after it has gone dormant.
|
|
||||||
awakenKeepalive chan struct{}
|
|
||||||
|
|
||||||
framer *framer
|
framer *framer
|
||||||
// controlBuf delivers all the control related tasks (e.g., window
|
// controlBuf delivers all the control related tasks (e.g., window
|
||||||
|
@ -77,9 +82,6 @@ type http2Client struct {
|
||||||
|
|
||||||
perRPCCreds []credentials.PerRPCCredentials
|
perRPCCreds []credentials.PerRPCCredentials
|
||||||
|
|
||||||
// Boolean to keep track of reading activity on transport.
|
|
||||||
// 1 is true and 0 is false.
|
|
||||||
activity uint32 // Accessed atomically.
|
|
||||||
kp keepalive.ClientParameters
|
kp keepalive.ClientParameters
|
||||||
keepaliveEnabled bool
|
keepaliveEnabled bool
|
||||||
|
|
||||||
|
@ -110,6 +112,16 @@ type http2Client struct {
|
||||||
// goAwayReason records the http2.ErrCode and debug data received with the
|
// goAwayReason records the http2.ErrCode and debug data received with the
|
||||||
// GoAway frame.
|
// GoAway frame.
|
||||||
goAwayReason GoAwayReason
|
goAwayReason GoAwayReason
|
||||||
|
// A condition variable used to signal when the keepalive goroutine should
|
||||||
|
// go dormant. The condition for dormancy is based on the number of active
|
||||||
|
// streams and the `PermitWithoutStream` keepalive client parameter. And
|
||||||
|
// since the number of active streams is guarded by the above mutex, we use
|
||||||
|
// the same for this condition variable as well.
|
||||||
|
kpDormancyCond *sync.Cond
|
||||||
|
// A boolean to track whether the keepalive goroutine is dormant or not.
|
||||||
|
// This is checked before attempting to signal the above condition
|
||||||
|
// variable.
|
||||||
|
kpDormant bool
|
||||||
|
|
||||||
// Fields below are for channelz metric collection.
|
// Fields below are for channelz metric collection.
|
||||||
channelzID int64 // channelz unique identification number
|
channelzID int64 // channelz unique identification number
|
||||||
|
@ -117,6 +129,10 @@ type http2Client struct {
|
||||||
|
|
||||||
onGoAway func(GoAwayReason)
|
onGoAway func(GoAwayReason)
|
||||||
onClose func()
|
onClose func()
|
||||||
|
|
||||||
|
bufferPool *bufferPool
|
||||||
|
|
||||||
|
connectionID uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func dial(ctx context.Context, fn func(context.Context, string) (net.Conn, error), addr string) (net.Conn, error) {
|
func dial(ctx context.Context, fn func(context.Context, string) (net.Conn, error), addr string) (net.Conn, error) {
|
||||||
|
@ -230,7 +246,6 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts Conne
|
||||||
readerDone: make(chan struct{}),
|
readerDone: make(chan struct{}),
|
||||||
writerDone: make(chan struct{}),
|
writerDone: make(chan struct{}),
|
||||||
goAway: make(chan struct{}),
|
goAway: make(chan struct{}),
|
||||||
awakenKeepalive: make(chan struct{}, 1),
|
|
||||||
framer: newFramer(conn, writeBufSize, readBufSize, maxHeaderListSize),
|
framer: newFramer(conn, writeBufSize, readBufSize, maxHeaderListSize),
|
||||||
fc: &trInFlow{limit: uint32(icwz)},
|
fc: &trInFlow{limit: uint32(icwz)},
|
||||||
scheme: scheme,
|
scheme: scheme,
|
||||||
|
@ -249,6 +264,7 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts Conne
|
||||||
onGoAway: onGoAway,
|
onGoAway: onGoAway,
|
||||||
onClose: onClose,
|
onClose: onClose,
|
||||||
keepaliveEnabled: keepaliveEnabled,
|
keepaliveEnabled: keepaliveEnabled,
|
||||||
|
bufferPool: newBufferPool(),
|
||||||
}
|
}
|
||||||
t.controlBuf = newControlBuffer(t.ctxDone)
|
t.controlBuf = newControlBuffer(t.ctxDone)
|
||||||
if opts.InitialWindowSize >= defaultWindowSize {
|
if opts.InitialWindowSize >= defaultWindowSize {
|
||||||
|
@ -261,9 +277,6 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts Conne
|
||||||
updateFlowControl: t.updateFlowControl,
|
updateFlowControl: t.updateFlowControl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Make sure awakenKeepalive can't be written upon.
|
|
||||||
// keepalive routine will make it writable, if need be.
|
|
||||||
t.awakenKeepalive <- struct{}{}
|
|
||||||
if t.statsHandler != nil {
|
if t.statsHandler != nil {
|
||||||
t.ctx = t.statsHandler.TagConn(t.ctx, &stats.ConnTagInfo{
|
t.ctx = t.statsHandler.TagConn(t.ctx, &stats.ConnTagInfo{
|
||||||
RemoteAddr: t.remoteAddr,
|
RemoteAddr: t.remoteAddr,
|
||||||
|
@ -278,6 +291,7 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts Conne
|
||||||
t.channelzID = channelz.RegisterNormalSocket(t, opts.ChannelzParentID, fmt.Sprintf("%s -> %s", t.localAddr, t.remoteAddr))
|
t.channelzID = channelz.RegisterNormalSocket(t, opts.ChannelzParentID, fmt.Sprintf("%s -> %s", t.localAddr, t.remoteAddr))
|
||||||
}
|
}
|
||||||
if t.keepaliveEnabled {
|
if t.keepaliveEnabled {
|
||||||
|
t.kpDormancyCond = sync.NewCond(&t.mu)
|
||||||
go t.keepalive()
|
go t.keepalive()
|
||||||
}
|
}
|
||||||
// Start the reader goroutine for incoming message. Each transport has
|
// Start the reader goroutine for incoming message. Each transport has
|
||||||
|
@ -322,6 +336,8 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts Conne
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.connectionID = atomic.AddUint64(&clientConnectionCounter, 1)
|
||||||
|
|
||||||
if err := t.framer.writer.Flush(); err != nil {
|
if err := t.framer.writer.Flush(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -344,6 +360,7 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts Conne
|
||||||
func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream {
|
func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream {
|
||||||
// TODO(zhaoq): Handle uint32 overflow of Stream.id.
|
// TODO(zhaoq): Handle uint32 overflow of Stream.id.
|
||||||
s := &Stream{
|
s := &Stream{
|
||||||
|
ct: t,
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
method: callHdr.Method,
|
method: callHdr.Method,
|
||||||
sendCompress: callHdr.SendCompress,
|
sendCompress: callHdr.SendCompress,
|
||||||
|
@ -367,6 +384,7 @@ func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream {
|
||||||
closeStream: func(err error) {
|
closeStream: func(err error) {
|
||||||
t.CloseStream(s, err)
|
t.CloseStream(s, err)
|
||||||
},
|
},
|
||||||
|
freeBuffer: t.bufferPool.put,
|
||||||
},
|
},
|
||||||
windowHandler: func(n int) {
|
windowHandler: func(n int) {
|
||||||
t.updateWindow(s, uint32(n))
|
t.updateWindow(s, uint32(n))
|
||||||
|
@ -376,23 +394,24 @@ func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *http2Client) getPeer() *peer.Peer {
|
func (t *http2Client) getPeer() *peer.Peer {
|
||||||
pr := &peer.Peer{
|
return &peer.Peer{
|
||||||
Addr: t.remoteAddr,
|
Addr: t.remoteAddr,
|
||||||
|
AuthInfo: t.authInfo,
|
||||||
}
|
}
|
||||||
// Attach Auth info if there is any.
|
|
||||||
if t.authInfo != nil {
|
|
||||||
pr.AuthInfo = t.authInfo
|
|
||||||
}
|
|
||||||
return pr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr) ([]hpack.HeaderField, error) {
|
func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr) ([]hpack.HeaderField, error) {
|
||||||
aud := t.createAudience(callHdr)
|
aud := t.createAudience(callHdr)
|
||||||
authData, err := t.getTrAuthData(ctx, aud)
|
ri := credentials.RequestInfo{
|
||||||
|
Method: callHdr.Method,
|
||||||
|
AuthInfo: t.authInfo,
|
||||||
|
}
|
||||||
|
ctxWithRequestInfo := internal.NewRequestInfoContext.(func(context.Context, credentials.RequestInfo) context.Context)(ctx, ri)
|
||||||
|
authData, err := t.getTrAuthData(ctxWithRequestInfo, aud)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
callAuthData, err := t.getCallAuthData(ctx, aud, callHdr)
|
callAuthData, err := t.getCallAuthData(ctxWithRequestInfo, aud, callHdr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -415,6 +434,7 @@ func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr)
|
||||||
|
|
||||||
if callHdr.SendCompress != "" {
|
if callHdr.SendCompress != "" {
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-encoding", Value: callHdr.SendCompress})
|
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-encoding", Value: callHdr.SendCompress})
|
||||||
|
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-accept-encoding", Value: callHdr.SendCompress})
|
||||||
}
|
}
|
||||||
if dl, ok := ctx.Deadline(); ok {
|
if dl, ok := ctx.Deadline(); ok {
|
||||||
// Send out timeout regardless its value. The server can detect timeout context by itself.
|
// Send out timeout regardless its value. The server can detect timeout context by itself.
|
||||||
|
@ -437,6 +457,15 @@ func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr)
|
||||||
|
|
||||||
if md, added, ok := metadata.FromOutgoingContextRaw(ctx); ok {
|
if md, added, ok := metadata.FromOutgoingContextRaw(ctx); ok {
|
||||||
var k string
|
var k string
|
||||||
|
for k, vv := range md {
|
||||||
|
// HTTP doesn't allow you to set pseudoheaders after non pseudoheaders were set.
|
||||||
|
if isReservedHeader(k) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, v := range vv {
|
||||||
|
headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)})
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, vv := range added {
|
for _, vv := range added {
|
||||||
for i, v := range vv {
|
for i, v := range vv {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
|
@ -450,15 +479,6 @@ func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr)
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: strings.ToLower(k), Value: encodeMetadataHeader(k, v)})
|
headerFields = append(headerFields, hpack.HeaderField{Name: strings.ToLower(k), Value: encodeMetadataHeader(k, v)})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for k, vv := range md {
|
|
||||||
// HTTP doesn't allow you to set pseudoheaders after non pseudoheaders were set.
|
|
||||||
if isReservedHeader(k) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, v := range vv {
|
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if md, ok := t.md.(*metadata.MD); ok {
|
if md, ok := t.md.(*metadata.MD); ok {
|
||||||
for k, vv := range *md {
|
for k, vv := range *md {
|
||||||
|
@ -489,6 +509,9 @@ func (t *http2Client) createAudience(callHdr *CallHdr) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *http2Client) getTrAuthData(ctx context.Context, audience string) (map[string]string, error) {
|
func (t *http2Client) getTrAuthData(ctx context.Context, audience string) (map[string]string, error) {
|
||||||
|
if len(t.perRPCCreds) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
authData := map[string]string{}
|
authData := map[string]string{}
|
||||||
for _, c := range t.perRPCCreds {
|
for _, c := range t.perRPCCreds {
|
||||||
data, err := c.GetRequestMetadata(ctx, audience)
|
data, err := c.GetRequestMetadata(ctx, audience)
|
||||||
|
@ -509,7 +532,7 @@ func (t *http2Client) getTrAuthData(ctx context.Context, audience string) (map[s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *http2Client) getCallAuthData(ctx context.Context, audience string, callHdr *CallHdr) (map[string]string, error) {
|
func (t *http2Client) getCallAuthData(ctx context.Context, audience string, callHdr *CallHdr) (map[string]string, error) {
|
||||||
callAuthData := map[string]string{}
|
var callAuthData map[string]string
|
||||||
// Check if credentials.PerRPCCredentials were provided via call options.
|
// Check if credentials.PerRPCCredentials were provided via call options.
|
||||||
// Note: if these credentials are provided both via dial options and call
|
// Note: if these credentials are provided both via dial options and call
|
||||||
// options, then both sets of credentials will be applied.
|
// options, then both sets of credentials will be applied.
|
||||||
|
@ -521,6 +544,7 @@ func (t *http2Client) getCallAuthData(ctx context.Context, audience string, call
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "transport: %v", err)
|
return nil, status.Errorf(codes.Internal, "transport: %v", err)
|
||||||
}
|
}
|
||||||
|
callAuthData = make(map[string]string, len(data))
|
||||||
for k, v := range data {
|
for k, v := range data {
|
||||||
// Capital header names are illegal in HTTP/2
|
// Capital header names are illegal in HTTP/2
|
||||||
k = strings.ToLower(k)
|
k = strings.ToLower(k)
|
||||||
|
@ -552,12 +576,11 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
|
||||||
if atomic.CompareAndSwapUint32(&s.headerChanClosed, 0, 1) {
|
if atomic.CompareAndSwapUint32(&s.headerChanClosed, 0, 1) {
|
||||||
close(s.headerChan)
|
close(s.headerChan)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
hdr := &headerFrame{
|
hdr := &headerFrame{
|
||||||
hf: headerFields,
|
hf: headerFields,
|
||||||
endStream: false,
|
endStream: false,
|
||||||
initStream: func(id uint32) (bool, error) {
|
initStream: func(id uint32) error {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
if state := t.state; state != reachable {
|
if state := t.state; state != reachable {
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
|
@ -567,29 +590,19 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
|
||||||
err = ErrConnClosing
|
err = ErrConnClosing
|
||||||
}
|
}
|
||||||
cleanup(err)
|
cleanup(err)
|
||||||
return false, err
|
return err
|
||||||
}
|
}
|
||||||
t.activeStreams[id] = s
|
t.activeStreams[id] = s
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
atomic.AddInt64(&t.czData.streamsStarted, 1)
|
atomic.AddInt64(&t.czData.streamsStarted, 1)
|
||||||
atomic.StoreInt64(&t.czData.lastStreamCreatedTime, time.Now().UnixNano())
|
atomic.StoreInt64(&t.czData.lastStreamCreatedTime, time.Now().UnixNano())
|
||||||
}
|
}
|
||||||
var sendPing bool
|
// If the keepalive goroutine has gone dormant, wake it up.
|
||||||
// If the number of active streams change from 0 to 1, then check if keepalive
|
if t.kpDormant {
|
||||||
// has gone dormant. If so, wake it up.
|
t.kpDormancyCond.Signal()
|
||||||
if len(t.activeStreams) == 1 && t.keepaliveEnabled {
|
|
||||||
select {
|
|
||||||
case t.awakenKeepalive <- struct{}{}:
|
|
||||||
sendPing = true
|
|
||||||
// Fill the awakenKeepalive channel again as this channel must be
|
|
||||||
// kept non-writable except at the point that the keepalive()
|
|
||||||
// goroutine is waiting either to be awaken or shutdown.
|
|
||||||
t.awakenKeepalive <- struct{}{}
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
return sendPing, nil
|
return nil
|
||||||
},
|
},
|
||||||
onOrphaned: cleanup,
|
onOrphaned: cleanup,
|
||||||
wq: s.wq,
|
wq: s.wq,
|
||||||
|
@ -667,12 +680,14 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if t.statsHandler != nil {
|
if t.statsHandler != nil {
|
||||||
|
header, _, _ := metadata.FromOutgoingContextRaw(ctx)
|
||||||
outHeader := &stats.OutHeader{
|
outHeader := &stats.OutHeader{
|
||||||
Client: true,
|
Client: true,
|
||||||
FullMethod: callHdr.Method,
|
FullMethod: callHdr.Method,
|
||||||
RemoteAddr: t.remoteAddr,
|
RemoteAddr: t.remoteAddr,
|
||||||
LocalAddr: t.localAddr,
|
LocalAddr: t.localAddr,
|
||||||
Compression: callHdr.SendCompress,
|
Compression: callHdr.SendCompress,
|
||||||
|
Header: header.Copy(),
|
||||||
}
|
}
|
||||||
t.statsHandler.HandleRPC(s.ctx, outHeader)
|
t.statsHandler.HandleRPC(s.ctx, outHeader)
|
||||||
}
|
}
|
||||||
|
@ -765,9 +780,17 @@ func (t *http2Client) Close() error {
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
// Call t.onClose before setting the state to closing to prevent the client
|
||||||
|
// from attempting to create new streams ASAP.
|
||||||
|
t.onClose()
|
||||||
t.state = closing
|
t.state = closing
|
||||||
streams := t.activeStreams
|
streams := t.activeStreams
|
||||||
t.activeStreams = nil
|
t.activeStreams = nil
|
||||||
|
if t.kpDormant {
|
||||||
|
// If the keepalive goroutine is blocked on this condition variable, we
|
||||||
|
// should unblock it so that the goroutine eventually exits.
|
||||||
|
t.kpDormancyCond.Signal()
|
||||||
|
}
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
t.controlBuf.finish()
|
t.controlBuf.finish()
|
||||||
t.cancel()
|
t.cancel()
|
||||||
|
@ -785,7 +808,6 @@ func (t *http2Client) Close() error {
|
||||||
}
|
}
|
||||||
t.statsHandler.HandleConn(t.ctx, connEnd)
|
t.statsHandler.HandleConn(t.ctx, connEnd)
|
||||||
}
|
}
|
||||||
t.onClose()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -844,11 +866,11 @@ func (t *http2Client) Write(s *Stream, hdr []byte, data []byte, opts *Options) e
|
||||||
return t.controlBuf.put(df)
|
return t.controlBuf.put(df)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *http2Client) getStream(f http2.Frame) (*Stream, bool) {
|
func (t *http2Client) getStream(f http2.Frame) *Stream {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
s := t.activeStreams[f.Header().StreamID]
|
||||||
s, ok := t.activeStreams[f.Header().StreamID]
|
t.mu.Unlock()
|
||||||
return s, ok
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// adjustWindow sends out extra window update over the initial window size
|
// adjustWindow sends out extra window update over the initial window size
|
||||||
|
@ -928,8 +950,8 @@ func (t *http2Client) handleData(f *http2.DataFrame) {
|
||||||
t.controlBuf.put(bdpPing)
|
t.controlBuf.put(bdpPing)
|
||||||
}
|
}
|
||||||
// Select the right stream to dispatch.
|
// Select the right stream to dispatch.
|
||||||
s, ok := t.getStream(f)
|
s := t.getStream(f)
|
||||||
if !ok {
|
if s == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if size > 0 {
|
if size > 0 {
|
||||||
|
@ -946,9 +968,10 @@ func (t *http2Client) handleData(f *http2.DataFrame) {
|
||||||
// guarantee f.Data() is consumed before the arrival of next frame.
|
// guarantee f.Data() is consumed before the arrival of next frame.
|
||||||
// Can this copy be eliminated?
|
// Can this copy be eliminated?
|
||||||
if len(f.Data()) > 0 {
|
if len(f.Data()) > 0 {
|
||||||
data := make([]byte, len(f.Data()))
|
buffer := t.bufferPool.get()
|
||||||
copy(data, f.Data())
|
buffer.Reset()
|
||||||
s.write(recvMsg{data: data})
|
buffer.Write(f.Data())
|
||||||
|
s.write(recvMsg{buffer: buffer})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The server has closed the stream without sending trailers. Record that
|
// The server has closed the stream without sending trailers. Record that
|
||||||
|
@ -959,8 +982,8 @@ func (t *http2Client) handleData(f *http2.DataFrame) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *http2Client) handleRSTStream(f *http2.RSTStreamFrame) {
|
func (t *http2Client) handleRSTStream(f *http2.RSTStreamFrame) {
|
||||||
s, ok := t.getStream(f)
|
s := t.getStream(f)
|
||||||
if !ok {
|
if s == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if f.ErrCode == http2.ErrCodeRefusedStream {
|
if f.ErrCode == http2.ErrCodeRefusedStream {
|
||||||
|
@ -973,9 +996,9 @@ func (t *http2Client) handleRSTStream(f *http2.RSTStreamFrame) {
|
||||||
statusCode = codes.Unknown
|
statusCode = codes.Unknown
|
||||||
}
|
}
|
||||||
if statusCode == codes.Canceled {
|
if statusCode == codes.Canceled {
|
||||||
// Our deadline was already exceeded, and that was likely the cause of
|
if d, ok := s.ctx.Deadline(); ok && !d.After(time.Now()) {
|
||||||
// this cancelation. Alter the status code accordingly.
|
// Our deadline was already exceeded, and that was likely the cause
|
||||||
if d, ok := s.ctx.Deadline(); ok && d.After(time.Now()) {
|
// of this cancelation. Alter the status code accordingly.
|
||||||
statusCode = codes.DeadlineExceeded
|
statusCode = codes.DeadlineExceeded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1080,11 +1103,12 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
|
||||||
default:
|
default:
|
||||||
t.setGoAwayReason(f)
|
t.setGoAwayReason(f)
|
||||||
close(t.goAway)
|
close(t.goAway)
|
||||||
t.state = draining
|
|
||||||
t.controlBuf.put(&incomingGoAway{})
|
t.controlBuf.put(&incomingGoAway{})
|
||||||
|
// Notify the clientconn about the GOAWAY before we set the state to
|
||||||
// This has to be a new goroutine because we're still using the current goroutine to read in the transport.
|
// draining, to allow the client to stop attempting to create streams
|
||||||
|
// before disallowing new streams on this connection.
|
||||||
t.onGoAway(t.goAwayReason)
|
t.onGoAway(t.goAwayReason)
|
||||||
|
t.state = draining
|
||||||
}
|
}
|
||||||
// All streams with IDs greater than the GoAwayId
|
// All streams with IDs greater than the GoAwayId
|
||||||
// and smaller than the previous GoAway ID should be killed.
|
// and smaller than the previous GoAway ID should be killed.
|
||||||
|
@ -1136,8 +1160,8 @@ func (t *http2Client) handleWindowUpdate(f *http2.WindowUpdateFrame) {
|
||||||
|
|
||||||
// operateHeaders takes action on the decoded headers.
|
// operateHeaders takes action on the decoded headers.
|
||||||
func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
||||||
s, ok := t.getStream(frame)
|
s := t.getStream(frame)
|
||||||
if !ok {
|
if s == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
endStream := frame.StreamEnded()
|
endStream := frame.StreamEnded()
|
||||||
|
@ -1166,12 +1190,14 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
||||||
inHeader := &stats.InHeader{
|
inHeader := &stats.InHeader{
|
||||||
Client: true,
|
Client: true,
|
||||||
WireLength: int(frame.Header().Length),
|
WireLength: int(frame.Header().Length),
|
||||||
|
Header: s.header.Copy(),
|
||||||
}
|
}
|
||||||
t.statsHandler.HandleRPC(s.ctx, inHeader)
|
t.statsHandler.HandleRPC(s.ctx, inHeader)
|
||||||
} else {
|
} else {
|
||||||
inTrailer := &stats.InTrailer{
|
inTrailer := &stats.InTrailer{
|
||||||
Client: true,
|
Client: true,
|
||||||
WireLength: int(frame.Header().Length),
|
WireLength: int(frame.Header().Length),
|
||||||
|
Trailer: s.trailer.Copy(),
|
||||||
}
|
}
|
||||||
t.statsHandler.HandleRPC(s.ctx, inTrailer)
|
t.statsHandler.HandleRPC(s.ctx, inTrailer)
|
||||||
}
|
}
|
||||||
|
@ -1180,6 +1206,7 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
||||||
|
|
||||||
// If headerChan hasn't been closed yet
|
// If headerChan hasn't been closed yet
|
||||||
if atomic.CompareAndSwapUint32(&s.headerChanClosed, 0, 1) {
|
if atomic.CompareAndSwapUint32(&s.headerChanClosed, 0, 1) {
|
||||||
|
s.headerValid = true
|
||||||
if !endStream {
|
if !endStream {
|
||||||
// HEADERS frame block carries a Response-Headers.
|
// HEADERS frame block carries a Response-Headers.
|
||||||
isHeader = true
|
isHeader = true
|
||||||
|
@ -1222,7 +1249,7 @@ func (t *http2Client) reader() {
|
||||||
}
|
}
|
||||||
t.conn.SetReadDeadline(time.Time{}) // reset deadline once we get the settings frame (we didn't time out, yay!)
|
t.conn.SetReadDeadline(time.Time{}) // reset deadline once we get the settings frame (we didn't time out, yay!)
|
||||||
if t.keepaliveEnabled {
|
if t.keepaliveEnabled {
|
||||||
atomic.CompareAndSwapUint32(&t.activity, 0, 1)
|
atomic.StoreInt64(&t.lastRead, time.Now().UnixNano())
|
||||||
}
|
}
|
||||||
sf, ok := frame.(*http2.SettingsFrame)
|
sf, ok := frame.(*http2.SettingsFrame)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -1234,9 +1261,10 @@ func (t *http2Client) reader() {
|
||||||
|
|
||||||
// loop to keep reading incoming messages on this transport.
|
// loop to keep reading incoming messages on this transport.
|
||||||
for {
|
for {
|
||||||
|
t.controlBuf.throttle()
|
||||||
frame, err := t.framer.fr.ReadFrame()
|
frame, err := t.framer.fr.ReadFrame()
|
||||||
if t.keepaliveEnabled {
|
if t.keepaliveEnabled {
|
||||||
atomic.CompareAndSwapUint32(&t.activity, 0, 1)
|
atomic.StoreInt64(&t.lastRead, time.Now().UnixNano())
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Abort an active stream if the http2.Framer returns a
|
// Abort an active stream if the http2.Framer returns a
|
||||||
|
@ -1280,55 +1308,83 @@ func (t *http2Client) reader() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func minTime(a, b time.Duration) time.Duration {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
// keepalive running in a separate goroutune makes sure the connection is alive by sending pings.
|
// keepalive running in a separate goroutune makes sure the connection is alive by sending pings.
|
||||||
func (t *http2Client) keepalive() {
|
func (t *http2Client) keepalive() {
|
||||||
p := &ping{data: [8]byte{}}
|
p := &ping{data: [8]byte{}}
|
||||||
|
// True iff a ping has been sent, and no data has been received since then.
|
||||||
|
outstandingPing := false
|
||||||
|
// Amount of time remaining before which we should receive an ACK for the
|
||||||
|
// last sent ping.
|
||||||
|
timeoutLeft := time.Duration(0)
|
||||||
|
// Records the last value of t.lastRead before we go block on the timer.
|
||||||
|
// This is required to check for read activity since then.
|
||||||
|
prevNano := time.Now().UnixNano()
|
||||||
timer := time.NewTimer(t.kp.Time)
|
timer := time.NewTimer(t.kp.Time)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
if atomic.CompareAndSwapUint32(&t.activity, 1, 0) {
|
lastRead := atomic.LoadInt64(&t.lastRead)
|
||||||
timer.Reset(t.kp.Time)
|
if lastRead > prevNano {
|
||||||
|
// There has been read activity since the last time we were here.
|
||||||
|
outstandingPing = false
|
||||||
|
// Next timer should fire at kp.Time seconds from lastRead time.
|
||||||
|
timer.Reset(time.Duration(lastRead) + t.kp.Time - time.Duration(time.Now().UnixNano()))
|
||||||
|
prevNano = lastRead
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Check if keepalive should go dormant.
|
if outstandingPing && timeoutLeft <= 0 {
|
||||||
t.mu.Lock()
|
t.Close()
|
||||||
if len(t.activeStreams) < 1 && !t.kp.PermitWithoutStream {
|
|
||||||
// Make awakenKeepalive writable.
|
|
||||||
<-t.awakenKeepalive
|
|
||||||
t.mu.Unlock()
|
|
||||||
select {
|
|
||||||
case <-t.awakenKeepalive:
|
|
||||||
// If the control gets here a ping has been sent
|
|
||||||
// need to reset the timer with keepalive.Timeout.
|
|
||||||
case <-t.ctx.Done():
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
t.mu.Lock()
|
||||||
|
if t.state == closing {
|
||||||
|
// If the transport is closing, we should exit from the
|
||||||
|
// keepalive goroutine here. If not, we could have a race
|
||||||
|
// between the call to Signal() from Close() and the call to
|
||||||
|
// Wait() here, whereby the keepalive goroutine ends up
|
||||||
|
// blocking on the condition variable which will never be
|
||||||
|
// signalled again.
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(t.activeStreams) < 1 && !t.kp.PermitWithoutStream {
|
||||||
|
// If a ping was sent out previously (because there were active
|
||||||
|
// streams at that point) which wasn't acked and its timeout
|
||||||
|
// hadn't fired, but we got here and are about to go dormant,
|
||||||
|
// we should make sure that we unconditionally send a ping once
|
||||||
|
// we awaken.
|
||||||
|
outstandingPing = false
|
||||||
|
t.kpDormant = true
|
||||||
|
t.kpDormancyCond.Wait()
|
||||||
|
}
|
||||||
|
t.kpDormant = false
|
||||||
|
t.mu.Unlock()
|
||||||
|
|
||||||
|
// We get here either because we were dormant and a new stream was
|
||||||
|
// created which unblocked the Wait() call, or because the
|
||||||
|
// keepalive timer expired. In both cases, we need to send a ping.
|
||||||
|
if !outstandingPing {
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
atomic.AddInt64(&t.czData.kpCount, 1)
|
atomic.AddInt64(&t.czData.kpCount, 1)
|
||||||
}
|
}
|
||||||
// Send ping.
|
|
||||||
t.controlBuf.put(p)
|
t.controlBuf.put(p)
|
||||||
|
timeoutLeft = t.kp.Timeout
|
||||||
|
outstandingPing = true
|
||||||
}
|
}
|
||||||
|
// The amount of time to sleep here is the minimum of kp.Time and
|
||||||
// By the time control gets here a ping has been sent one way or the other.
|
// timeoutLeft. This will ensure that we wait only for kp.Time
|
||||||
timer.Reset(t.kp.Timeout)
|
// before sending out the next ping (for cases where the ping is
|
||||||
select {
|
// acked).
|
||||||
case <-timer.C:
|
sleepDuration := minTime(t.kp.Time, timeoutLeft)
|
||||||
if atomic.CompareAndSwapUint32(&t.activity, 1, 0) {
|
timeoutLeft -= sleepDuration
|
||||||
timer.Reset(t.kp.Time)
|
timer.Reset(sleepDuration)
|
||||||
continue
|
|
||||||
}
|
|
||||||
t.Close()
|
|
||||||
return
|
|
||||||
case <-t.ctx.Done():
|
|
||||||
if !timer.Stop() {
|
|
||||||
<-timer.C
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case <-t.ctx.Done():
|
case <-t.ctx.Done():
|
||||||
if !timer.Stop() {
|
if !timer.Stop() {
|
||||||
<-timer.C
|
<-timer.C
|
||||||
|
|
|
@ -35,9 +35,11 @@ import (
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"golang.org/x/net/http2/hpack"
|
"golang.org/x/net/http2/hpack"
|
||||||
|
|
||||||
|
spb "google.golang.org/genproto/googleapis/rpc/status"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
|
"google.golang.org/grpc/internal"
|
||||||
"google.golang.org/grpc/internal/channelz"
|
"google.golang.org/grpc/internal/channelz"
|
||||||
"google.golang.org/grpc/internal/grpcrand"
|
"google.golang.org/grpc/internal/grpcrand"
|
||||||
"google.golang.org/grpc/keepalive"
|
"google.golang.org/grpc/keepalive"
|
||||||
|
@ -55,13 +57,20 @@ var (
|
||||||
// ErrHeaderListSizeLimitViolation indicates that the header list size is larger
|
// ErrHeaderListSizeLimitViolation indicates that the header list size is larger
|
||||||
// than the limit set by peer.
|
// than the limit set by peer.
|
||||||
ErrHeaderListSizeLimitViolation = errors.New("transport: trying to send header list size larger than the limit set by peer")
|
ErrHeaderListSizeLimitViolation = errors.New("transport: trying to send header list size larger than the limit set by peer")
|
||||||
|
// statusRawProto is a function to get to the raw status proto wrapped in a
|
||||||
|
// status.Status without a proto.Clone().
|
||||||
|
statusRawProto = internal.StatusRawProto.(func(*status.Status) *spb.Status)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// serverConnectionCounter counts the number of connections a server has seen
|
||||||
|
// (equal to the number of http2Servers created). Must be accessed atomically.
|
||||||
|
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.
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
ctxDone <-chan struct{} // Cache the context.Done() chan
|
done chan struct{}
|
||||||
cancel context.CancelFunc
|
|
||||||
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.
|
||||||
|
@ -79,12 +88,8 @@ type http2Server struct {
|
||||||
controlBuf *controlBuffer
|
controlBuf *controlBuffer
|
||||||
fc *trInFlow
|
fc *trInFlow
|
||||||
stats stats.Handler
|
stats stats.Handler
|
||||||
// Flag to keep track of reading activity on transport.
|
|
||||||
// 1 is true and 0 is false.
|
|
||||||
activity uint32 // Accessed atomically.
|
|
||||||
// Keepalive and max-age parameters for the server.
|
// Keepalive and max-age parameters for the server.
|
||||||
kp keepalive.ServerParameters
|
kp keepalive.ServerParameters
|
||||||
|
|
||||||
// Keepalive enforcement policy.
|
// Keepalive enforcement policy.
|
||||||
kep keepalive.EnforcementPolicy
|
kep keepalive.EnforcementPolicy
|
||||||
// The time instance last ping was received.
|
// The time instance last ping was received.
|
||||||
|
@ -119,6 +124,9 @@ type http2Server struct {
|
||||||
// Fields below are for channelz metric collection.
|
// Fields below are for channelz metric collection.
|
||||||
channelzID int64 // channelz unique identification number
|
channelzID int64 // channelz unique identification number
|
||||||
czData *channelzData
|
czData *channelzData
|
||||||
|
bufferPool *bufferPool
|
||||||
|
|
||||||
|
connectionID uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is
|
// newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is
|
||||||
|
@ -132,7 +140,10 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
|
||||||
}
|
}
|
||||||
framer := newFramer(conn, writeBufSize, readBufSize, maxHeaderListSize)
|
framer := newFramer(conn, writeBufSize, readBufSize, maxHeaderListSize)
|
||||||
// Send initial settings as connection preface to client.
|
// Send initial settings as connection preface to client.
|
||||||
var isettings []http2.Setting
|
isettings := []http2.Setting{{
|
||||||
|
ID: http2.SettingMaxFrameSize,
|
||||||
|
Val: http2MaxFrameLen,
|
||||||
|
}}
|
||||||
// TODO(zhaoq): Have a better way to signal "no limit" because 0 is
|
// TODO(zhaoq): Have a better way to signal "no limit" because 0 is
|
||||||
// permitted in the HTTP2 spec.
|
// permitted in the HTTP2 spec.
|
||||||
maxStreams := config.MaxStreams
|
maxStreams := config.MaxStreams
|
||||||
|
@ -166,6 +177,12 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
|
||||||
Val: *config.MaxHeaderListSize,
|
Val: *config.MaxHeaderListSize,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if config.HeaderTableSize != nil {
|
||||||
|
isettings = append(isettings, http2.Setting{
|
||||||
|
ID: http2.SettingHeaderTableSize,
|
||||||
|
Val: *config.HeaderTableSize,
|
||||||
|
})
|
||||||
|
}
|
||||||
if err := framer.fr.WriteSettings(isettings...); err != nil {
|
if err := framer.fr.WriteSettings(isettings...); err != nil {
|
||||||
return nil, connectionErrorf(false, err, "transport: %v", err)
|
return nil, connectionErrorf(false, err, "transport: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -197,11 +214,10 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
|
||||||
if kep.MinTime == 0 {
|
if kep.MinTime == 0 {
|
||||||
kep.MinTime = defaultKeepalivePolicyMinTime
|
kep.MinTime = defaultKeepalivePolicyMinTime
|
||||||
}
|
}
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
done := make(chan struct{})
|
||||||
t := &http2Server{
|
t := &http2Server{
|
||||||
ctx: ctx,
|
ctx: context.Background(),
|
||||||
cancel: cancel,
|
done: done,
|
||||||
ctxDone: ctx.Done(),
|
|
||||||
conn: conn,
|
conn: conn,
|
||||||
remoteAddr: conn.RemoteAddr(),
|
remoteAddr: conn.RemoteAddr(),
|
||||||
localAddr: conn.LocalAddr(),
|
localAddr: conn.LocalAddr(),
|
||||||
|
@ -220,8 +236,9 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
|
||||||
kep: kep,
|
kep: kep,
|
||||||
initialWindowSize: iwz,
|
initialWindowSize: iwz,
|
||||||
czData: new(channelzData),
|
czData: new(channelzData),
|
||||||
|
bufferPool: newBufferPool(),
|
||||||
}
|
}
|
||||||
t.controlBuf = newControlBuffer(t.ctxDone)
|
t.controlBuf = newControlBuffer(t.done)
|
||||||
if dynamicWindow {
|
if dynamicWindow {
|
||||||
t.bdpEst = &bdpEstimator{
|
t.bdpEst = &bdpEstimator{
|
||||||
bdp: initialWindowSize,
|
bdp: initialWindowSize,
|
||||||
|
@ -239,6 +256,9 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
t.channelzID = channelz.RegisterNormalSocket(t, config.ChannelzParentID, fmt.Sprintf("%s -> %s", t.remoteAddr, t.localAddr))
|
t.channelzID = channelz.RegisterNormalSocket(t, config.ChannelzParentID, fmt.Sprintf("%s -> %s", t.remoteAddr, t.localAddr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.connectionID = atomic.AddUint64(&serverConnectionCounter, 1)
|
||||||
|
|
||||||
t.framer.writer.Flush()
|
t.framer.writer.Flush()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -263,7 +283,7 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, connectionErrorf(false, err, "transport: http2Server.HandleStreams failed to read initial settings frame: %v", err)
|
return nil, connectionErrorf(false, err, "transport: http2Server.HandleStreams failed to read initial settings frame: %v", err)
|
||||||
}
|
}
|
||||||
atomic.StoreUint32(&t.activity, 1)
|
atomic.StoreInt64(&t.lastRead, time.Now().UnixNano())
|
||||||
sf, ok := frame.(*http2.SettingsFrame)
|
sf, ok := frame.(*http2.SettingsFrame)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, connectionErrorf(false, nil, "transport: http2Server.HandleStreams saw invalid preface type %T from client", frame)
|
return nil, connectionErrorf(false, nil, "transport: http2Server.HandleStreams saw invalid preface type %T from client", frame)
|
||||||
|
@ -352,12 +372,14 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
||||||
rstCode: http2.ErrCodeRefusedStream,
|
rstCode: http2.ErrCodeRefusedStream,
|
||||||
onWrite: func() {},
|
onWrite: func() {},
|
||||||
})
|
})
|
||||||
|
s.cancel()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
if t.state != reachable {
|
if t.state != reachable {
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
|
s.cancel()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if uint32(len(t.activeStreams)) >= t.maxStreams {
|
if uint32(len(t.activeStreams)) >= t.maxStreams {
|
||||||
|
@ -368,12 +390,14 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
||||||
rstCode: http2.ErrCodeRefusedStream,
|
rstCode: http2.ErrCodeRefusedStream,
|
||||||
onWrite: func() {},
|
onWrite: func() {},
|
||||||
})
|
})
|
||||||
|
s.cancel()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if streamID%2 != 1 || streamID <= t.maxStreamID {
|
if streamID%2 != 1 || streamID <= t.maxStreamID {
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
// illegal gRPC stream id.
|
// illegal gRPC stream id.
|
||||||
errorf("transport: http2Server.HandleStreams received an illegal stream id: %v", streamID)
|
errorf("transport: http2Server.HandleStreams received an illegal stream id: %v", streamID)
|
||||||
|
s.cancel()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
t.maxStreamID = streamID
|
t.maxStreamID = streamID
|
||||||
|
@ -398,6 +422,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
||||||
LocalAddr: t.localAddr,
|
LocalAddr: t.localAddr,
|
||||||
Compression: s.recvCompress,
|
Compression: s.recvCompress,
|
||||||
WireLength: int(frame.Header().Length),
|
WireLength: int(frame.Header().Length),
|
||||||
|
Header: metadata.MD(state.data.mdata).Copy(),
|
||||||
}
|
}
|
||||||
t.stats.HandleRPC(s.ctx, inHeader)
|
t.stats.HandleRPC(s.ctx, inHeader)
|
||||||
}
|
}
|
||||||
|
@ -408,6 +433,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
||||||
ctx: s.ctx,
|
ctx: s.ctx,
|
||||||
ctxDone: s.ctxDone,
|
ctxDone: s.ctxDone,
|
||||||
recv: s.buf,
|
recv: s.buf,
|
||||||
|
freeBuffer: t.bufferPool.put,
|
||||||
},
|
},
|
||||||
windowHandler: func(n int) {
|
windowHandler: func(n int) {
|
||||||
t.updateWindow(s, uint32(n))
|
t.updateWindow(s, uint32(n))
|
||||||
|
@ -428,8 +454,9 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
||||||
func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context.Context, string) context.Context) {
|
func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context.Context, string) context.Context) {
|
||||||
defer close(t.readerDone)
|
defer close(t.readerDone)
|
||||||
for {
|
for {
|
||||||
|
t.controlBuf.throttle()
|
||||||
frame, err := t.framer.fr.ReadFrame()
|
frame, err := t.framer.fr.ReadFrame()
|
||||||
atomic.StoreUint32(&t.activity, 1)
|
atomic.StoreInt64(&t.lastRead, time.Now().UnixNano())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if se, ok := err.(http2.StreamError); ok {
|
if se, ok := err.(http2.StreamError); ok {
|
||||||
warningf("transport: http2Server.HandleStreams encountered http2.StreamError: %v", se)
|
warningf("transport: http2Server.HandleStreams encountered http2.StreamError: %v", se)
|
||||||
|
@ -591,9 +618,10 @@ func (t *http2Server) handleData(f *http2.DataFrame) {
|
||||||
// guarantee f.Data() is consumed before the arrival of next frame.
|
// guarantee f.Data() is consumed before the arrival of next frame.
|
||||||
// Can this copy be eliminated?
|
// Can this copy be eliminated?
|
||||||
if len(f.Data()) > 0 {
|
if len(f.Data()) > 0 {
|
||||||
data := make([]byte, len(f.Data()))
|
buffer := t.bufferPool.get()
|
||||||
copy(data, f.Data())
|
buffer.Reset()
|
||||||
s.write(recvMsg{data: data})
|
buffer.Write(f.Data())
|
||||||
|
s.write(recvMsg{buffer: buffer})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if f.Header().Flags.Has(http2.FlagDataEndStream) {
|
if f.Header().Flags.Has(http2.FlagDataEndStream) {
|
||||||
|
@ -736,7 +764,7 @@ func (t *http2Server) checkForHeaderListSize(it interface{}) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteHeader sends the header metedata md back to the client.
|
// WriteHeader sends the header metadata md back to the client.
|
||||||
func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
|
func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
|
||||||
if s.updateHeaderSent() || s.getState() == streamDone {
|
if s.updateHeaderSent() || s.getState() == streamDone {
|
||||||
return ErrIllegalHeaderWrite
|
return ErrIllegalHeaderWrite
|
||||||
|
@ -757,6 +785,10 @@ func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *http2Server) setResetPingStrikes() {
|
||||||
|
atomic.StoreUint32(&t.resetPingStrikes, 1)
|
||||||
|
}
|
||||||
|
|
||||||
func (t *http2Server) writeHeaderLocked(s *Stream) error {
|
func (t *http2Server) writeHeaderLocked(s *Stream) error {
|
||||||
// TODO(mmukhi): Benchmark if the performance gets better if count the metadata and other header fields
|
// TODO(mmukhi): Benchmark if the performance gets better if count the metadata and other header fields
|
||||||
// first and create a slice of that exact size.
|
// first and create a slice of that exact size.
|
||||||
|
@ -771,9 +803,7 @@ func (t *http2Server) writeHeaderLocked(s *Stream) error {
|
||||||
streamID: s.id,
|
streamID: s.id,
|
||||||
hf: headerFields,
|
hf: headerFields,
|
||||||
endStream: false,
|
endStream: false,
|
||||||
onWrite: func() {
|
onWrite: t.setResetPingStrikes,
|
||||||
atomic.StoreUint32(&t.resetPingStrikes, 1)
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
if !success {
|
if !success {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -785,7 +815,9 @@ func (t *http2Server) writeHeaderLocked(s *Stream) error {
|
||||||
if t.stats != nil {
|
if t.stats != nil {
|
||||||
// Note: WireLength is not set in outHeader.
|
// Note: WireLength is not set in outHeader.
|
||||||
// TODO(mmukhi): Revisit this later, if needed.
|
// TODO(mmukhi): Revisit this later, if needed.
|
||||||
outHeader := &stats.OutHeader{}
|
outHeader := &stats.OutHeader{
|
||||||
|
Header: s.header.Copy(),
|
||||||
|
}
|
||||||
t.stats.HandleRPC(s.Context(), outHeader)
|
t.stats.HandleRPC(s.Context(), outHeader)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -817,7 +849,7 @@ func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error {
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-status", Value: strconv.Itoa(int(st.Code()))})
|
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-status", Value: strconv.Itoa(int(st.Code()))})
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-message", Value: encodeGrpcMessage(st.Message())})
|
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-message", Value: encodeGrpcMessage(st.Message())})
|
||||||
|
|
||||||
if p := st.Proto(); p != nil && len(p.Details) > 0 {
|
if p := statusRawProto(st); p != nil && len(p.Details) > 0 {
|
||||||
stBytes, err := proto.Marshal(p)
|
stBytes, err := proto.Marshal(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: return error instead, when callers are able to handle it.
|
// TODO: return error instead, when callers are able to handle it.
|
||||||
|
@ -833,9 +865,7 @@ func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error {
|
||||||
streamID: s.id,
|
streamID: s.id,
|
||||||
hf: headerFields,
|
hf: headerFields,
|
||||||
endStream: true,
|
endStream: true,
|
||||||
onWrite: func() {
|
onWrite: t.setResetPingStrikes,
|
||||||
atomic.StoreUint32(&t.resetPingStrikes, 1)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
s.hdrMu.Unlock()
|
s.hdrMu.Unlock()
|
||||||
success, err := t.controlBuf.execute(t.checkForHeaderListSize, trailingHeader)
|
success, err := t.controlBuf.execute(t.checkForHeaderListSize, trailingHeader)
|
||||||
|
@ -850,7 +880,9 @@ func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error {
|
||||||
rst := s.getState() == streamActive
|
rst := s.getState() == streamActive
|
||||||
t.finishStream(s, rst, http2.ErrCodeNo, trailingHeader, true)
|
t.finishStream(s, rst, http2.ErrCodeNo, trailingHeader, true)
|
||||||
if t.stats != nil {
|
if t.stats != nil {
|
||||||
t.stats.HandleRPC(s.Context(), &stats.OutTrailer{})
|
t.stats.HandleRPC(s.Context(), &stats.OutTrailer{
|
||||||
|
Trailer: s.trailer.Copy(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -872,7 +904,7 @@ func (t *http2Server) Write(s *Stream, hdr []byte, data []byte, opts *Options) e
|
||||||
// TODO(mmukhi, dfawley): Should the server write also return io.EOF?
|
// TODO(mmukhi, dfawley): Should the server write also return io.EOF?
|
||||||
s.cancel()
|
s.cancel()
|
||||||
select {
|
select {
|
||||||
case <-t.ctx.Done():
|
case <-t.done:
|
||||||
return ErrConnClosing
|
return ErrConnClosing
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
@ -890,13 +922,11 @@ func (t *http2Server) Write(s *Stream, hdr []byte, data []byte, opts *Options) e
|
||||||
streamID: s.id,
|
streamID: s.id,
|
||||||
h: hdr,
|
h: hdr,
|
||||||
d: data,
|
d: data,
|
||||||
onEachWrite: func() {
|
onEachWrite: t.setResetPingStrikes,
|
||||||
atomic.StoreUint32(&t.resetPingStrikes, 1)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
if err := s.wq.get(int32(len(hdr) + len(data))); err != nil {
|
if err := s.wq.get(int32(len(hdr) + len(data))); err != nil {
|
||||||
select {
|
select {
|
||||||
case <-t.ctx.Done():
|
case <-t.done:
|
||||||
return ErrConnClosing
|
return ErrConnClosing
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
@ -913,32 +943,35 @@ func (t *http2Server) Write(s *Stream, hdr []byte, data []byte, opts *Options) e
|
||||||
// after an additional duration of keepalive.Timeout.
|
// after an additional duration of keepalive.Timeout.
|
||||||
func (t *http2Server) keepalive() {
|
func (t *http2Server) keepalive() {
|
||||||
p := &ping{}
|
p := &ping{}
|
||||||
var pingSent bool
|
// True iff a ping has been sent, and no data has been received since then.
|
||||||
maxIdle := time.NewTimer(t.kp.MaxConnectionIdle)
|
outstandingPing := false
|
||||||
maxAge := time.NewTimer(t.kp.MaxConnectionAge)
|
// Amount of time remaining before which we should receive an ACK for the
|
||||||
keepalive := time.NewTimer(t.kp.Time)
|
// last sent ping.
|
||||||
// NOTE: All exit paths of this function should reset their
|
kpTimeoutLeft := time.Duration(0)
|
||||||
// respective timers. A failure to do so will cause the
|
// Records the last value of t.lastRead before we go block on the timer.
|
||||||
// following clean-up to deadlock and eventually leak.
|
// This is required to check for read activity since then.
|
||||||
|
prevNano := time.Now().UnixNano()
|
||||||
|
// Initialize the different timers to their default values.
|
||||||
|
idleTimer := time.NewTimer(t.kp.MaxConnectionIdle)
|
||||||
|
ageTimer := time.NewTimer(t.kp.MaxConnectionAge)
|
||||||
|
kpTimer := time.NewTimer(t.kp.Time)
|
||||||
defer func() {
|
defer func() {
|
||||||
if !maxIdle.Stop() {
|
// We need to drain the underlying channel in these timers after a call
|
||||||
<-maxIdle.C
|
// to Stop(), only if we are interested in resetting them. Clearly we
|
||||||
}
|
// are not interested in resetting them here.
|
||||||
if !maxAge.Stop() {
|
idleTimer.Stop()
|
||||||
<-maxAge.C
|
ageTimer.Stop()
|
||||||
}
|
kpTimer.Stop()
|
||||||
if !keepalive.Stop() {
|
|
||||||
<-keepalive.C
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-maxIdle.C:
|
case <-idleTimer.C:
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
idle := t.idle
|
idle := t.idle
|
||||||
if idle.IsZero() { // The connection is non-idle.
|
if idle.IsZero() { // The connection is non-idle.
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
maxIdle.Reset(t.kp.MaxConnectionIdle)
|
idleTimer.Reset(t.kp.MaxConnectionIdle)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val := t.kp.MaxConnectionIdle - time.Since(idle)
|
val := t.kp.MaxConnectionIdle - time.Since(idle)
|
||||||
|
@ -947,42 +980,52 @@ func (t *http2Server) keepalive() {
|
||||||
// The connection has been idle for a duration of keepalive.MaxConnectionIdle or more.
|
// The connection has been idle for a duration of keepalive.MaxConnectionIdle or more.
|
||||||
// Gracefully close the connection.
|
// Gracefully close the connection.
|
||||||
t.drain(http2.ErrCodeNo, []byte{})
|
t.drain(http2.ErrCodeNo, []byte{})
|
||||||
// Resetting the timer so that the clean-up doesn't deadlock.
|
|
||||||
maxIdle.Reset(infinity)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
maxIdle.Reset(val)
|
idleTimer.Reset(val)
|
||||||
case <-maxAge.C:
|
case <-ageTimer.C:
|
||||||
t.drain(http2.ErrCodeNo, []byte{})
|
t.drain(http2.ErrCodeNo, []byte{})
|
||||||
maxAge.Reset(t.kp.MaxConnectionAgeGrace)
|
ageTimer.Reset(t.kp.MaxConnectionAgeGrace)
|
||||||
select {
|
select {
|
||||||
case <-maxAge.C:
|
case <-ageTimer.C:
|
||||||
// Close the connection after grace period.
|
// Close the connection after grace period.
|
||||||
|
infof("transport: closing server transport due to maximum connection age.")
|
||||||
t.Close()
|
t.Close()
|
||||||
// Resetting the timer so that the clean-up doesn't deadlock.
|
case <-t.done:
|
||||||
maxAge.Reset(infinity)
|
|
||||||
case <-t.ctx.Done():
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case <-keepalive.C:
|
case <-kpTimer.C:
|
||||||
if atomic.CompareAndSwapUint32(&t.activity, 1, 0) {
|
lastRead := atomic.LoadInt64(&t.lastRead)
|
||||||
pingSent = false
|
if lastRead > prevNano {
|
||||||
keepalive.Reset(t.kp.Time)
|
// There has been read activity since the last time we were
|
||||||
|
// here. Setup the timer to fire at kp.Time seconds from
|
||||||
|
// lastRead time and continue.
|
||||||
|
outstandingPing = false
|
||||||
|
kpTimer.Reset(time.Duration(lastRead) + t.kp.Time - time.Duration(time.Now().UnixNano()))
|
||||||
|
prevNano = lastRead
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if pingSent {
|
if outstandingPing && kpTimeoutLeft <= 0 {
|
||||||
|
infof("transport: closing server transport due to idleness.")
|
||||||
t.Close()
|
t.Close()
|
||||||
// Resetting the timer so that the clean-up doesn't deadlock.
|
|
||||||
keepalive.Reset(infinity)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pingSent = true
|
if !outstandingPing {
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
atomic.AddInt64(&t.czData.kpCount, 1)
|
atomic.AddInt64(&t.czData.kpCount, 1)
|
||||||
}
|
}
|
||||||
t.controlBuf.put(p)
|
t.controlBuf.put(p)
|
||||||
keepalive.Reset(t.kp.Timeout)
|
kpTimeoutLeft = t.kp.Timeout
|
||||||
case <-t.ctx.Done():
|
outstandingPing = true
|
||||||
|
}
|
||||||
|
// The amount of time to sleep here is the minimum of kp.Time and
|
||||||
|
// timeoutLeft. This will ensure that we wait only for kp.Time
|
||||||
|
// before sending out the next ping (for cases where the ping is
|
||||||
|
// acked).
|
||||||
|
sleepDuration := minTime(t.kp.Time, kpTimeoutLeft)
|
||||||
|
kpTimeoutLeft -= sleepDuration
|
||||||
|
kpTimer.Reset(sleepDuration)
|
||||||
|
case <-t.done:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1002,7 +1045,7 @@ func (t *http2Server) Close() error {
|
||||||
t.activeStreams = nil
|
t.activeStreams = nil
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
t.controlBuf.finish()
|
t.controlBuf.finish()
|
||||||
t.cancel()
|
close(t.done)
|
||||||
err := t.conn.Close()
|
err := t.conn.Close()
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
channelz.RemoveEntry(t.channelzID)
|
channelz.RemoveEntry(t.channelzID)
|
||||||
|
@ -1019,13 +1062,7 @@ func (t *http2Server) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteStream deletes the stream s from transport's active streams.
|
// deleteStream deletes the stream s from transport's active streams.
|
||||||
func (t *http2Server) deleteStream(s *Stream, eosReceived bool) (oldState streamState) {
|
func (t *http2Server) deleteStream(s *Stream, eosReceived bool) {
|
||||||
oldState = s.swapState(streamDone)
|
|
||||||
if oldState == streamDone {
|
|
||||||
// If the stream was already done, return.
|
|
||||||
return oldState
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case stream sending and receiving are invoked in separate
|
// In case stream sending and receiving are invoked in separate
|
||||||
// goroutines (e.g., bi-directional streaming), cancel needs to be
|
// goroutines (e.g., bi-directional streaming), cancel needs to be
|
||||||
// called to interrupt the potential blocking on other goroutines.
|
// called to interrupt the potential blocking on other goroutines.
|
||||||
|
@ -1047,15 +1084,13 @@ func (t *http2Server) deleteStream(s *Stream, eosReceived bool) (oldState stream
|
||||||
atomic.AddInt64(&t.czData.streamsFailed, 1)
|
atomic.AddInt64(&t.czData.streamsFailed, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return oldState
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// finishStream closes the stream and puts the trailing headerFrame into controlbuf.
|
// finishStream closes the stream and puts the trailing headerFrame into controlbuf.
|
||||||
func (t *http2Server) finishStream(s *Stream, rst bool, rstCode http2.ErrCode, hdr *headerFrame, eosReceived bool) {
|
func (t *http2Server) finishStream(s *Stream, rst bool, rstCode http2.ErrCode, hdr *headerFrame, eosReceived bool) {
|
||||||
oldState := t.deleteStream(s, eosReceived)
|
oldState := s.swapState(streamDone)
|
||||||
// If the stream is already closed, then don't put trailing header to controlbuf.
|
|
||||||
if oldState == streamDone {
|
if oldState == streamDone {
|
||||||
|
// If the stream was already done, return.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1063,14 +1098,18 @@ func (t *http2Server) finishStream(s *Stream, rst bool, rstCode http2.ErrCode, h
|
||||||
streamID: s.id,
|
streamID: s.id,
|
||||||
rst: rst,
|
rst: rst,
|
||||||
rstCode: rstCode,
|
rstCode: rstCode,
|
||||||
onWrite: func() {},
|
onWrite: func() {
|
||||||
|
t.deleteStream(s, eosReceived)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
t.controlBuf.put(hdr)
|
t.controlBuf.put(hdr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// closeStream clears the footprint of a stream when the stream is not needed any more.
|
// closeStream clears the footprint of a stream when the stream is not needed any more.
|
||||||
func (t *http2Server) closeStream(s *Stream, rst bool, rstCode http2.ErrCode, eosReceived bool) {
|
func (t *http2Server) closeStream(s *Stream, rst bool, rstCode http2.ErrCode, eosReceived bool) {
|
||||||
|
s.swapState(streamDone)
|
||||||
t.deleteStream(s, eosReceived)
|
t.deleteStream(s, eosReceived)
|
||||||
|
|
||||||
t.controlBuf.put(&cleanupStream{
|
t.controlBuf.put(&cleanupStream{
|
||||||
streamID: s.id,
|
streamID: s.id,
|
||||||
rst: rst,
|
rst: rst,
|
||||||
|
@ -1146,7 +1185,7 @@ func (t *http2Server) outgoingGoAwayHandler(g *goAway) (bool, error) {
|
||||||
select {
|
select {
|
||||||
case <-t.drainChan:
|
case <-t.drainChan:
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
case <-t.ctx.Done():
|
case <-t.done:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.controlBuf.put(&goAway{code: g.code, debugData: g.debugData})
|
t.controlBuf.put(&goAway{code: g.code, debugData: g.debugData})
|
||||||
|
@ -1196,7 +1235,7 @@ func (t *http2Server) getOutFlowWindow() int64 {
|
||||||
select {
|
select {
|
||||||
case sz := <-resp:
|
case sz := <-resp:
|
||||||
return int64(sz)
|
return int64(sz)
|
||||||
case <-t.ctxDone:
|
case <-t.done:
|
||||||
return -1
|
return -1
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
return -2
|
return -2
|
||||||
|
|
|
@ -667,6 +667,7 @@ func newFramer(conn net.Conn, writeBufferSize, readBufferSize int, maxHeaderList
|
||||||
writer: w,
|
writer: w,
|
||||||
fr: http2.NewFramer(w, r),
|
fr: http2.NewFramer(w, r),
|
||||||
}
|
}
|
||||||
|
f.fr.SetMaxReadFrameSize(http2MaxFrameLen)
|
||||||
// Opt-in to Frame reuse API on framer to reduce garbage.
|
// Opt-in to Frame reuse API on framer to reduce garbage.
|
||||||
// Frames aren't safe to read from after a subsequent call to ReadFrame.
|
// Frames aren't safe to read from after a subsequent call to ReadFrame.
|
||||||
f.fr.SetReuseFrames()
|
f.fr.SetReuseFrames()
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
package transport
|
package transport
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -39,10 +40,32 @@ import (
|
||||||
"google.golang.org/grpc/tap"
|
"google.golang.org/grpc/tap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type bufferPool struct {
|
||||||
|
pool sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBufferPool() *bufferPool {
|
||||||
|
return &bufferPool{
|
||||||
|
pool: sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return new(bytes.Buffer)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *bufferPool) get() *bytes.Buffer {
|
||||||
|
return p.pool.Get().(*bytes.Buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *bufferPool) put(b *bytes.Buffer) {
|
||||||
|
p.pool.Put(b)
|
||||||
|
}
|
||||||
|
|
||||||
// recvMsg represents the received msg from the transport. All transport
|
// recvMsg represents the received msg from the transport. All transport
|
||||||
// protocol specific info has been removed.
|
// protocol specific info has been removed.
|
||||||
type recvMsg struct {
|
type recvMsg struct {
|
||||||
data []byte
|
buffer *bytes.Buffer
|
||||||
// nil: received some data
|
// nil: received some data
|
||||||
// io.EOF: stream is completed. data is nil.
|
// io.EOF: stream is completed. data is nil.
|
||||||
// other non-nil error: transport failure. data is nil.
|
// other non-nil error: transport failure. data is nil.
|
||||||
|
@ -50,10 +73,11 @@ type recvMsg struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// recvBuffer is an unbounded channel of recvMsg structs.
|
// recvBuffer is an unbounded channel of recvMsg structs.
|
||||||
// Note recvBuffer differs from controlBuffer only in that recvBuffer
|
//
|
||||||
// holds a channel of only recvMsg structs instead of objects implementing "item" interface.
|
// Note: recvBuffer differs from buffer.Unbounded only in the fact that it
|
||||||
// recvBuffer is written to much more often than
|
// holds a channel of recvMsg structs instead of objects implementing "item"
|
||||||
// controlBuffer and using strict recvMsg structs helps avoid allocation in "recvBuffer.put"
|
// interface. recvBuffer is written to much more often and using strict recvMsg
|
||||||
|
// structs helps avoid allocation in "recvBuffer.put"
|
||||||
type recvBuffer struct {
|
type recvBuffer struct {
|
||||||
c chan recvMsg
|
c chan recvMsg
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
@ -117,8 +141,9 @@ type recvBufferReader struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
ctxDone <-chan struct{} // cache of ctx.Done() (for performance).
|
ctxDone <-chan struct{} // cache of ctx.Done() (for performance).
|
||||||
recv *recvBuffer
|
recv *recvBuffer
|
||||||
last []byte // Stores the remaining data in the previous calls.
|
last *bytes.Buffer // Stores the remaining data in the previous calls.
|
||||||
err error
|
err error
|
||||||
|
freeBuffer func(*bytes.Buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read reads the next len(p) bytes from last. If last is drained, it tries to
|
// Read reads the next len(p) bytes from last. If last is drained, it tries to
|
||||||
|
@ -128,10 +153,13 @@ func (r *recvBufferReader) Read(p []byte) (n int, err error) {
|
||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
return 0, r.err
|
return 0, r.err
|
||||||
}
|
}
|
||||||
if r.last != nil && len(r.last) > 0 {
|
if r.last != nil {
|
||||||
// Read remaining data left in last call.
|
// Read remaining data left in last call.
|
||||||
copied := copy(p, r.last)
|
copied, _ := r.last.Read(p)
|
||||||
r.last = r.last[copied:]
|
if r.last.Len() == 0 {
|
||||||
|
r.freeBuffer(r.last)
|
||||||
|
r.last = nil
|
||||||
|
}
|
||||||
return copied, nil
|
return copied, nil
|
||||||
}
|
}
|
||||||
if r.closeStream != nil {
|
if r.closeStream != nil {
|
||||||
|
@ -157,6 +185,19 @@ func (r *recvBufferReader) readClient(p []byte) (n int, err error) {
|
||||||
// r.readAdditional acts on that message and returns the necessary error.
|
// r.readAdditional acts on that message and returns the necessary error.
|
||||||
select {
|
select {
|
||||||
case <-r.ctxDone:
|
case <-r.ctxDone:
|
||||||
|
// Note that this adds the ctx error to the end of recv buffer, and
|
||||||
|
// reads from the head. This will delay the error until recv buffer is
|
||||||
|
// empty, thus will delay ctx cancellation in Recv().
|
||||||
|
//
|
||||||
|
// It's done this way to fix a race between ctx cancel and trailer. The
|
||||||
|
// race was, stream.Recv() may return ctx error if ctxDone wins the
|
||||||
|
// race, but stream.Trailer() may return a non-nil md because the stream
|
||||||
|
// was not marked as done when trailer is received. This closeStream
|
||||||
|
// call will mark stream as done, thus fix the race.
|
||||||
|
//
|
||||||
|
// TODO: delaying ctx error seems like a unnecessary side effect. What
|
||||||
|
// we really want is to mark the stream as done, and return ctx error
|
||||||
|
// faster.
|
||||||
r.closeStream(ContextErr(r.ctx.Err()))
|
r.closeStream(ContextErr(r.ctx.Err()))
|
||||||
m := <-r.recv.get()
|
m := <-r.recv.get()
|
||||||
return r.readAdditional(m, p)
|
return r.readAdditional(m, p)
|
||||||
|
@ -170,8 +211,13 @@ func (r *recvBufferReader) readAdditional(m recvMsg, p []byte) (n int, err error
|
||||||
if m.err != nil {
|
if m.err != nil {
|
||||||
return 0, m.err
|
return 0, m.err
|
||||||
}
|
}
|
||||||
copied := copy(p, m.data)
|
copied, _ := m.buffer.Read(p)
|
||||||
r.last = m.data[copied:]
|
if m.buffer.Len() == 0 {
|
||||||
|
r.freeBuffer(m.buffer)
|
||||||
|
r.last = nil
|
||||||
|
} else {
|
||||||
|
r.last = m.buffer
|
||||||
|
}
|
||||||
return copied, nil
|
return copied, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,6 +234,7 @@ const (
|
||||||
type Stream struct {
|
type Stream struct {
|
||||||
id uint32
|
id uint32
|
||||||
st ServerTransport // nil for client side Stream
|
st ServerTransport // nil for client side Stream
|
||||||
|
ct *http2Client // nil for server side Stream
|
||||||
ctx context.Context // the associated context of the stream
|
ctx context.Context // the associated context of the stream
|
||||||
cancel context.CancelFunc // always nil for client side Stream
|
cancel context.CancelFunc // always nil for client side Stream
|
||||||
done chan struct{} // closed at the end of stream to unblock writers. On the client side.
|
done chan struct{} // closed at the end of stream to unblock writers. On the client side.
|
||||||
|
@ -206,6 +253,10 @@ type Stream struct {
|
||||||
|
|
||||||
headerChan chan struct{} // closed to indicate the end of header metadata.
|
headerChan chan struct{} // closed to indicate the end of header metadata.
|
||||||
headerChanClosed uint32 // set when headerChan is closed. Used to avoid closing headerChan multiple times.
|
headerChanClosed uint32 // set when headerChan is closed. Used to avoid closing headerChan multiple times.
|
||||||
|
// headerValid indicates whether a valid header was received. Only
|
||||||
|
// meaningful after headerChan is closed (always call waitOnHeader() before
|
||||||
|
// reading its value). Not valid on server side.
|
||||||
|
headerValid bool
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -258,26 +309,28 @@ func (s *Stream) getState() streamState {
|
||||||
return streamState(atomic.LoadUint32((*uint32)(&s.state)))
|
return streamState(atomic.LoadUint32((*uint32)(&s.state)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) waitOnHeader() error {
|
func (s *Stream) waitOnHeader() {
|
||||||
if s.headerChan == nil {
|
if s.headerChan == nil {
|
||||||
// On the server headerChan is always nil since a stream originates
|
// On the server headerChan is always nil since a stream originates
|
||||||
// only after having received headers.
|
// only after having received headers.
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case <-s.ctx.Done():
|
case <-s.ctx.Done():
|
||||||
return ContextErr(s.ctx.Err())
|
// Close the stream to prevent headers/trailers from changing after
|
||||||
|
// this function returns.
|
||||||
|
s.ct.CloseStream(s, ContextErr(s.ctx.Err()))
|
||||||
|
// headerChan could possibly not be closed yet if closeStream raced
|
||||||
|
// with operateHeaders; wait until it is closed explicitly here.
|
||||||
|
<-s.headerChan
|
||||||
case <-s.headerChan:
|
case <-s.headerChan:
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecvCompress returns the compression algorithm applied to the inbound
|
// RecvCompress returns the compression algorithm applied to the inbound
|
||||||
// message. It is empty string if there is no compression applied.
|
// message. It is empty string if there is no compression applied.
|
||||||
func (s *Stream) RecvCompress() string {
|
func (s *Stream) RecvCompress() string {
|
||||||
if err := s.waitOnHeader(); err != nil {
|
s.waitOnHeader()
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return s.recvCompress
|
return s.recvCompress
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,36 +351,27 @@ func (s *Stream) Done() <-chan struct{} {
|
||||||
// available. It blocks until i) the metadata is ready or ii) there is no header
|
// available. It blocks until i) the metadata is ready or ii) there is no header
|
||||||
// metadata or iii) the stream is canceled/expired.
|
// metadata or iii) the stream is canceled/expired.
|
||||||
//
|
//
|
||||||
// On server side, it returns the out header after t.WriteHeader is called.
|
// On server side, it returns the out header after t.WriteHeader is called. It
|
||||||
|
// does not block and must not be called until after WriteHeader.
|
||||||
func (s *Stream) Header() (metadata.MD, error) {
|
func (s *Stream) Header() (metadata.MD, error) {
|
||||||
if s.headerChan == nil && s.header != nil {
|
if s.headerChan == nil {
|
||||||
// On server side, return the header in stream. It will be the out
|
// On server side, return the header in stream. It will be the out
|
||||||
// header after t.WriteHeader is called.
|
// header after t.WriteHeader is called.
|
||||||
return s.header.Copy(), nil
|
return s.header.Copy(), nil
|
||||||
}
|
}
|
||||||
err := s.waitOnHeader()
|
s.waitOnHeader()
|
||||||
// Even if the stream is closed, header is returned if available.
|
if !s.headerValid {
|
||||||
select {
|
return nil, s.status.Err()
|
||||||
case <-s.headerChan:
|
|
||||||
if s.header == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
return s.header.Copy(), nil
|
return s.header.Copy(), nil
|
||||||
default:
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrailersOnly blocks until a header or trailers-only frame is received and
|
// TrailersOnly blocks until a header or trailers-only frame is received and
|
||||||
// then returns true if the stream was trailers-only. If the stream ends
|
// then returns true if the stream was trailers-only. If the stream ends
|
||||||
// before headers are received, returns true, nil. If a context error happens
|
// before headers are received, returns true, nil. Client-side only.
|
||||||
// first, returns it as a status error. Client-side only.
|
func (s *Stream) TrailersOnly() bool {
|
||||||
func (s *Stream) TrailersOnly() (bool, error) {
|
s.waitOnHeader()
|
||||||
err := s.waitOnHeader()
|
return s.noHeaders
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return s.noHeaders, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trailer returns the cached trailer metedata. Note that if it is not called
|
// Trailer returns the cached trailer metedata. Note that if it is not called
|
||||||
|
@ -481,6 +525,7 @@ type ServerConfig struct {
|
||||||
ReadBufferSize int
|
ReadBufferSize int
|
||||||
ChannelzParentID int64
|
ChannelzParentID int64
|
||||||
MaxHeaderListSize *uint32
|
MaxHeaderListSize *uint32
|
||||||
|
HeaderTableSize *uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServerTransport creates a ServerTransport with conn or non-nil error
|
// NewServerTransport creates a ServerTransport with conn or non-nil error
|
||||||
|
|
|
@ -17,9 +17,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Package naming defines the naming API and related data structures for gRPC.
|
// Package naming defines the naming API and related data structures for gRPC.
|
||||||
// The interface is EXPERIMENTAL and may be subject to change.
|
|
||||||
//
|
//
|
||||||
// Deprecated: please use package resolver.
|
// This package is deprecated: please use package resolver instead.
|
||||||
package naming
|
package naming
|
||||||
|
|
||||||
// Operation defines the corresponding operations for a name resolution change.
|
// Operation defines the corresponding operations for a name resolution change.
|
||||||
|
|
|
@ -20,6 +20,7 @@ package grpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -31,49 +32,78 @@ import (
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// v2PickerWrapper wraps a balancer.Picker while providing the
|
||||||
|
// balancer.V2Picker API. It requires a pickerWrapper to generate errors
|
||||||
|
// including the latest connectionError. To be deleted when balancer.Picker is
|
||||||
|
// updated to the balancer.V2Picker API.
|
||||||
|
type v2PickerWrapper struct {
|
||||||
|
picker balancer.Picker
|
||||||
|
connErr *connErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *v2PickerWrapper) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
|
||||||
|
sc, done, err := v.picker.Pick(info.Ctx, info)
|
||||||
|
if err != nil {
|
||||||
|
if err == balancer.ErrTransientFailure {
|
||||||
|
return balancer.PickResult{}, balancer.TransientFailureError(fmt.Errorf("%v, latest connection error: %v", err, v.connErr.connectionError()))
|
||||||
|
}
|
||||||
|
return balancer.PickResult{}, err
|
||||||
|
}
|
||||||
|
return balancer.PickResult{SubConn: sc, Done: done}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// pickerWrapper is a wrapper of balancer.Picker. It blocks on certain pick
|
// pickerWrapper is a wrapper of balancer.Picker. It blocks on certain pick
|
||||||
// actions and unblock when there's a picker update.
|
// actions and unblock when there's a picker update.
|
||||||
type pickerWrapper struct {
|
type pickerWrapper struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
done bool
|
done bool
|
||||||
blockingCh chan struct{}
|
blockingCh chan struct{}
|
||||||
picker balancer.Picker
|
picker balancer.V2Picker
|
||||||
|
|
||||||
// The latest connection happened.
|
// The latest connection error. TODO: remove when V1 picker is deprecated;
|
||||||
connErrMu sync.Mutex
|
// balancer should be responsible for providing the error.
|
||||||
connErr error
|
*connErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPickerWrapper() *pickerWrapper {
|
type connErr struct {
|
||||||
bp := &pickerWrapper{blockingCh: make(chan struct{})}
|
mu sync.Mutex
|
||||||
return bp
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bp *pickerWrapper) updateConnectionError(err error) {
|
func (c *connErr) updateConnectionError(err error) {
|
||||||
bp.connErrMu.Lock()
|
c.mu.Lock()
|
||||||
bp.connErr = err
|
c.err = err
|
||||||
bp.connErrMu.Unlock()
|
c.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bp *pickerWrapper) connectionError() error {
|
func (c *connErr) connectionError() error {
|
||||||
bp.connErrMu.Lock()
|
c.mu.Lock()
|
||||||
err := bp.connErr
|
err := c.err
|
||||||
bp.connErrMu.Unlock()
|
c.mu.Unlock()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newPickerWrapper() *pickerWrapper {
|
||||||
|
return &pickerWrapper{blockingCh: make(chan struct{}), connErr: &connErr{}}
|
||||||
|
}
|
||||||
|
|
||||||
// updatePicker is called by UpdateBalancerState. It unblocks all blocked pick.
|
// updatePicker is called by UpdateBalancerState. It unblocks all blocked pick.
|
||||||
func (bp *pickerWrapper) updatePicker(p balancer.Picker) {
|
func (pw *pickerWrapper) updatePicker(p balancer.Picker) {
|
||||||
bp.mu.Lock()
|
pw.updatePickerV2(&v2PickerWrapper{picker: p, connErr: pw.connErr})
|
||||||
if bp.done {
|
}
|
||||||
bp.mu.Unlock()
|
|
||||||
|
// updatePicker is called by UpdateBalancerState. It unblocks all blocked pick.
|
||||||
|
func (pw *pickerWrapper) updatePickerV2(p balancer.V2Picker) {
|
||||||
|
pw.mu.Lock()
|
||||||
|
if pw.done {
|
||||||
|
pw.mu.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bp.picker = p
|
pw.picker = p
|
||||||
// bp.blockingCh should never be nil.
|
// pw.blockingCh should never be nil.
|
||||||
close(bp.blockingCh)
|
close(pw.blockingCh)
|
||||||
bp.blockingCh = make(chan struct{})
|
pw.blockingCh = make(chan struct{})
|
||||||
bp.mu.Unlock()
|
pw.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func doneChannelzWrapper(acw *acBalancerWrapper, done func(balancer.DoneInfo)) func(balancer.DoneInfo) {
|
func doneChannelzWrapper(acw *acBalancerWrapper, done func(balancer.DoneInfo)) func(balancer.DoneInfo) {
|
||||||
|
@ -100,83 +130,85 @@ func doneChannelzWrapper(acw *acBalancerWrapper, done func(balancer.DoneInfo)) f
|
||||||
// - the current picker returns other errors and failfast is false.
|
// - the current picker returns other errors and failfast is false.
|
||||||
// - the subConn returned by the current picker is not READY
|
// - the subConn returned by the current picker is not READY
|
||||||
// When one of these situations happens, pick blocks until the picker gets updated.
|
// When one of these situations happens, pick blocks until the picker gets updated.
|
||||||
func (bp *pickerWrapper) pick(ctx context.Context, failfast bool, opts balancer.PickOptions) (transport.ClientTransport, func(balancer.DoneInfo), error) {
|
func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer.PickInfo) (transport.ClientTransport, func(balancer.DoneInfo), error) {
|
||||||
var ch chan struct{}
|
var ch chan struct{}
|
||||||
|
|
||||||
|
var lastPickErr error
|
||||||
for {
|
for {
|
||||||
bp.mu.Lock()
|
pw.mu.Lock()
|
||||||
if bp.done {
|
if pw.done {
|
||||||
bp.mu.Unlock()
|
pw.mu.Unlock()
|
||||||
return nil, nil, ErrClientConnClosing
|
return nil, nil, ErrClientConnClosing
|
||||||
}
|
}
|
||||||
|
|
||||||
if bp.picker == nil {
|
if pw.picker == nil {
|
||||||
ch = bp.blockingCh
|
ch = pw.blockingCh
|
||||||
}
|
}
|
||||||
if ch == bp.blockingCh {
|
if ch == pw.blockingCh {
|
||||||
// This could happen when either:
|
// This could happen when either:
|
||||||
// - bp.picker is nil (the previous if condition), or
|
// - pw.picker is nil (the previous if condition), or
|
||||||
// - has called pick on the current picker.
|
// - has called pick on the current picker.
|
||||||
bp.mu.Unlock()
|
pw.mu.Unlock()
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
if connectionErr := bp.connectionError(); connectionErr != nil {
|
var errStr string
|
||||||
|
if lastPickErr != nil {
|
||||||
|
errStr = "latest balancer error: " + lastPickErr.Error()
|
||||||
|
} else if connectionErr := pw.connectionError(); connectionErr != nil {
|
||||||
|
errStr = "latest connection error: " + connectionErr.Error()
|
||||||
|
} else {
|
||||||
|
errStr = ctx.Err().Error()
|
||||||
|
}
|
||||||
switch ctx.Err() {
|
switch ctx.Err() {
|
||||||
case context.DeadlineExceeded:
|
case context.DeadlineExceeded:
|
||||||
return nil, nil, status.Errorf(codes.DeadlineExceeded, "latest connection error: %v", connectionErr)
|
return nil, nil, status.Error(codes.DeadlineExceeded, errStr)
|
||||||
case context.Canceled:
|
case context.Canceled:
|
||||||
return nil, nil, status.Errorf(codes.Canceled, "latest connection error: %v", connectionErr)
|
return nil, nil, status.Error(codes.Canceled, errStr)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return nil, nil, ctx.Err()
|
|
||||||
case <-ch:
|
case <-ch:
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ch = bp.blockingCh
|
ch = pw.blockingCh
|
||||||
p := bp.picker
|
p := pw.picker
|
||||||
bp.mu.Unlock()
|
pw.mu.Unlock()
|
||||||
|
|
||||||
subConn, done, err := p.Pick(ctx, opts)
|
pickResult, err := p.Pick(info)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err {
|
if err == balancer.ErrNoSubConnAvailable {
|
||||||
case balancer.ErrNoSubConnAvailable:
|
|
||||||
continue
|
|
||||||
case balancer.ErrTransientFailure:
|
|
||||||
if !failfast {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return nil, nil, status.Errorf(codes.Unavailable, "%v, latest connection error: %v", err, bp.connectionError())
|
if tfe, ok := err.(interface{ IsTransientFailure() bool }); ok && tfe.IsTransientFailure() {
|
||||||
case context.DeadlineExceeded:
|
if !failfast {
|
||||||
return nil, nil, status.Error(codes.DeadlineExceeded, err.Error())
|
lastPickErr = err
|
||||||
case context.Canceled:
|
continue
|
||||||
return nil, nil, status.Error(codes.Canceled, err.Error())
|
}
|
||||||
default:
|
return nil, nil, status.Error(codes.Unavailable, err.Error())
|
||||||
|
}
|
||||||
if _, ok := status.FromError(err); ok {
|
if _, ok := status.FromError(err); ok {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
// err is some other error.
|
// err is some other error.
|
||||||
return nil, nil, status.Error(codes.Unknown, err.Error())
|
return nil, nil, status.Error(codes.Unknown, err.Error())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
acw, ok := subConn.(*acBalancerWrapper)
|
acw, ok := pickResult.SubConn.(*acBalancerWrapper)
|
||||||
if !ok {
|
if !ok {
|
||||||
grpclog.Error("subconn returned from pick is not *acBalancerWrapper")
|
grpclog.Error("subconn returned from pick is not *acBalancerWrapper")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if t, ok := acw.getAddrConn().getReadyTransport(); ok {
|
if t, ok := acw.getAddrConn().getReadyTransport(); ok {
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
return t, doneChannelzWrapper(acw, done), nil
|
return t, doneChannelzWrapper(acw, pickResult.Done), nil
|
||||||
}
|
}
|
||||||
return t, done, nil
|
return t, pickResult.Done, nil
|
||||||
}
|
}
|
||||||
if done != nil {
|
if pickResult.Done != nil {
|
||||||
// Calling done with nil error, no bytes sent and no bytes received.
|
// Calling done with nil error, no bytes sent and no bytes received.
|
||||||
// DoneInfo with default value works.
|
// DoneInfo with default value works.
|
||||||
done(balancer.DoneInfo{})
|
pickResult.Done(balancer.DoneInfo{})
|
||||||
}
|
}
|
||||||
grpclog.Infof("blockingPicker: the picked transport is not ready, loop back to repick")
|
grpclog.Infof("blockingPicker: the picked transport is not ready, loop back to repick")
|
||||||
// If ok == false, ac.state is not READY.
|
// If ok == false, ac.state is not READY.
|
||||||
|
@ -186,12 +218,12 @@ func (bp *pickerWrapper) pick(ctx context.Context, failfast bool, opts balancer.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bp *pickerWrapper) close() {
|
func (pw *pickerWrapper) close() {
|
||||||
bp.mu.Lock()
|
pw.mu.Lock()
|
||||||
defer bp.mu.Unlock()
|
defer pw.mu.Unlock()
|
||||||
if bp.done {
|
if pw.done {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bp.done = true
|
pw.done = true
|
||||||
close(bp.blockingCh)
|
close(pw.blockingCh)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,12 +19,14 @@
|
||||||
package grpc
|
package grpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"errors"
|
||||||
|
|
||||||
"google.golang.org/grpc/balancer"
|
"google.golang.org/grpc/balancer"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/connectivity"
|
"google.golang.org/grpc/connectivity"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
"google.golang.org/grpc/resolver"
|
"google.golang.org/grpc/resolver"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PickFirstBalancerName is the name of the pick_first balancer.
|
// PickFirstBalancerName is the name of the pick_first balancer.
|
||||||
|
@ -45,48 +47,98 @@ func (*pickfirstBuilder) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type pickfirstBalancer struct {
|
type pickfirstBalancer struct {
|
||||||
|
state connectivity.State
|
||||||
cc balancer.ClientConn
|
cc balancer.ClientConn
|
||||||
sc balancer.SubConn
|
sc balancer.SubConn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ balancer.V2Balancer = &pickfirstBalancer{} // Assert we implement v2
|
||||||
|
|
||||||
func (b *pickfirstBalancer) HandleResolvedAddrs(addrs []resolver.Address, err error) {
|
func (b *pickfirstBalancer) HandleResolvedAddrs(addrs []resolver.Address, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Infof("pickfirstBalancer: HandleResolvedAddrs called with error %v", err)
|
b.ResolverError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if b.sc == nil {
|
b.UpdateClientConnState(balancer.ClientConnState{ResolverState: resolver.State{Addresses: addrs}}) // Ignore error
|
||||||
b.sc, err = b.cc.NewSubConn(addrs, balancer.NewSubConnOptions{})
|
|
||||||
if err != nil {
|
|
||||||
//TODO(yuxuanli): why not change the cc state to Idle?
|
|
||||||
grpclog.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b.cc.UpdateBalancerState(connectivity.Idle, &picker{sc: b.sc})
|
|
||||||
b.sc.Connect()
|
|
||||||
} else {
|
|
||||||
b.sc.UpdateAddresses(addrs)
|
|
||||||
b.sc.Connect()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *pickfirstBalancer) HandleSubConnStateChange(sc balancer.SubConn, s connectivity.State) {
|
func (b *pickfirstBalancer) HandleSubConnStateChange(sc balancer.SubConn, s connectivity.State) {
|
||||||
|
b.UpdateSubConnState(sc, balancer.SubConnState{ConnectivityState: s})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *pickfirstBalancer) ResolverError(err error) {
|
||||||
|
switch b.state {
|
||||||
|
case connectivity.TransientFailure, connectivity.Idle, connectivity.Connecting:
|
||||||
|
// Set a failing picker if we don't have a good picker.
|
||||||
|
b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.TransientFailure,
|
||||||
|
Picker: &picker{err: status.Errorf(codes.Unavailable, "name resolver error: %v", err)}},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if grpclog.V(2) {
|
||||||
|
grpclog.Infof("pickfirstBalancer: ResolverError called with error %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *pickfirstBalancer) UpdateClientConnState(cs balancer.ClientConnState) error {
|
||||||
|
if len(cs.ResolverState.Addresses) == 0 {
|
||||||
|
b.ResolverError(errors.New("produced zero addresses"))
|
||||||
|
return balancer.ErrBadResolverState
|
||||||
|
}
|
||||||
|
if b.sc == nil {
|
||||||
|
var err error
|
||||||
|
b.sc, err = b.cc.NewSubConn(cs.ResolverState.Addresses, balancer.NewSubConnOptions{})
|
||||||
|
if err != nil {
|
||||||
|
if grpclog.V(2) {
|
||||||
|
grpclog.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err)
|
||||||
|
}
|
||||||
|
b.state = connectivity.TransientFailure
|
||||||
|
b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.TransientFailure,
|
||||||
|
Picker: &picker{err: status.Errorf(codes.Unavailable, "error creating connection: %v", err)}},
|
||||||
|
)
|
||||||
|
return balancer.ErrBadResolverState
|
||||||
|
}
|
||||||
|
b.state = connectivity.Idle
|
||||||
|
b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.Idle, Picker: &picker{result: balancer.PickResult{SubConn: b.sc}}})
|
||||||
|
b.sc.Connect()
|
||||||
|
} else {
|
||||||
|
b.sc.UpdateAddresses(cs.ResolverState.Addresses)
|
||||||
|
b.sc.Connect()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *pickfirstBalancer) UpdateSubConnState(sc balancer.SubConn, s balancer.SubConnState) {
|
||||||
|
if grpclog.V(2) {
|
||||||
grpclog.Infof("pickfirstBalancer: HandleSubConnStateChange: %p, %v", sc, s)
|
grpclog.Infof("pickfirstBalancer: HandleSubConnStateChange: %p, %v", sc, s)
|
||||||
|
}
|
||||||
if b.sc != sc {
|
if b.sc != sc {
|
||||||
|
if grpclog.V(2) {
|
||||||
grpclog.Infof("pickfirstBalancer: ignored state change because sc is not recognized")
|
grpclog.Infof("pickfirstBalancer: ignored state change because sc is not recognized")
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if s == connectivity.Shutdown {
|
b.state = s.ConnectivityState
|
||||||
|
if s.ConnectivityState == connectivity.Shutdown {
|
||||||
b.sc = nil
|
b.sc = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch s {
|
switch s.ConnectivityState {
|
||||||
case connectivity.Ready, connectivity.Idle:
|
case connectivity.Ready, connectivity.Idle:
|
||||||
b.cc.UpdateBalancerState(s, &picker{sc: sc})
|
b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &picker{result: balancer.PickResult{SubConn: sc}}})
|
||||||
case connectivity.Connecting:
|
case connectivity.Connecting:
|
||||||
b.cc.UpdateBalancerState(s, &picker{err: balancer.ErrNoSubConnAvailable})
|
b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &picker{err: balancer.ErrNoSubConnAvailable}})
|
||||||
case connectivity.TransientFailure:
|
case connectivity.TransientFailure:
|
||||||
b.cc.UpdateBalancerState(s, &picker{err: balancer.ErrTransientFailure})
|
err := balancer.ErrTransientFailure
|
||||||
|
// TODO: this can be unconditional after the V1 API is removed, as
|
||||||
|
// SubConnState will always contain a connection error.
|
||||||
|
if s.ConnectionError != nil {
|
||||||
|
err = balancer.TransientFailureError(s.ConnectionError)
|
||||||
|
}
|
||||||
|
b.cc.UpdateState(balancer.State{
|
||||||
|
ConnectivityState: s.ConnectivityState,
|
||||||
|
Picker: &picker{err: err},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,15 +146,12 @@ func (b *pickfirstBalancer) Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
type picker struct {
|
type picker struct {
|
||||||
|
result balancer.PickResult
|
||||||
err error
|
err error
|
||||||
sc balancer.SubConn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *picker) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) {
|
func (p *picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
|
||||||
if p.err != nil {
|
return p.result, p.err
|
||||||
return nil, nil, p.err
|
|
||||||
}
|
|
||||||
return p.sc, nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Reflection
|
||||||
|
|
||||||
|
Package reflection implements server reflection service.
|
||||||
|
|
||||||
|
The service implemented is defined in: https://github.com/grpc/grpc/blob/master/src/proto/grpc/reflection/v1alpha/reflection.proto.
|
||||||
|
|
||||||
|
To register server reflection on a gRPC server:
|
||||||
|
```go
|
||||||
|
import "google.golang.org/grpc/reflection"
|
||||||
|
|
||||||
|
s := grpc.NewServer()
|
||||||
|
pb.RegisterYourOwnServer(s, &server{})
|
||||||
|
|
||||||
|
// Register reflection service on gRPC server.
|
||||||
|
reflection.Register(s)
|
||||||
|
|
||||||
|
s.Serve(lis)
|
||||||
|
```
|
750
vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection.pb.go
generated
vendored
Normal file
750
vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection.pb.go
generated
vendored
Normal file
|
@ -0,0 +1,750 @@
|
||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// source: grpc_reflection_v1alpha/reflection.proto
|
||||||
|
|
||||||
|
package grpc_reflection_v1alpha
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
fmt "fmt"
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
math "math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
|
|
||||||
|
// The message sent by the client when calling ServerReflectionInfo method.
|
||||||
|
type ServerReflectionRequest struct {
|
||||||
|
Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"`
|
||||||
|
// To use reflection service, the client should set one of the following
|
||||||
|
// fields in message_request. The server distinguishes requests by their
|
||||||
|
// defined field and then handles them using corresponding methods.
|
||||||
|
//
|
||||||
|
// Types that are valid to be assigned to MessageRequest:
|
||||||
|
// *ServerReflectionRequest_FileByFilename
|
||||||
|
// *ServerReflectionRequest_FileContainingSymbol
|
||||||
|
// *ServerReflectionRequest_FileContainingExtension
|
||||||
|
// *ServerReflectionRequest_AllExtensionNumbersOfType
|
||||||
|
// *ServerReflectionRequest_ListServices
|
||||||
|
MessageRequest isServerReflectionRequest_MessageRequest `protobuf_oneof:"message_request"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServerReflectionRequest) Reset() { *m = ServerReflectionRequest{} }
|
||||||
|
func (m *ServerReflectionRequest) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*ServerReflectionRequest) ProtoMessage() {}
|
||||||
|
func (*ServerReflectionRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_42a8ac412db3cb03, []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServerReflectionRequest) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_ServerReflectionRequest.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *ServerReflectionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_ServerReflectionRequest.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *ServerReflectionRequest) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_ServerReflectionRequest.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *ServerReflectionRequest) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_ServerReflectionRequest.Size(m)
|
||||||
|
}
|
||||||
|
func (m *ServerReflectionRequest) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_ServerReflectionRequest.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_ServerReflectionRequest proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *ServerReflectionRequest) GetHost() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Host
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type isServerReflectionRequest_MessageRequest interface {
|
||||||
|
isServerReflectionRequest_MessageRequest()
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerReflectionRequest_FileByFilename struct {
|
||||||
|
FileByFilename string `protobuf:"bytes,3,opt,name=file_by_filename,json=fileByFilename,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerReflectionRequest_FileContainingSymbol struct {
|
||||||
|
FileContainingSymbol string `protobuf:"bytes,4,opt,name=file_containing_symbol,json=fileContainingSymbol,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerReflectionRequest_FileContainingExtension struct {
|
||||||
|
FileContainingExtension *ExtensionRequest `protobuf:"bytes,5,opt,name=file_containing_extension,json=fileContainingExtension,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerReflectionRequest_AllExtensionNumbersOfType struct {
|
||||||
|
AllExtensionNumbersOfType string `protobuf:"bytes,6,opt,name=all_extension_numbers_of_type,json=allExtensionNumbersOfType,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerReflectionRequest_ListServices struct {
|
||||||
|
ListServices string `protobuf:"bytes,7,opt,name=list_services,json=listServices,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ServerReflectionRequest_FileByFilename) isServerReflectionRequest_MessageRequest() {}
|
||||||
|
|
||||||
|
func (*ServerReflectionRequest_FileContainingSymbol) isServerReflectionRequest_MessageRequest() {}
|
||||||
|
|
||||||
|
func (*ServerReflectionRequest_FileContainingExtension) isServerReflectionRequest_MessageRequest() {}
|
||||||
|
|
||||||
|
func (*ServerReflectionRequest_AllExtensionNumbersOfType) isServerReflectionRequest_MessageRequest() {}
|
||||||
|
|
||||||
|
func (*ServerReflectionRequest_ListServices) isServerReflectionRequest_MessageRequest() {}
|
||||||
|
|
||||||
|
func (m *ServerReflectionRequest) GetMessageRequest() isServerReflectionRequest_MessageRequest {
|
||||||
|
if m != nil {
|
||||||
|
return m.MessageRequest
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServerReflectionRequest) GetFileByFilename() string {
|
||||||
|
if x, ok := m.GetMessageRequest().(*ServerReflectionRequest_FileByFilename); ok {
|
||||||
|
return x.FileByFilename
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServerReflectionRequest) GetFileContainingSymbol() string {
|
||||||
|
if x, ok := m.GetMessageRequest().(*ServerReflectionRequest_FileContainingSymbol); ok {
|
||||||
|
return x.FileContainingSymbol
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServerReflectionRequest) GetFileContainingExtension() *ExtensionRequest {
|
||||||
|
if x, ok := m.GetMessageRequest().(*ServerReflectionRequest_FileContainingExtension); ok {
|
||||||
|
return x.FileContainingExtension
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServerReflectionRequest) GetAllExtensionNumbersOfType() string {
|
||||||
|
if x, ok := m.GetMessageRequest().(*ServerReflectionRequest_AllExtensionNumbersOfType); ok {
|
||||||
|
return x.AllExtensionNumbersOfType
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServerReflectionRequest) GetListServices() string {
|
||||||
|
if x, ok := m.GetMessageRequest().(*ServerReflectionRequest_ListServices); ok {
|
||||||
|
return x.ListServices
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX_OneofWrappers is for the internal use of the proto package.
|
||||||
|
func (*ServerReflectionRequest) XXX_OneofWrappers() []interface{} {
|
||||||
|
return []interface{}{
|
||||||
|
(*ServerReflectionRequest_FileByFilename)(nil),
|
||||||
|
(*ServerReflectionRequest_FileContainingSymbol)(nil),
|
||||||
|
(*ServerReflectionRequest_FileContainingExtension)(nil),
|
||||||
|
(*ServerReflectionRequest_AllExtensionNumbersOfType)(nil),
|
||||||
|
(*ServerReflectionRequest_ListServices)(nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The type name and extension number sent by the client when requesting
|
||||||
|
// file_containing_extension.
|
||||||
|
type ExtensionRequest struct {
|
||||||
|
// Fully-qualified type name. The format should be <package>.<type>
|
||||||
|
ContainingType string `protobuf:"bytes,1,opt,name=containing_type,json=containingType,proto3" json:"containing_type,omitempty"`
|
||||||
|
ExtensionNumber int32 `protobuf:"varint,2,opt,name=extension_number,json=extensionNumber,proto3" json:"extension_number,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ExtensionRequest) Reset() { *m = ExtensionRequest{} }
|
||||||
|
func (m *ExtensionRequest) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*ExtensionRequest) ProtoMessage() {}
|
||||||
|
func (*ExtensionRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_42a8ac412db3cb03, []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ExtensionRequest) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_ExtensionRequest.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *ExtensionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_ExtensionRequest.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *ExtensionRequest) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_ExtensionRequest.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *ExtensionRequest) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_ExtensionRequest.Size(m)
|
||||||
|
}
|
||||||
|
func (m *ExtensionRequest) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_ExtensionRequest.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_ExtensionRequest proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *ExtensionRequest) GetContainingType() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.ContainingType
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ExtensionRequest) GetExtensionNumber() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.ExtensionNumber
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// The message sent by the server to answer ServerReflectionInfo method.
|
||||||
|
type ServerReflectionResponse struct {
|
||||||
|
ValidHost string `protobuf:"bytes,1,opt,name=valid_host,json=validHost,proto3" json:"valid_host,omitempty"`
|
||||||
|
OriginalRequest *ServerReflectionRequest `protobuf:"bytes,2,opt,name=original_request,json=originalRequest,proto3" json:"original_request,omitempty"`
|
||||||
|
// The server sets one of the following fields according to the
|
||||||
|
// message_request in the request.
|
||||||
|
//
|
||||||
|
// Types that are valid to be assigned to MessageResponse:
|
||||||
|
// *ServerReflectionResponse_FileDescriptorResponse
|
||||||
|
// *ServerReflectionResponse_AllExtensionNumbersResponse
|
||||||
|
// *ServerReflectionResponse_ListServicesResponse
|
||||||
|
// *ServerReflectionResponse_ErrorResponse
|
||||||
|
MessageResponse isServerReflectionResponse_MessageResponse `protobuf_oneof:"message_response"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServerReflectionResponse) Reset() { *m = ServerReflectionResponse{} }
|
||||||
|
func (m *ServerReflectionResponse) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*ServerReflectionResponse) ProtoMessage() {}
|
||||||
|
func (*ServerReflectionResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_42a8ac412db3cb03, []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServerReflectionResponse) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_ServerReflectionResponse.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *ServerReflectionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_ServerReflectionResponse.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *ServerReflectionResponse) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_ServerReflectionResponse.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *ServerReflectionResponse) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_ServerReflectionResponse.Size(m)
|
||||||
|
}
|
||||||
|
func (m *ServerReflectionResponse) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_ServerReflectionResponse.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_ServerReflectionResponse proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *ServerReflectionResponse) GetValidHost() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.ValidHost
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServerReflectionResponse) GetOriginalRequest() *ServerReflectionRequest {
|
||||||
|
if m != nil {
|
||||||
|
return m.OriginalRequest
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type isServerReflectionResponse_MessageResponse interface {
|
||||||
|
isServerReflectionResponse_MessageResponse()
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerReflectionResponse_FileDescriptorResponse struct {
|
||||||
|
FileDescriptorResponse *FileDescriptorResponse `protobuf:"bytes,4,opt,name=file_descriptor_response,json=fileDescriptorResponse,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerReflectionResponse_AllExtensionNumbersResponse struct {
|
||||||
|
AllExtensionNumbersResponse *ExtensionNumberResponse `protobuf:"bytes,5,opt,name=all_extension_numbers_response,json=allExtensionNumbersResponse,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerReflectionResponse_ListServicesResponse struct {
|
||||||
|
ListServicesResponse *ListServiceResponse `protobuf:"bytes,6,opt,name=list_services_response,json=listServicesResponse,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerReflectionResponse_ErrorResponse struct {
|
||||||
|
ErrorResponse *ErrorResponse `protobuf:"bytes,7,opt,name=error_response,json=errorResponse,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ServerReflectionResponse_FileDescriptorResponse) isServerReflectionResponse_MessageResponse() {}
|
||||||
|
|
||||||
|
func (*ServerReflectionResponse_AllExtensionNumbersResponse) isServerReflectionResponse_MessageResponse() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ServerReflectionResponse_ListServicesResponse) isServerReflectionResponse_MessageResponse() {}
|
||||||
|
|
||||||
|
func (*ServerReflectionResponse_ErrorResponse) isServerReflectionResponse_MessageResponse() {}
|
||||||
|
|
||||||
|
func (m *ServerReflectionResponse) GetMessageResponse() isServerReflectionResponse_MessageResponse {
|
||||||
|
if m != nil {
|
||||||
|
return m.MessageResponse
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServerReflectionResponse) GetFileDescriptorResponse() *FileDescriptorResponse {
|
||||||
|
if x, ok := m.GetMessageResponse().(*ServerReflectionResponse_FileDescriptorResponse); ok {
|
||||||
|
return x.FileDescriptorResponse
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServerReflectionResponse) GetAllExtensionNumbersResponse() *ExtensionNumberResponse {
|
||||||
|
if x, ok := m.GetMessageResponse().(*ServerReflectionResponse_AllExtensionNumbersResponse); ok {
|
||||||
|
return x.AllExtensionNumbersResponse
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServerReflectionResponse) GetListServicesResponse() *ListServiceResponse {
|
||||||
|
if x, ok := m.GetMessageResponse().(*ServerReflectionResponse_ListServicesResponse); ok {
|
||||||
|
return x.ListServicesResponse
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServerReflectionResponse) GetErrorResponse() *ErrorResponse {
|
||||||
|
if x, ok := m.GetMessageResponse().(*ServerReflectionResponse_ErrorResponse); ok {
|
||||||
|
return x.ErrorResponse
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX_OneofWrappers is for the internal use of the proto package.
|
||||||
|
func (*ServerReflectionResponse) XXX_OneofWrappers() []interface{} {
|
||||||
|
return []interface{}{
|
||||||
|
(*ServerReflectionResponse_FileDescriptorResponse)(nil),
|
||||||
|
(*ServerReflectionResponse_AllExtensionNumbersResponse)(nil),
|
||||||
|
(*ServerReflectionResponse_ListServicesResponse)(nil),
|
||||||
|
(*ServerReflectionResponse_ErrorResponse)(nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialized FileDescriptorProto messages sent by the server answering
|
||||||
|
// a file_by_filename, file_containing_symbol, or file_containing_extension
|
||||||
|
// request.
|
||||||
|
type FileDescriptorResponse struct {
|
||||||
|
// Serialized FileDescriptorProto messages. We avoid taking a dependency on
|
||||||
|
// descriptor.proto, which uses proto2 only features, by making them opaque
|
||||||
|
// bytes instead.
|
||||||
|
FileDescriptorProto [][]byte `protobuf:"bytes,1,rep,name=file_descriptor_proto,json=fileDescriptorProto,proto3" json:"file_descriptor_proto,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *FileDescriptorResponse) Reset() { *m = FileDescriptorResponse{} }
|
||||||
|
func (m *FileDescriptorResponse) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*FileDescriptorResponse) ProtoMessage() {}
|
||||||
|
func (*FileDescriptorResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_42a8ac412db3cb03, []int{3}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *FileDescriptorResponse) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_FileDescriptorResponse.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *FileDescriptorResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_FileDescriptorResponse.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *FileDescriptorResponse) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_FileDescriptorResponse.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *FileDescriptorResponse) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_FileDescriptorResponse.Size(m)
|
||||||
|
}
|
||||||
|
func (m *FileDescriptorResponse) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_FileDescriptorResponse.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_FileDescriptorResponse proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *FileDescriptorResponse) GetFileDescriptorProto() [][]byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.FileDescriptorProto
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// A list of extension numbers sent by the server answering
|
||||||
|
// all_extension_numbers_of_type request.
|
||||||
|
type ExtensionNumberResponse struct {
|
||||||
|
// Full name of the base type, including the package name. The format
|
||||||
|
// is <package>.<type>
|
||||||
|
BaseTypeName string `protobuf:"bytes,1,opt,name=base_type_name,json=baseTypeName,proto3" json:"base_type_name,omitempty"`
|
||||||
|
ExtensionNumber []int32 `protobuf:"varint,2,rep,packed,name=extension_number,json=extensionNumber,proto3" json:"extension_number,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ExtensionNumberResponse) Reset() { *m = ExtensionNumberResponse{} }
|
||||||
|
func (m *ExtensionNumberResponse) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*ExtensionNumberResponse) ProtoMessage() {}
|
||||||
|
func (*ExtensionNumberResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_42a8ac412db3cb03, []int{4}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ExtensionNumberResponse) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_ExtensionNumberResponse.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *ExtensionNumberResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_ExtensionNumberResponse.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *ExtensionNumberResponse) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_ExtensionNumberResponse.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *ExtensionNumberResponse) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_ExtensionNumberResponse.Size(m)
|
||||||
|
}
|
||||||
|
func (m *ExtensionNumberResponse) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_ExtensionNumberResponse.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_ExtensionNumberResponse proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *ExtensionNumberResponse) GetBaseTypeName() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.BaseTypeName
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ExtensionNumberResponse) GetExtensionNumber() []int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.ExtensionNumber
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// A list of ServiceResponse sent by the server answering list_services request.
|
||||||
|
type ListServiceResponse struct {
|
||||||
|
// The information of each service may be expanded in the future, so we use
|
||||||
|
// ServiceResponse message to encapsulate it.
|
||||||
|
Service []*ServiceResponse `protobuf:"bytes,1,rep,name=service,proto3" json:"service,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ListServiceResponse) Reset() { *m = ListServiceResponse{} }
|
||||||
|
func (m *ListServiceResponse) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*ListServiceResponse) ProtoMessage() {}
|
||||||
|
func (*ListServiceResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_42a8ac412db3cb03, []int{5}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ListServiceResponse) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_ListServiceResponse.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *ListServiceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_ListServiceResponse.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *ListServiceResponse) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_ListServiceResponse.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *ListServiceResponse) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_ListServiceResponse.Size(m)
|
||||||
|
}
|
||||||
|
func (m *ListServiceResponse) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_ListServiceResponse.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_ListServiceResponse proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *ListServiceResponse) GetService() []*ServiceResponse {
|
||||||
|
if m != nil {
|
||||||
|
return m.Service
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The information of a single service used by ListServiceResponse to answer
|
||||||
|
// list_services request.
|
||||||
|
type ServiceResponse struct {
|
||||||
|
// Full name of a registered service, including its package name. The format
|
||||||
|
// is <package>.<service>
|
||||||
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServiceResponse) Reset() { *m = ServiceResponse{} }
|
||||||
|
func (m *ServiceResponse) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*ServiceResponse) ProtoMessage() {}
|
||||||
|
func (*ServiceResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_42a8ac412db3cb03, []int{6}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServiceResponse) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_ServiceResponse.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *ServiceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_ServiceResponse.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *ServiceResponse) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_ServiceResponse.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *ServiceResponse) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_ServiceResponse.Size(m)
|
||||||
|
}
|
||||||
|
func (m *ServiceResponse) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_ServiceResponse.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_ServiceResponse proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *ServiceResponse) GetName() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// The error code and error message sent by the server when an error occurs.
|
||||||
|
type ErrorResponse struct {
|
||||||
|
// This field uses the error codes defined in grpc::StatusCode.
|
||||||
|
ErrorCode int32 `protobuf:"varint,1,opt,name=error_code,json=errorCode,proto3" json:"error_code,omitempty"`
|
||||||
|
ErrorMessage string `protobuf:"bytes,2,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ErrorResponse) Reset() { *m = ErrorResponse{} }
|
||||||
|
func (m *ErrorResponse) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*ErrorResponse) ProtoMessage() {}
|
||||||
|
func (*ErrorResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_42a8ac412db3cb03, []int{7}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ErrorResponse) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_ErrorResponse.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *ErrorResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_ErrorResponse.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *ErrorResponse) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_ErrorResponse.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *ErrorResponse) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_ErrorResponse.Size(m)
|
||||||
|
}
|
||||||
|
func (m *ErrorResponse) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_ErrorResponse.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_ErrorResponse proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *ErrorResponse) GetErrorCode() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.ErrorCode
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ErrorResponse) GetErrorMessage() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.ErrorMessage
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*ServerReflectionRequest)(nil), "grpc.reflection.v1alpha.ServerReflectionRequest")
|
||||||
|
proto.RegisterType((*ExtensionRequest)(nil), "grpc.reflection.v1alpha.ExtensionRequest")
|
||||||
|
proto.RegisterType((*ServerReflectionResponse)(nil), "grpc.reflection.v1alpha.ServerReflectionResponse")
|
||||||
|
proto.RegisterType((*FileDescriptorResponse)(nil), "grpc.reflection.v1alpha.FileDescriptorResponse")
|
||||||
|
proto.RegisterType((*ExtensionNumberResponse)(nil), "grpc.reflection.v1alpha.ExtensionNumberResponse")
|
||||||
|
proto.RegisterType((*ListServiceResponse)(nil), "grpc.reflection.v1alpha.ListServiceResponse")
|
||||||
|
proto.RegisterType((*ServiceResponse)(nil), "grpc.reflection.v1alpha.ServiceResponse")
|
||||||
|
proto.RegisterType((*ErrorResponse)(nil), "grpc.reflection.v1alpha.ErrorResponse")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterFile("grpc_reflection_v1alpha/reflection.proto", fileDescriptor_42a8ac412db3cb03)
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileDescriptor_42a8ac412db3cb03 = []byte{
|
||||||
|
// 656 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x51, 0x73, 0xd2, 0x40,
|
||||||
|
0x10, 0x6e, 0x5a, 0x68, 0x87, 0x85, 0x02, 0x5e, 0x2b, 0xa4, 0x3a, 0x75, 0x98, 0x68, 0x35, 0x75,
|
||||||
|
0x1c, 0xda, 0xe2, 0x8c, 0x3f, 0x80, 0xaa, 0x83, 0x33, 0xb5, 0x75, 0x0e, 0x5f, 0x1c, 0x1f, 0x6e,
|
||||||
|
0x02, 0x2c, 0x34, 0x1a, 0x72, 0xf1, 0x2e, 0x45, 0x79, 0xf2, 0x47, 0xf8, 0xa3, 0xfc, 0x4b, 0x3e,
|
||||||
|
0x3a, 0x77, 0x09, 0x21, 0xa4, 0x44, 0xa7, 0x4f, 0x30, 0xdf, 0xee, 0xde, 0xb7, 0xbb, 0xdf, 0xb7,
|
||||||
|
0x01, 0x7b, 0x22, 0x82, 0x21, 0x13, 0x38, 0xf6, 0x70, 0x18, 0xba, 0xdc, 0x67, 0xb3, 0x33, 0xc7,
|
||||||
|
0x0b, 0xae, 0x9d, 0x93, 0x25, 0xd4, 0x0e, 0x04, 0x0f, 0x39, 0x69, 0xaa, 0xcc, 0x76, 0x0a, 0x8e,
|
||||||
|
0x33, 0xad, 0x3f, 0x9b, 0xd0, 0xec, 0xa3, 0x98, 0xa1, 0xa0, 0x49, 0x90, 0xe2, 0xb7, 0x1b, 0x94,
|
||||||
|
0x21, 0x21, 0x50, 0xb8, 0xe6, 0x32, 0x34, 0x8d, 0x96, 0x61, 0x97, 0xa8, 0xfe, 0x4f, 0x9e, 0x43,
|
||||||
|
0x7d, 0xec, 0x7a, 0xc8, 0x06, 0x73, 0xa6, 0x7e, 0x7d, 0x67, 0x8a, 0xe6, 0x96, 0x8a, 0xf7, 0x36,
|
||||||
|
0x68, 0x55, 0x21, 0xdd, 0xf9, 0xdb, 0x18, 0x27, 0xaf, 0xa0, 0xa1, 0x73, 0x87, 0xdc, 0x0f, 0x1d,
|
||||||
|
0xd7, 0x77, 0xfd, 0x09, 0x93, 0xf3, 0xe9, 0x80, 0x7b, 0x66, 0x21, 0xae, 0xd8, 0x57, 0xf1, 0xf3,
|
||||||
|
0x24, 0xdc, 0xd7, 0x51, 0x32, 0x81, 0x83, 0x6c, 0x1d, 0xfe, 0x08, 0xd1, 0x97, 0x2e, 0xf7, 0xcd,
|
||||||
|
0x62, 0xcb, 0xb0, 0xcb, 0x9d, 0xe3, 0x76, 0xce, 0x40, 0xed, 0x37, 0x8b, 0xcc, 0x78, 0x8a, 0xde,
|
||||||
|
0x06, 0x6d, 0xae, 0xb2, 0x24, 0x19, 0xa4, 0x0b, 0x87, 0x8e, 0xe7, 0x2d, 0x1f, 0x67, 0xfe, 0xcd,
|
||||||
|
0x74, 0x80, 0x42, 0x32, 0x3e, 0x66, 0xe1, 0x3c, 0x40, 0x73, 0x3b, 0xee, 0xf3, 0xc0, 0xf1, 0xbc,
|
||||||
|
0xa4, 0xec, 0x32, 0x4a, 0xba, 0x1a, 0x7f, 0x9c, 0x07, 0x48, 0x8e, 0x60, 0xd7, 0x73, 0x65, 0xc8,
|
||||||
|
0x24, 0x8a, 0x99, 0x3b, 0x44, 0x69, 0xee, 0xc4, 0x35, 0x15, 0x05, 0xf7, 0x63, 0xb4, 0x7b, 0x0f,
|
||||||
|
0x6a, 0x53, 0x94, 0xd2, 0x99, 0x20, 0x13, 0x51, 0x63, 0xd6, 0x18, 0xea, 0xd9, 0x66, 0xc9, 0x33,
|
||||||
|
0xa8, 0xa5, 0xa6, 0xd6, 0x3d, 0x44, 0xdb, 0xaf, 0x2e, 0x61, 0x4d, 0x7b, 0x0c, 0xf5, 0x6c, 0xdb,
|
||||||
|
0xe6, 0x66, 0xcb, 0xb0, 0x8b, 0xb4, 0x86, 0xab, 0x8d, 0x5a, 0xbf, 0x0b, 0x60, 0xde, 0x96, 0x58,
|
||||||
|
0x06, 0xdc, 0x97, 0x48, 0x0e, 0x01, 0x66, 0x8e, 0xe7, 0x8e, 0x58, 0x4a, 0xe9, 0x92, 0x46, 0x7a,
|
||||||
|
0x4a, 0xee, 0xcf, 0x50, 0xe7, 0xc2, 0x9d, 0xb8, 0xbe, 0xe3, 0x2d, 0xfa, 0xd6, 0x34, 0xe5, 0xce,
|
||||||
|
0x69, 0xae, 0x02, 0x39, 0x76, 0xa2, 0xb5, 0xc5, 0x4b, 0x8b, 0x61, 0xbf, 0x82, 0xa9, 0x75, 0x1e,
|
||||||
|
0xa1, 0x1c, 0x0a, 0x37, 0x08, 0xb9, 0x60, 0x22, 0xee, 0x4b, 0x3b, 0xa4, 0xdc, 0x39, 0xc9, 0x25,
|
||||||
|
0x51, 0x26, 0x7b, 0x9d, 0xd4, 0x2d, 0xc6, 0xe9, 0x6d, 0x50, 0x6d, 0xb9, 0xdb, 0x11, 0xf2, 0x1d,
|
||||||
|
0x1e, 0xad, 0xd7, 0x3a, 0xa1, 0x2c, 0xfe, 0x67, 0xae, 0x8c, 0x01, 0x52, 0x9c, 0x0f, 0xd7, 0xd8,
|
||||||
|
0x23, 0x21, 0x1e, 0x41, 0x63, 0xc5, 0x20, 0x4b, 0xc2, 0x6d, 0x4d, 0xf8, 0x22, 0x97, 0xf0, 0x62,
|
||||||
|
0x69, 0xa0, 0x14, 0xd9, 0x7e, 0xda, 0x57, 0x09, 0xcb, 0x15, 0x54, 0x51, 0x88, 0xf4, 0x06, 0x77,
|
||||||
|
0xf4, 0xeb, 0x4f, 0xf3, 0xc7, 0x51, 0xe9, 0xa9, 0x77, 0x77, 0x31, 0x0d, 0x74, 0x09, 0xd4, 0x97,
|
||||||
|
0x86, 0x8d, 0x30, 0xeb, 0x02, 0x1a, 0xeb, 0xf7, 0x4e, 0x3a, 0x70, 0x3f, 0x2b, 0xa5, 0xfe, 0xf0,
|
||||||
|
0x98, 0x46, 0x6b, 0xcb, 0xae, 0xd0, 0xbd, 0x55, 0x51, 0x3e, 0xa8, 0x90, 0xf5, 0x05, 0x9a, 0x39,
|
||||||
|
0x2b, 0x25, 0x4f, 0xa0, 0x3a, 0x70, 0x24, 0xea, 0x03, 0x60, 0xfa, 0x1b, 0x13, 0x39, 0xb3, 0xa2,
|
||||||
|
0x50, 0xe5, 0xff, 0x4b, 0xf5, 0x7d, 0x59, 0x7f, 0x03, 0x5b, 0xeb, 0x6e, 0xe0, 0x13, 0xec, 0xad,
|
||||||
|
0xd9, 0x26, 0xe9, 0xc2, 0x4e, 0x2c, 0x8b, 0x6e, 0xb4, 0xdc, 0xb1, 0xff, 0xe9, 0xea, 0x54, 0x29,
|
||||||
|
0x5d, 0x14, 0x5a, 0x47, 0x50, 0xcb, 0x3e, 0x4b, 0xa0, 0x90, 0x6a, 0x5a, 0xff, 0xb7, 0xfa, 0xb0,
|
||||||
|
0xbb, 0xb2, 0x71, 0x75, 0x79, 0x91, 0x62, 0x43, 0x3e, 0x8a, 0x52, 0x8b, 0xb4, 0xa4, 0x91, 0x73,
|
||||||
|
0x3e, 0x42, 0xf2, 0x18, 0x22, 0x41, 0x58, 0xac, 0x82, 0x3e, 0xbb, 0x12, 0xad, 0x68, 0xf0, 0x7d,
|
||||||
|
0x84, 0x75, 0x7e, 0x19, 0x50, 0xcf, 0x9e, 0x1b, 0xf9, 0x09, 0xfb, 0x59, 0xec, 0x9d, 0x3f, 0xe6,
|
||||||
|
0xe4, 0xce, 0x17, 0xfb, 0xe0, 0xec, 0x0e, 0x15, 0xd1, 0x54, 0xb6, 0x71, 0x6a, 0x0c, 0xb6, 0xb5,
|
||||||
|
0xf4, 0x2f, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x85, 0x02, 0x09, 0x9d, 0x9f, 0x06, 0x00, 0x00,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ context.Context
|
||||||
|
var _ grpc.ClientConn
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
const _ = grpc.SupportPackageIsVersion4
|
||||||
|
|
||||||
|
// ServerReflectionClient is the client API for ServerReflection service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||||
|
type ServerReflectionClient interface {
|
||||||
|
// The reflection service is structured as a bidirectional stream, ensuring
|
||||||
|
// all related requests go to a single server.
|
||||||
|
ServerReflectionInfo(ctx context.Context, opts ...grpc.CallOption) (ServerReflection_ServerReflectionInfoClient, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverReflectionClient struct {
|
||||||
|
cc *grpc.ClientConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerReflectionClient(cc *grpc.ClientConn) ServerReflectionClient {
|
||||||
|
return &serverReflectionClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *serverReflectionClient) ServerReflectionInfo(ctx context.Context, opts ...grpc.CallOption) (ServerReflection_ServerReflectionInfoClient, error) {
|
||||||
|
stream, err := c.cc.NewStream(ctx, &_ServerReflection_serviceDesc.Streams[0], "/grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo", opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &serverReflectionServerReflectionInfoClient{stream}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerReflection_ServerReflectionInfoClient interface {
|
||||||
|
Send(*ServerReflectionRequest) error
|
||||||
|
Recv() (*ServerReflectionResponse, error)
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverReflectionServerReflectionInfoClient struct {
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *serverReflectionServerReflectionInfoClient) Send(m *ServerReflectionRequest) error {
|
||||||
|
return x.ClientStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *serverReflectionServerReflectionInfoClient) Recv() (*ServerReflectionResponse, error) {
|
||||||
|
m := new(ServerReflectionResponse)
|
||||||
|
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerReflectionServer is the server API for ServerReflection service.
|
||||||
|
type ServerReflectionServer interface {
|
||||||
|
// The reflection service is structured as a bidirectional stream, ensuring
|
||||||
|
// all related requests go to a single server.
|
||||||
|
ServerReflectionInfo(ServerReflection_ServerReflectionInfoServer) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedServerReflectionServer can be embedded to have forward compatible implementations.
|
||||||
|
type UnimplementedServerReflectionServer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*UnimplementedServerReflectionServer) ServerReflectionInfo(srv ServerReflection_ServerReflectionInfoServer) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method ServerReflectionInfo not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterServerReflectionServer(s *grpc.Server, srv ServerReflectionServer) {
|
||||||
|
s.RegisterService(&_ServerReflection_serviceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _ServerReflection_ServerReflectionInfo_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
return srv.(ServerReflectionServer).ServerReflectionInfo(&serverReflectionServerReflectionInfoServer{stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerReflection_ServerReflectionInfoServer interface {
|
||||||
|
Send(*ServerReflectionResponse) error
|
||||||
|
Recv() (*ServerReflectionRequest, error)
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverReflectionServerReflectionInfoServer struct {
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *serverReflectionServerReflectionInfoServer) Send(m *ServerReflectionResponse) error {
|
||||||
|
return x.ServerStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *serverReflectionServerReflectionInfoServer) Recv() (*ServerReflectionRequest, error) {
|
||||||
|
m := new(ServerReflectionRequest)
|
||||||
|
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ServerReflection_serviceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "grpc.reflection.v1alpha.ServerReflection",
|
||||||
|
HandlerType: (*ServerReflectionServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{},
|
||||||
|
Streams: []grpc.StreamDesc{
|
||||||
|
{
|
||||||
|
StreamName: "ServerReflectionInfo",
|
||||||
|
Handler: _ServerReflection_ServerReflectionInfo_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
ClientStreams: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Metadata: "grpc_reflection_v1alpha/reflection.proto",
|
||||||
|
}
|
136
vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection.proto
generated
vendored
Normal file
136
vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection.proto
generated
vendored
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
// Copyright 2016 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.
|
||||||
|
|
||||||
|
// Service exported by server reflection
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package grpc.reflection.v1alpha;
|
||||||
|
|
||||||
|
service ServerReflection {
|
||||||
|
// The reflection service is structured as a bidirectional stream, ensuring
|
||||||
|
// all related requests go to a single server.
|
||||||
|
rpc ServerReflectionInfo(stream ServerReflectionRequest)
|
||||||
|
returns (stream ServerReflectionResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The message sent by the client when calling ServerReflectionInfo method.
|
||||||
|
message ServerReflectionRequest {
|
||||||
|
string host = 1;
|
||||||
|
// To use reflection service, the client should set one of the following
|
||||||
|
// fields in message_request. The server distinguishes requests by their
|
||||||
|
// defined field and then handles them using corresponding methods.
|
||||||
|
oneof message_request {
|
||||||
|
// Find a proto file by the file name.
|
||||||
|
string file_by_filename = 3;
|
||||||
|
|
||||||
|
// Find the proto file that declares the given fully-qualified symbol name.
|
||||||
|
// This field should be a fully-qualified symbol name
|
||||||
|
// (e.g. <package>.<service>[.<method>] or <package>.<type>).
|
||||||
|
string file_containing_symbol = 4;
|
||||||
|
|
||||||
|
// Find the proto file which defines an extension extending the given
|
||||||
|
// message type with the given field number.
|
||||||
|
ExtensionRequest file_containing_extension = 5;
|
||||||
|
|
||||||
|
// Finds the tag numbers used by all known extensions of extendee_type, and
|
||||||
|
// appends them to ExtensionNumberResponse in an undefined order.
|
||||||
|
// Its corresponding method is best-effort: it's not guaranteed that the
|
||||||
|
// reflection service will implement this method, and it's not guaranteed
|
||||||
|
// that this method will provide all extensions. Returns
|
||||||
|
// StatusCode::UNIMPLEMENTED if it's not implemented.
|
||||||
|
// This field should be a fully-qualified type name. The format is
|
||||||
|
// <package>.<type>
|
||||||
|
string all_extension_numbers_of_type = 6;
|
||||||
|
|
||||||
|
// List the full names of registered services. The content will not be
|
||||||
|
// checked.
|
||||||
|
string list_services = 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The type name and extension number sent by the client when requesting
|
||||||
|
// file_containing_extension.
|
||||||
|
message ExtensionRequest {
|
||||||
|
// Fully-qualified type name. The format should be <package>.<type>
|
||||||
|
string containing_type = 1;
|
||||||
|
int32 extension_number = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The message sent by the server to answer ServerReflectionInfo method.
|
||||||
|
message ServerReflectionResponse {
|
||||||
|
string valid_host = 1;
|
||||||
|
ServerReflectionRequest original_request = 2;
|
||||||
|
// The server sets one of the following fields according to the
|
||||||
|
// message_request in the request.
|
||||||
|
oneof message_response {
|
||||||
|
// This message is used to answer file_by_filename, file_containing_symbol,
|
||||||
|
// file_containing_extension requests with transitive dependencies.
|
||||||
|
// As the repeated label is not allowed in oneof fields, we use a
|
||||||
|
// FileDescriptorResponse message to encapsulate the repeated fields.
|
||||||
|
// The reflection service is allowed to avoid sending FileDescriptorProtos
|
||||||
|
// that were previously sent in response to earlier requests in the stream.
|
||||||
|
FileDescriptorResponse file_descriptor_response = 4;
|
||||||
|
|
||||||
|
// This message is used to answer all_extension_numbers_of_type requests.
|
||||||
|
ExtensionNumberResponse all_extension_numbers_response = 5;
|
||||||
|
|
||||||
|
// This message is used to answer list_services requests.
|
||||||
|
ListServiceResponse list_services_response = 6;
|
||||||
|
|
||||||
|
// This message is used when an error occurs.
|
||||||
|
ErrorResponse error_response = 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialized FileDescriptorProto messages sent by the server answering
|
||||||
|
// a file_by_filename, file_containing_symbol, or file_containing_extension
|
||||||
|
// request.
|
||||||
|
message FileDescriptorResponse {
|
||||||
|
// Serialized FileDescriptorProto messages. We avoid taking a dependency on
|
||||||
|
// descriptor.proto, which uses proto2 only features, by making them opaque
|
||||||
|
// bytes instead.
|
||||||
|
repeated bytes file_descriptor_proto = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A list of extension numbers sent by the server answering
|
||||||
|
// all_extension_numbers_of_type request.
|
||||||
|
message ExtensionNumberResponse {
|
||||||
|
// Full name of the base type, including the package name. The format
|
||||||
|
// is <package>.<type>
|
||||||
|
string base_type_name = 1;
|
||||||
|
repeated int32 extension_number = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A list of ServiceResponse sent by the server answering list_services request.
|
||||||
|
message ListServiceResponse {
|
||||||
|
// The information of each service may be expanded in the future, so we use
|
||||||
|
// ServiceResponse message to encapsulate it.
|
||||||
|
repeated ServiceResponse service = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The information of a single service used by ListServiceResponse to answer
|
||||||
|
// list_services request.
|
||||||
|
message ServiceResponse {
|
||||||
|
// Full name of a registered service, including its package name. The format
|
||||||
|
// is <package>.<service>
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The error code and error message sent by the server when an error occurs.
|
||||||
|
message ErrorResponse {
|
||||||
|
// This field uses the error codes defined in grpc::StatusCode.
|
||||||
|
int32 error_code = 1;
|
||||||
|
string error_message = 2;
|
||||||
|
}
|
|
@ -0,0 +1,454 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2016 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
//go:generate protoc --go_out=plugins=grpc:. grpc_reflection_v1alpha/reflection.proto
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package reflection implements server reflection service.
|
||||||
|
|
||||||
|
The service implemented is defined in:
|
||||||
|
https://github.com/grpc/grpc/blob/master/src/proto/grpc/reflection/v1alpha/reflection.proto.
|
||||||
|
|
||||||
|
To register server reflection on a gRPC server:
|
||||||
|
import "google.golang.org/grpc/reflection"
|
||||||
|
|
||||||
|
s := grpc.NewServer()
|
||||||
|
pb.RegisterYourOwnServer(s, &server{})
|
||||||
|
|
||||||
|
// Register reflection service on gRPC server.
|
||||||
|
reflection.Register(s)
|
||||||
|
|
||||||
|
s.Serve(lis)
|
||||||
|
|
||||||
|
*/
|
||||||
|
package reflection // import "google.golang.org/grpc/reflection"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
rpb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
type serverReflectionServer struct {
|
||||||
|
s *grpc.Server
|
||||||
|
|
||||||
|
initSymbols sync.Once
|
||||||
|
serviceNames []string
|
||||||
|
symbols map[string]*dpb.FileDescriptorProto // map of fully-qualified names to files
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register registers the server reflection service on the given gRPC server.
|
||||||
|
func Register(s *grpc.Server) {
|
||||||
|
rpb.RegisterServerReflectionServer(s, &serverReflectionServer{
|
||||||
|
s: s,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// protoMessage is used for type assertion on proto messages.
|
||||||
|
// Generated proto message implements function Descriptor(), but Descriptor()
|
||||||
|
// is not part of interface proto.Message. This interface is needed to
|
||||||
|
// call Descriptor().
|
||||||
|
type protoMessage interface {
|
||||||
|
Descriptor() ([]byte, []int)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serverReflectionServer) getSymbols() (svcNames []string, symbolIndex map[string]*dpb.FileDescriptorProto) {
|
||||||
|
s.initSymbols.Do(func() {
|
||||||
|
serviceInfo := s.s.GetServiceInfo()
|
||||||
|
|
||||||
|
s.symbols = map[string]*dpb.FileDescriptorProto{}
|
||||||
|
s.serviceNames = make([]string, 0, len(serviceInfo))
|
||||||
|
processed := map[string]struct{}{}
|
||||||
|
for svc, info := range serviceInfo {
|
||||||
|
s.serviceNames = append(s.serviceNames, svc)
|
||||||
|
fdenc, ok := parseMetadata(info.Metadata)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fd, err := decodeFileDesc(fdenc)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.processFile(fd, processed)
|
||||||
|
}
|
||||||
|
sort.Strings(s.serviceNames)
|
||||||
|
})
|
||||||
|
|
||||||
|
return s.serviceNames, s.symbols
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serverReflectionServer) processFile(fd *dpb.FileDescriptorProto, processed map[string]struct{}) {
|
||||||
|
filename := fd.GetName()
|
||||||
|
if _, ok := processed[filename]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
processed[filename] = struct{}{}
|
||||||
|
|
||||||
|
prefix := fd.GetPackage()
|
||||||
|
|
||||||
|
for _, msg := range fd.MessageType {
|
||||||
|
s.processMessage(fd, prefix, msg)
|
||||||
|
}
|
||||||
|
for _, en := range fd.EnumType {
|
||||||
|
s.processEnum(fd, prefix, en)
|
||||||
|
}
|
||||||
|
for _, ext := range fd.Extension {
|
||||||
|
s.processField(fd, prefix, ext)
|
||||||
|
}
|
||||||
|
for _, svc := range fd.Service {
|
||||||
|
svcName := fqn(prefix, svc.GetName())
|
||||||
|
s.symbols[svcName] = fd
|
||||||
|
for _, meth := range svc.Method {
|
||||||
|
name := fqn(svcName, meth.GetName())
|
||||||
|
s.symbols[name] = fd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dep := range fd.Dependency {
|
||||||
|
fdenc := proto.FileDescriptor(dep)
|
||||||
|
fdDep, err := decodeFileDesc(fdenc)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.processFile(fdDep, processed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serverReflectionServer) processMessage(fd *dpb.FileDescriptorProto, prefix string, msg *dpb.DescriptorProto) {
|
||||||
|
msgName := fqn(prefix, msg.GetName())
|
||||||
|
s.symbols[msgName] = fd
|
||||||
|
|
||||||
|
for _, nested := range msg.NestedType {
|
||||||
|
s.processMessage(fd, msgName, nested)
|
||||||
|
}
|
||||||
|
for _, en := range msg.EnumType {
|
||||||
|
s.processEnum(fd, msgName, en)
|
||||||
|
}
|
||||||
|
for _, ext := range msg.Extension {
|
||||||
|
s.processField(fd, msgName, ext)
|
||||||
|
}
|
||||||
|
for _, fld := range msg.Field {
|
||||||
|
s.processField(fd, msgName, fld)
|
||||||
|
}
|
||||||
|
for _, oneof := range msg.OneofDecl {
|
||||||
|
oneofName := fqn(msgName, oneof.GetName())
|
||||||
|
s.symbols[oneofName] = fd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serverReflectionServer) processEnum(fd *dpb.FileDescriptorProto, prefix string, en *dpb.EnumDescriptorProto) {
|
||||||
|
enName := fqn(prefix, en.GetName())
|
||||||
|
s.symbols[enName] = fd
|
||||||
|
|
||||||
|
for _, val := range en.Value {
|
||||||
|
valName := fqn(enName, val.GetName())
|
||||||
|
s.symbols[valName] = fd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serverReflectionServer) processField(fd *dpb.FileDescriptorProto, prefix string, fld *dpb.FieldDescriptorProto) {
|
||||||
|
fldName := fqn(prefix, fld.GetName())
|
||||||
|
s.symbols[fldName] = fd
|
||||||
|
}
|
||||||
|
|
||||||
|
func fqn(prefix, name string) string {
|
||||||
|
if prefix == "" {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
return prefix + "." + name
|
||||||
|
}
|
||||||
|
|
||||||
|
// fileDescForType gets the file descriptor for the given type.
|
||||||
|
// The given type should be a proto message.
|
||||||
|
func (s *serverReflectionServer) fileDescForType(st reflect.Type) (*dpb.FileDescriptorProto, error) {
|
||||||
|
m, ok := reflect.Zero(reflect.PtrTo(st)).Interface().(protoMessage)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("failed to create message from type: %v", st)
|
||||||
|
}
|
||||||
|
enc, _ := m.Descriptor()
|
||||||
|
|
||||||
|
return decodeFileDesc(enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeFileDesc does decompression and unmarshalling on the given
|
||||||
|
// file descriptor byte slice.
|
||||||
|
func decodeFileDesc(enc []byte) (*dpb.FileDescriptorProto, error) {
|
||||||
|
raw, err := decompress(enc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decompress enc: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fd := new(dpb.FileDescriptorProto)
|
||||||
|
if err := proto.Unmarshal(raw, fd); err != nil {
|
||||||
|
return nil, fmt.Errorf("bad descriptor: %v", err)
|
||||||
|
}
|
||||||
|
return fd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// decompress does gzip decompression.
|
||||||
|
func decompress(b []byte) ([]byte, error) {
|
||||||
|
r, err := gzip.NewReader(bytes.NewReader(b))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("bad gzipped descriptor: %v", err)
|
||||||
|
}
|
||||||
|
out, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("bad gzipped descriptor: %v", err)
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func typeForName(name string) (reflect.Type, error) {
|
||||||
|
pt := proto.MessageType(name)
|
||||||
|
if pt == nil {
|
||||||
|
return nil, fmt.Errorf("unknown type: %q", name)
|
||||||
|
}
|
||||||
|
st := pt.Elem()
|
||||||
|
|
||||||
|
return st, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileDescContainingExtension(st reflect.Type, ext int32) (*dpb.FileDescriptorProto, error) {
|
||||||
|
m, ok := reflect.Zero(reflect.PtrTo(st)).Interface().(proto.Message)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("failed to create message from type: %v", st)
|
||||||
|
}
|
||||||
|
|
||||||
|
var extDesc *proto.ExtensionDesc
|
||||||
|
for id, desc := range proto.RegisteredExtensions(m) {
|
||||||
|
if id == ext {
|
||||||
|
extDesc = desc
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if extDesc == nil {
|
||||||
|
return nil, fmt.Errorf("failed to find registered extension for extension number %v", ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodeFileDesc(proto.FileDescriptor(extDesc.Filename))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serverReflectionServer) allExtensionNumbersForType(st reflect.Type) ([]int32, error) {
|
||||||
|
m, ok := reflect.Zero(reflect.PtrTo(st)).Interface().(proto.Message)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("failed to create message from type: %v", st)
|
||||||
|
}
|
||||||
|
|
||||||
|
exts := proto.RegisteredExtensions(m)
|
||||||
|
out := make([]int32, 0, len(exts))
|
||||||
|
for id := range exts {
|
||||||
|
out = append(out, id)
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// fileDescEncodingByFilename finds the file descriptor for given filename,
|
||||||
|
// does marshalling on it and returns the marshalled result.
|
||||||
|
func (s *serverReflectionServer) fileDescEncodingByFilename(name string) ([]byte, error) {
|
||||||
|
enc := proto.FileDescriptor(name)
|
||||||
|
if enc == nil {
|
||||||
|
return nil, fmt.Errorf("unknown file: %v", name)
|
||||||
|
}
|
||||||
|
fd, err := decodeFileDesc(enc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return proto.Marshal(fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseMetadata finds the file descriptor bytes specified meta.
|
||||||
|
// For SupportPackageIsVersion4, m is the name of the proto file, we
|
||||||
|
// call proto.FileDescriptor to get the byte slice.
|
||||||
|
// For SupportPackageIsVersion3, m is a byte slice itself.
|
||||||
|
func parseMetadata(meta interface{}) ([]byte, bool) {
|
||||||
|
// Check if meta is the file name.
|
||||||
|
if fileNameForMeta, ok := meta.(string); ok {
|
||||||
|
return proto.FileDescriptor(fileNameForMeta), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if meta is the byte slice.
|
||||||
|
if enc, ok := meta.([]byte); ok {
|
||||||
|
return enc, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// fileDescEncodingContainingSymbol finds the file descriptor containing the given symbol,
|
||||||
|
// does marshalling on it and returns the marshalled result.
|
||||||
|
// The given symbol can be a type, a service or a method.
|
||||||
|
func (s *serverReflectionServer) fileDescEncodingContainingSymbol(name string) ([]byte, error) {
|
||||||
|
_, symbols := s.getSymbols()
|
||||||
|
fd := symbols[name]
|
||||||
|
if fd == nil {
|
||||||
|
// Check if it's a type name that was not present in the
|
||||||
|
// transitive dependencies of the registered services.
|
||||||
|
if st, err := typeForName(name); err == nil {
|
||||||
|
fd, err = s.fileDescForType(st)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fd == nil {
|
||||||
|
return nil, fmt.Errorf("unknown symbol: %v", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return proto.Marshal(fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fileDescEncodingContainingExtension finds the file descriptor containing given extension,
|
||||||
|
// does marshalling on it and returns the marshalled result.
|
||||||
|
func (s *serverReflectionServer) fileDescEncodingContainingExtension(typeName string, extNum int32) ([]byte, error) {
|
||||||
|
st, err := typeForName(typeName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fd, err := fileDescContainingExtension(st, extNum)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return proto.Marshal(fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// allExtensionNumbersForTypeName returns all extension numbers for the given type.
|
||||||
|
func (s *serverReflectionServer) allExtensionNumbersForTypeName(name string) ([]int32, error) {
|
||||||
|
st, err := typeForName(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
extNums, err := s.allExtensionNumbersForType(st)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return extNums, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerReflectionInfo is the reflection service handler.
|
||||||
|
func (s *serverReflectionServer) ServerReflectionInfo(stream rpb.ServerReflection_ServerReflectionInfoServer) error {
|
||||||
|
for {
|
||||||
|
in, err := stream.Recv()
|
||||||
|
if err == io.EOF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
out := &rpb.ServerReflectionResponse{
|
||||||
|
ValidHost: in.Host,
|
||||||
|
OriginalRequest: in,
|
||||||
|
}
|
||||||
|
switch req := in.MessageRequest.(type) {
|
||||||
|
case *rpb.ServerReflectionRequest_FileByFilename:
|
||||||
|
b, err := s.fileDescEncodingByFilename(req.FileByFilename)
|
||||||
|
if err != nil {
|
||||||
|
out.MessageResponse = &rpb.ServerReflectionResponse_ErrorResponse{
|
||||||
|
ErrorResponse: &rpb.ErrorResponse{
|
||||||
|
ErrorCode: int32(codes.NotFound),
|
||||||
|
ErrorMessage: err.Error(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.MessageResponse = &rpb.ServerReflectionResponse_FileDescriptorResponse{
|
||||||
|
FileDescriptorResponse: &rpb.FileDescriptorResponse{FileDescriptorProto: [][]byte{b}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *rpb.ServerReflectionRequest_FileContainingSymbol:
|
||||||
|
b, err := s.fileDescEncodingContainingSymbol(req.FileContainingSymbol)
|
||||||
|
if err != nil {
|
||||||
|
out.MessageResponse = &rpb.ServerReflectionResponse_ErrorResponse{
|
||||||
|
ErrorResponse: &rpb.ErrorResponse{
|
||||||
|
ErrorCode: int32(codes.NotFound),
|
||||||
|
ErrorMessage: err.Error(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.MessageResponse = &rpb.ServerReflectionResponse_FileDescriptorResponse{
|
||||||
|
FileDescriptorResponse: &rpb.FileDescriptorResponse{FileDescriptorProto: [][]byte{b}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *rpb.ServerReflectionRequest_FileContainingExtension:
|
||||||
|
typeName := req.FileContainingExtension.ContainingType
|
||||||
|
extNum := req.FileContainingExtension.ExtensionNumber
|
||||||
|
b, err := s.fileDescEncodingContainingExtension(typeName, extNum)
|
||||||
|
if err != nil {
|
||||||
|
out.MessageResponse = &rpb.ServerReflectionResponse_ErrorResponse{
|
||||||
|
ErrorResponse: &rpb.ErrorResponse{
|
||||||
|
ErrorCode: int32(codes.NotFound),
|
||||||
|
ErrorMessage: err.Error(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.MessageResponse = &rpb.ServerReflectionResponse_FileDescriptorResponse{
|
||||||
|
FileDescriptorResponse: &rpb.FileDescriptorResponse{FileDescriptorProto: [][]byte{b}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *rpb.ServerReflectionRequest_AllExtensionNumbersOfType:
|
||||||
|
extNums, err := s.allExtensionNumbersForTypeName(req.AllExtensionNumbersOfType)
|
||||||
|
if err != nil {
|
||||||
|
out.MessageResponse = &rpb.ServerReflectionResponse_ErrorResponse{
|
||||||
|
ErrorResponse: &rpb.ErrorResponse{
|
||||||
|
ErrorCode: int32(codes.NotFound),
|
||||||
|
ErrorMessage: err.Error(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.MessageResponse = &rpb.ServerReflectionResponse_AllExtensionNumbersResponse{
|
||||||
|
AllExtensionNumbersResponse: &rpb.ExtensionNumberResponse{
|
||||||
|
BaseTypeName: req.AllExtensionNumbersOfType,
|
||||||
|
ExtensionNumber: extNums,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *rpb.ServerReflectionRequest_ListServices:
|
||||||
|
svcNames, _ := s.getSymbols()
|
||||||
|
serviceResponses := make([]*rpb.ServiceResponse, len(svcNames))
|
||||||
|
for i, n := range svcNames {
|
||||||
|
serviceResponses[i] = &rpb.ServiceResponse{
|
||||||
|
Name: n,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.MessageResponse = &rpb.ServerReflectionResponse_ListServicesResponse{
|
||||||
|
ListServicesResponse: &rpb.ListServiceResponse{
|
||||||
|
Service: serviceResponses,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return status.Errorf(codes.InvalidArgument, "invalid MessageRequest: %v", in.MessageRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := stream.Send(out); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,15 @@
|
||||||
// All APIs in this package are experimental.
|
// All APIs in this package are experimental.
|
||||||
package resolver
|
package resolver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/attributes"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
"google.golang.org/grpc/serviceconfig"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// m is a map from scheme to resolver builder.
|
// m is a map from scheme to resolver builder.
|
||||||
m = make(map[string]Builder)
|
m = make(map[string]Builder)
|
||||||
|
@ -65,12 +74,18 @@ func GetDefaultScheme() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddressType indicates the address type returned by name resolution.
|
// AddressType indicates the address type returned by name resolution.
|
||||||
|
//
|
||||||
|
// Deprecated: use Attributes in Address instead.
|
||||||
type AddressType uint8
|
type AddressType uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Backend indicates the address is for a backend server.
|
// Backend indicates the address is for a backend server.
|
||||||
|
//
|
||||||
|
// Deprecated: use Attributes in Address instead.
|
||||||
Backend AddressType = iota
|
Backend AddressType = iota
|
||||||
// GRPCLB indicates the address is for a grpclb load balancer.
|
// GRPCLB indicates the address is for a grpclb load balancer.
|
||||||
|
//
|
||||||
|
// Deprecated: use Attributes in Address instead.
|
||||||
GRPCLB
|
GRPCLB
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -79,32 +94,75 @@ const (
|
||||||
type Address struct {
|
type Address struct {
|
||||||
// Addr is the server address on which a connection will be established.
|
// Addr is the server address on which a connection will be established.
|
||||||
Addr string
|
Addr string
|
||||||
// Type is the type of this address.
|
|
||||||
Type AddressType
|
|
||||||
// ServerName is the name of this address.
|
// ServerName is the name of this address.
|
||||||
|
// If non-empty, the ServerName is used as the transport certification authority for
|
||||||
|
// the address, instead of the hostname from the Dial target string. In most cases,
|
||||||
|
// this should not be set.
|
||||||
//
|
//
|
||||||
// e.g. if Type is GRPCLB, ServerName should be the name of the remote load
|
// If Type is GRPCLB, ServerName should be the name of the remote load
|
||||||
// balancer, not the name of the backend.
|
// balancer, not the name of the backend.
|
||||||
|
//
|
||||||
|
// WARNING: ServerName must only be populated with trusted values. It
|
||||||
|
// is insecure to populate it with data from untrusted inputs since untrusted
|
||||||
|
// values could be used to bypass the authority checks performed by TLS.
|
||||||
ServerName string
|
ServerName string
|
||||||
|
|
||||||
|
// Attributes contains arbitrary data about this address intended for
|
||||||
|
// consumption by the load balancing policy.
|
||||||
|
Attributes *attributes.Attributes
|
||||||
|
|
||||||
|
// Type is the type of this address.
|
||||||
|
//
|
||||||
|
// Deprecated: use Attributes instead.
|
||||||
|
Type AddressType
|
||||||
|
|
||||||
// Metadata is the information associated with Addr, which may be used
|
// Metadata is the information associated with Addr, which may be used
|
||||||
// to make load balancing decision.
|
// to make load balancing decision.
|
||||||
|
//
|
||||||
|
// Deprecated: use Attributes instead.
|
||||||
Metadata interface{}
|
Metadata interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildOption includes additional information for the builder to create
|
// BuildOptions includes additional information for the builder to create
|
||||||
// the resolver.
|
// the resolver.
|
||||||
type BuildOption struct {
|
type BuildOptions struct {
|
||||||
// DisableServiceConfig indicates whether resolver should fetch service config data.
|
// DisableServiceConfig indicates whether a resolver implementation should
|
||||||
|
// fetch service config data.
|
||||||
DisableServiceConfig bool
|
DisableServiceConfig bool
|
||||||
|
// DialCreds is the transport credentials used by the ClientConn for
|
||||||
|
// communicating with the target gRPC service (set via
|
||||||
|
// WithTransportCredentials). In cases where a name resolution service
|
||||||
|
// requires the same credentials, the resolver may use this field. In most
|
||||||
|
// cases though, it is not appropriate, and this field may be ignored.
|
||||||
|
DialCreds credentials.TransportCredentials
|
||||||
|
// CredsBundle is the credentials bundle used by the ClientConn for
|
||||||
|
// communicating with the target gRPC service (set via
|
||||||
|
// WithCredentialsBundle). In cases where a name resolution service
|
||||||
|
// requires the same credentials, the resolver may use this field. In most
|
||||||
|
// cases though, it is not appropriate, and this field may be ignored.
|
||||||
|
CredsBundle credentials.Bundle
|
||||||
|
// Dialer is the custom dialer used by the ClientConn for dialling the
|
||||||
|
// target gRPC service (set via WithDialer). In cases where a name
|
||||||
|
// resolution service requires the same dialer, the resolver may use this
|
||||||
|
// field. In most cases though, it is not appropriate, and this field may
|
||||||
|
// be ignored.
|
||||||
|
Dialer func(context.Context, string) (net.Conn, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// State contains the current Resolver state relevant to the ClientConn.
|
// State contains the current Resolver state relevant to the ClientConn.
|
||||||
type State struct {
|
type State struct {
|
||||||
Addresses []Address // Resolved addresses for the target
|
// Addresses is the latest set of resolved addresses for the target.
|
||||||
ServiceConfig string // JSON representation of the service config
|
Addresses []Address
|
||||||
|
|
||||||
// TODO: add Err error
|
// ServiceConfig contains the result from parsing the latest service
|
||||||
// TODO: add ParsedServiceConfig interface{}
|
// config. If it is nil, it indicates no service config is present or the
|
||||||
|
// resolver does not provide service configs.
|
||||||
|
ServiceConfig *serviceconfig.ParseResult
|
||||||
|
|
||||||
|
// Attributes contains arbitrary data about the resolver intended for
|
||||||
|
// consumption by the load balancing policy.
|
||||||
|
Attributes *attributes.Attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientConn contains the callbacks for resolver to notify any updates
|
// ClientConn contains the callbacks for resolver to notify any updates
|
||||||
|
@ -117,6 +175,10 @@ type State struct {
|
||||||
type ClientConn interface {
|
type ClientConn interface {
|
||||||
// UpdateState updates the state of the ClientConn appropriately.
|
// UpdateState updates the state of the ClientConn appropriately.
|
||||||
UpdateState(State)
|
UpdateState(State)
|
||||||
|
// ReportError notifies the ClientConn that the Resolver encountered an
|
||||||
|
// error. The ClientConn will notify the load balancer and begin calling
|
||||||
|
// ResolveNow on the Resolver with exponential backoff.
|
||||||
|
ReportError(error)
|
||||||
// NewAddress is called by resolver to notify ClientConn a new list
|
// NewAddress is called by resolver to notify ClientConn a new list
|
||||||
// of resolved addresses.
|
// of resolved addresses.
|
||||||
// The address list should be the complete list of resolved addresses.
|
// The address list should be the complete list of resolved addresses.
|
||||||
|
@ -128,6 +190,9 @@ type ClientConn interface {
|
||||||
//
|
//
|
||||||
// Deprecated: Use UpdateState instead.
|
// Deprecated: Use UpdateState instead.
|
||||||
NewServiceConfig(serviceConfig string)
|
NewServiceConfig(serviceConfig string)
|
||||||
|
// ParseServiceConfig parses the provided service config and returns an
|
||||||
|
// object that provides the parsed config.
|
||||||
|
ParseServiceConfig(serviceConfigJSON string) *serviceconfig.ParseResult
|
||||||
}
|
}
|
||||||
|
|
||||||
// Target represents a target for gRPC, as specified in:
|
// Target represents a target for gRPC, as specified in:
|
||||||
|
@ -159,14 +224,14 @@ type Builder interface {
|
||||||
//
|
//
|
||||||
// gRPC dial calls Build synchronously, and fails if the returned error is
|
// gRPC dial calls Build synchronously, and fails if the returned error is
|
||||||
// not nil.
|
// not nil.
|
||||||
Build(target Target, cc ClientConn, opts BuildOption) (Resolver, error)
|
Build(target Target, cc ClientConn, opts BuildOptions) (Resolver, error)
|
||||||
// Scheme returns the scheme supported by this resolver.
|
// Scheme returns the scheme supported by this resolver.
|
||||||
// Scheme is defined at https://github.com/grpc/grpc/blob/master/doc/naming.md.
|
// Scheme is defined at https://github.com/grpc/grpc/blob/master/doc/naming.md.
|
||||||
Scheme() string
|
Scheme() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveNowOption includes additional information for ResolveNow.
|
// ResolveNowOptions includes additional information for ResolveNow.
|
||||||
type ResolveNowOption struct{}
|
type ResolveNowOptions struct{}
|
||||||
|
|
||||||
// Resolver watches for the updates on the specified target.
|
// Resolver watches for the updates on the specified target.
|
||||||
// Updates include address updates and service config updates.
|
// Updates include address updates and service config updates.
|
||||||
|
@ -175,7 +240,7 @@ type Resolver interface {
|
||||||
// again. It's just a hint, resolver can ignore this if it's not necessary.
|
// again. It's just a hint, resolver can ignore this if it's not necessary.
|
||||||
//
|
//
|
||||||
// It could be called multiple times concurrently.
|
// It could be called multiple times concurrently.
|
||||||
ResolveNow(ResolveNowOption)
|
ResolveNow(ResolveNowOptions)
|
||||||
// Close closes the resolver.
|
// Close closes the resolver.
|
||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,22 +21,29 @@ package grpc
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/balancer"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
"google.golang.org/grpc/internal/channelz"
|
"google.golang.org/grpc/internal/channelz"
|
||||||
|
"google.golang.org/grpc/internal/grpcsync"
|
||||||
"google.golang.org/grpc/resolver"
|
"google.golang.org/grpc/resolver"
|
||||||
|
"google.golang.org/grpc/serviceconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ccResolverWrapper is a wrapper on top of cc for resolvers.
|
// ccResolverWrapper is a wrapper on top of cc for resolvers.
|
||||||
// It implements resolver.ClientConnection interface.
|
// It implements resolver.ClientConn interface.
|
||||||
type ccResolverWrapper struct {
|
type ccResolverWrapper struct {
|
||||||
cc *ClientConn
|
cc *ClientConn
|
||||||
|
resolverMu sync.Mutex
|
||||||
resolver resolver.Resolver
|
resolver resolver.Resolver
|
||||||
addrCh chan []resolver.Address
|
done *grpcsync.Event
|
||||||
scCh chan string
|
|
||||||
done uint32 // accessed atomically; set to 1 when closed.
|
|
||||||
curState resolver.State
|
curState resolver.State
|
||||||
|
|
||||||
|
pollingMu sync.Mutex
|
||||||
|
polling chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// split2 returns the values from strings.SplitN(s, sep, 2).
|
// split2 returns the values from strings.SplitN(s, sep, 2).
|
||||||
|
@ -67,60 +74,126 @@ func parseTarget(target string) (ret resolver.Target) {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// newCCResolverWrapper parses cc.target for scheme and gets the resolver
|
// newCCResolverWrapper uses the resolver.Builder to build a Resolver and
|
||||||
// builder for this scheme and builds the resolver. The monitoring goroutine
|
// returns a ccResolverWrapper object which wraps the newly built resolver.
|
||||||
// for it is not started yet and can be created by calling start().
|
func newCCResolverWrapper(cc *ClientConn, rb resolver.Builder) (*ccResolverWrapper, error) {
|
||||||
//
|
|
||||||
// If withResolverBuilder dial option is set, the specified resolver will be
|
|
||||||
// used instead.
|
|
||||||
func newCCResolverWrapper(cc *ClientConn) (*ccResolverWrapper, error) {
|
|
||||||
rb := cc.dopts.resolverBuilder
|
|
||||||
if rb == nil {
|
|
||||||
return nil, fmt.Errorf("could not get resolver for scheme: %q", cc.parsedTarget.Scheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
ccr := &ccResolverWrapper{
|
ccr := &ccResolverWrapper{
|
||||||
cc: cc,
|
cc: cc,
|
||||||
addrCh: make(chan []resolver.Address, 1),
|
done: grpcsync.NewEvent(),
|
||||||
scCh: make(chan string, 1),
|
}
|
||||||
|
|
||||||
|
var credsClone credentials.TransportCredentials
|
||||||
|
if creds := cc.dopts.copts.TransportCredentials; creds != nil {
|
||||||
|
credsClone = creds.Clone()
|
||||||
|
}
|
||||||
|
rbo := resolver.BuildOptions{
|
||||||
|
DisableServiceConfig: cc.dopts.disableServiceConfig,
|
||||||
|
DialCreds: credsClone,
|
||||||
|
CredsBundle: cc.dopts.copts.CredsBundle,
|
||||||
|
Dialer: cc.dopts.copts.Dialer,
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
ccr.resolver, err = rb.Build(cc.parsedTarget, ccr, resolver.BuildOption{DisableServiceConfig: cc.dopts.disableServiceConfig})
|
// We need to hold the lock here while we assign to the ccr.resolver field
|
||||||
|
// to guard against a data race caused by the following code path,
|
||||||
|
// rb.Build-->ccr.ReportError-->ccr.poll-->ccr.resolveNow, would end up
|
||||||
|
// accessing ccr.resolver which is being assigned here.
|
||||||
|
ccr.resolverMu.Lock()
|
||||||
|
defer ccr.resolverMu.Unlock()
|
||||||
|
ccr.resolver, err = rb.Build(cc.parsedTarget, ccr, rbo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return ccr, nil
|
return ccr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ccr *ccResolverWrapper) resolveNow(o resolver.ResolveNowOption) {
|
func (ccr *ccResolverWrapper) resolveNow(o resolver.ResolveNowOptions) {
|
||||||
|
ccr.resolverMu.Lock()
|
||||||
|
if !ccr.done.HasFired() {
|
||||||
ccr.resolver.ResolveNow(o)
|
ccr.resolver.ResolveNow(o)
|
||||||
}
|
}
|
||||||
|
ccr.resolverMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
func (ccr *ccResolverWrapper) close() {
|
func (ccr *ccResolverWrapper) close() {
|
||||||
|
ccr.resolverMu.Lock()
|
||||||
ccr.resolver.Close()
|
ccr.resolver.Close()
|
||||||
atomic.StoreUint32(&ccr.done, 1)
|
ccr.done.Fire()
|
||||||
|
ccr.resolverMu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ccr *ccResolverWrapper) isDone() bool {
|
// poll begins or ends asynchronous polling of the resolver based on whether
|
||||||
return atomic.LoadUint32(&ccr.done) == 1
|
// err is ErrBadResolverState.
|
||||||
|
func (ccr *ccResolverWrapper) poll(err error) {
|
||||||
|
ccr.pollingMu.Lock()
|
||||||
|
defer ccr.pollingMu.Unlock()
|
||||||
|
if err != balancer.ErrBadResolverState {
|
||||||
|
// stop polling
|
||||||
|
if ccr.polling != nil {
|
||||||
|
close(ccr.polling)
|
||||||
|
ccr.polling = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ccr.polling != nil {
|
||||||
|
// already polling
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p := make(chan struct{})
|
||||||
|
ccr.polling = p
|
||||||
|
go func() {
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
ccr.resolveNow(resolver.ResolveNowOptions{})
|
||||||
|
t := time.NewTimer(ccr.cc.dopts.resolveNowBackoff(i))
|
||||||
|
select {
|
||||||
|
case <-p:
|
||||||
|
t.Stop()
|
||||||
|
return
|
||||||
|
case <-ccr.done.Done():
|
||||||
|
// Resolver has been closed.
|
||||||
|
t.Stop()
|
||||||
|
return
|
||||||
|
case <-t.C:
|
||||||
|
select {
|
||||||
|
case <-p:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
// Timer expired; re-resolve.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ccr *ccResolverWrapper) UpdateState(s resolver.State) {
|
func (ccr *ccResolverWrapper) UpdateState(s resolver.State) {
|
||||||
if ccr.isDone() {
|
if ccr.done.HasFired() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
grpclog.Infof("ccResolverWrapper: sending update to cc: %v", s)
|
grpclog.Infof("ccResolverWrapper: sending update to cc: %v", s)
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
ccr.addChannelzTraceEvent(s)
|
ccr.addChannelzTraceEvent(s)
|
||||||
}
|
}
|
||||||
ccr.cc.updateResolverState(s)
|
|
||||||
ccr.curState = s
|
ccr.curState = s
|
||||||
|
ccr.poll(ccr.cc.updateResolverState(ccr.curState, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ccr *ccResolverWrapper) ReportError(err error) {
|
||||||
|
if ccr.done.HasFired() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
grpclog.Warningf("ccResolverWrapper: reporting error to cc: %v", err)
|
||||||
|
if channelz.IsOn() {
|
||||||
|
channelz.AddTraceEvent(ccr.cc.channelzID, &channelz.TraceEventDesc{
|
||||||
|
Desc: fmt.Sprintf("Resolver reported error: %v", err),
|
||||||
|
Severity: channelz.CtWarning,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ccr.poll(ccr.cc.updateResolverState(resolver.State{}, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAddress is called by the resolver implementation to send addresses to gRPC.
|
// NewAddress is called by the resolver implementation to send addresses to gRPC.
|
||||||
func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
|
func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
|
||||||
if ccr.isDone() {
|
if ccr.done.HasFired() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
grpclog.Infof("ccResolverWrapper: sending new addresses to cc: %v", addrs)
|
grpclog.Infof("ccResolverWrapper: sending new addresses to cc: %v", addrs)
|
||||||
|
@ -128,29 +201,54 @@ func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
|
||||||
ccr.addChannelzTraceEvent(resolver.State{Addresses: addrs, ServiceConfig: ccr.curState.ServiceConfig})
|
ccr.addChannelzTraceEvent(resolver.State{Addresses: addrs, ServiceConfig: ccr.curState.ServiceConfig})
|
||||||
}
|
}
|
||||||
ccr.curState.Addresses = addrs
|
ccr.curState.Addresses = addrs
|
||||||
ccr.cc.updateResolverState(ccr.curState)
|
ccr.poll(ccr.cc.updateResolverState(ccr.curState, nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServiceConfig is called by the resolver implementation to send service
|
// NewServiceConfig is called by the resolver implementation to send service
|
||||||
// configs to gRPC.
|
// configs to gRPC.
|
||||||
func (ccr *ccResolverWrapper) NewServiceConfig(sc string) {
|
func (ccr *ccResolverWrapper) NewServiceConfig(sc string) {
|
||||||
if ccr.isDone() {
|
if ccr.done.HasFired() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
grpclog.Infof("ccResolverWrapper: got new service config: %v", sc)
|
grpclog.Infof("ccResolverWrapper: got new service config: %v", sc)
|
||||||
if channelz.IsOn() {
|
if ccr.cc.dopts.disableServiceConfig {
|
||||||
ccr.addChannelzTraceEvent(resolver.State{Addresses: ccr.curState.Addresses, ServiceConfig: sc})
|
grpclog.Infof("Service config lookups disabled; ignoring config")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
ccr.curState.ServiceConfig = sc
|
scpr := parseServiceConfig(sc)
|
||||||
ccr.cc.updateResolverState(ccr.curState)
|
if scpr.Err != nil {
|
||||||
|
grpclog.Warningf("ccResolverWrapper: error parsing service config: %v", scpr.Err)
|
||||||
|
if channelz.IsOn() {
|
||||||
|
channelz.AddTraceEvent(ccr.cc.channelzID, &channelz.TraceEventDesc{
|
||||||
|
Desc: fmt.Sprintf("Error parsing service config: %v", scpr.Err),
|
||||||
|
Severity: channelz.CtWarning,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ccr.poll(balancer.ErrBadResolverState)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if channelz.IsOn() {
|
||||||
|
ccr.addChannelzTraceEvent(resolver.State{Addresses: ccr.curState.Addresses, ServiceConfig: scpr})
|
||||||
|
}
|
||||||
|
ccr.curState.ServiceConfig = scpr
|
||||||
|
ccr.poll(ccr.cc.updateResolverState(ccr.curState, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ccr *ccResolverWrapper) ParseServiceConfig(scJSON string) *serviceconfig.ParseResult {
|
||||||
|
return parseServiceConfig(scJSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ccr *ccResolverWrapper) addChannelzTraceEvent(s resolver.State) {
|
func (ccr *ccResolverWrapper) addChannelzTraceEvent(s resolver.State) {
|
||||||
if s.ServiceConfig == ccr.curState.ServiceConfig && (len(ccr.curState.Addresses) == 0) == (len(s.Addresses) == 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var updates []string
|
var updates []string
|
||||||
if s.ServiceConfig != ccr.curState.ServiceConfig {
|
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")
|
updates = append(updates, "service config updated")
|
||||||
}
|
}
|
||||||
if len(ccr.curState.Addresses) > 0 && len(s.Addresses) == 0 {
|
if len(ccr.curState.Addresses) > 0 && len(s.Addresses) == 0 {
|
||||||
|
|
|
@ -648,33 +648,56 @@ func recvAndDecompress(p *parser, s *transport.Stream, dc Decompressor, maxRecei
|
||||||
return nil, st.Err()
|
return nil, st.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var size int
|
||||||
if pf == compressionMade {
|
if pf == compressionMade {
|
||||||
// To match legacy behavior, if the decompressor is set by WithDecompressor or RPCDecompressor,
|
// To match legacy behavior, if the decompressor is set by WithDecompressor or RPCDecompressor,
|
||||||
// use this decompressor as the default.
|
// use this decompressor as the default.
|
||||||
if dc != nil {
|
if dc != nil {
|
||||||
d, err = dc.Do(bytes.NewReader(d))
|
d, err = dc.Do(bytes.NewReader(d))
|
||||||
|
size = len(d)
|
||||||
|
} else {
|
||||||
|
d, size, err = decompress(compressor, d, maxReceiveMessageSize)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the received message %v", err)
|
return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the received message %v", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
size = len(d)
|
||||||
|
}
|
||||||
|
if size > maxReceiveMessageSize {
|
||||||
|
// TODO: Revisit the error code. Currently keep it consistent with java
|
||||||
|
// implementation.
|
||||||
|
return nil, status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max (%d vs. %d)", size, maxReceiveMessageSize)
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using compressor, decompress d, returning data and size.
|
||||||
|
// Optionally, if data will be over maxReceiveMessageSize, just return the size.
|
||||||
|
func decompress(compressor encoding.Compressor, d []byte, maxReceiveMessageSize int) ([]byte, int, error) {
|
||||||
dcReader, err := compressor.Decompress(bytes.NewReader(d))
|
dcReader, err := compressor.Decompress(bytes.NewReader(d))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the received message %v", err)
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
if sizer, ok := compressor.(interface {
|
||||||
|
DecompressedSize(compressedBytes []byte) int
|
||||||
|
}); ok {
|
||||||
|
if size := sizer.DecompressedSize(d); size >= 0 {
|
||||||
|
if size > maxReceiveMessageSize {
|
||||||
|
return nil, size, nil
|
||||||
|
}
|
||||||
|
// size is used as an estimate to size the buffer, but we
|
||||||
|
// will read more data if available.
|
||||||
|
// +MinRead so ReadFrom will not reallocate if size is correct.
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, size+bytes.MinRead))
|
||||||
|
bytesRead, err := buf.ReadFrom(io.LimitReader(dcReader, int64(maxReceiveMessageSize)+1))
|
||||||
|
return buf.Bytes(), int(bytesRead), err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Read from LimitReader with limit max+1. So if the underlying
|
// Read from LimitReader with limit max+1. So if the underlying
|
||||||
// reader is over limit, the result will be bigger than max.
|
// reader is over limit, the result will be bigger than max.
|
||||||
d, err = ioutil.ReadAll(io.LimitReader(dcReader, int64(maxReceiveMessageSize)+1))
|
d, err = ioutil.ReadAll(io.LimitReader(dcReader, int64(maxReceiveMessageSize)+1))
|
||||||
if err != nil {
|
return d, len(d), err
|
||||||
return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the received message %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(d) > maxReceiveMessageSize {
|
|
||||||
// TODO: Revisit the error code. Currently keep it consistent with java
|
|
||||||
// implementation.
|
|
||||||
return nil, status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max (%d vs. %d)", len(d), maxReceiveMessageSize)
|
|
||||||
}
|
|
||||||
return d, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For the two compressor parameters, both should not be set, but if they are,
|
// For the two compressor parameters, both should not be set, but if they are,
|
||||||
|
@ -848,7 +871,7 @@ type channelzData struct {
|
||||||
|
|
||||||
// The SupportPackageIsVersion variables are referenced from generated protocol
|
// The SupportPackageIsVersion variables are referenced from generated protocol
|
||||||
// buffer files to ensure compatibility with the gRPC version used. The latest
|
// buffer files to ensure compatibility with the gRPC version used. The latest
|
||||||
// support package version is 5.
|
// support package version is 6.
|
||||||
//
|
//
|
||||||
// Older versions are kept for compatibility. They may be removed if
|
// Older versions are kept for compatibility. They may be removed if
|
||||||
// compatibility cannot be maintained.
|
// compatibility cannot be maintained.
|
||||||
|
@ -858,6 +881,7 @@ const (
|
||||||
SupportPackageIsVersion3 = true
|
SupportPackageIsVersion3 = true
|
||||||
SupportPackageIsVersion4 = true
|
SupportPackageIsVersion4 = true
|
||||||
SupportPackageIsVersion5 = true
|
SupportPackageIsVersion5 = true
|
||||||
|
SupportPackageIsVersion6 = true
|
||||||
)
|
)
|
||||||
|
|
||||||
const grpcUA = "grpc-go/" + Version
|
const grpcUA = "grpc-go/" + Version
|
||||||
|
|
|
@ -42,6 +42,7 @@ import (
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
"google.golang.org/grpc/internal/binarylog"
|
"google.golang.org/grpc/internal/binarylog"
|
||||||
"google.golang.org/grpc/internal/channelz"
|
"google.golang.org/grpc/internal/channelz"
|
||||||
|
"google.golang.org/grpc/internal/grpcsync"
|
||||||
"google.golang.org/grpc/internal/transport"
|
"google.golang.org/grpc/internal/transport"
|
||||||
"google.golang.org/grpc/keepalive"
|
"google.golang.org/grpc/keepalive"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
@ -56,6 +57,8 @@ const (
|
||||||
defaultServerMaxSendMessageSize = math.MaxInt32
|
defaultServerMaxSendMessageSize = math.MaxInt32
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var statusOK = status.New(codes.OK, "")
|
||||||
|
|
||||||
type methodHandler func(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor UnaryServerInterceptor) (interface{}, error)
|
type methodHandler func(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor UnaryServerInterceptor) (interface{}, error)
|
||||||
|
|
||||||
// MethodDesc represents an RPC service's method specification.
|
// MethodDesc represents an RPC service's method specification.
|
||||||
|
@ -90,17 +93,15 @@ type Server struct {
|
||||||
|
|
||||||
mu sync.Mutex // guards following
|
mu sync.Mutex // guards following
|
||||||
lis map[net.Listener]bool
|
lis map[net.Listener]bool
|
||||||
conns map[io.Closer]bool
|
conns map[transport.ServerTransport]bool
|
||||||
serve bool
|
serve bool
|
||||||
drain bool
|
drain bool
|
||||||
cv *sync.Cond // signaled when connections close for GracefulStop
|
cv *sync.Cond // signaled when connections close for GracefulStop
|
||||||
m map[string]*service // service name -> service info
|
m map[string]*service // service name -> service info
|
||||||
events trace.EventLog
|
events trace.EventLog
|
||||||
|
|
||||||
quit chan struct{}
|
quit *grpcsync.Event
|
||||||
done chan struct{}
|
done *grpcsync.Event
|
||||||
quitOnce sync.Once
|
|
||||||
doneOnce sync.Once
|
|
||||||
channelzRemoveOnce sync.Once
|
channelzRemoveOnce sync.Once
|
||||||
serveWG sync.WaitGroup // counts active Serve goroutines for GracefulStop
|
serveWG sync.WaitGroup // counts active Serve goroutines for GracefulStop
|
||||||
|
|
||||||
|
@ -129,6 +130,7 @@ type serverOptions struct {
|
||||||
readBufferSize int
|
readBufferSize int
|
||||||
connectionTimeout time.Duration
|
connectionTimeout time.Duration
|
||||||
maxHeaderListSize *uint32
|
maxHeaderListSize *uint32
|
||||||
|
headerTableSize *uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultServerOptions = serverOptions{
|
var defaultServerOptions = serverOptions{
|
||||||
|
@ -342,8 +344,8 @@ func StatsHandler(h stats.Handler) ServerOption {
|
||||||
// unknown service handler. The provided method is a bidi-streaming RPC service
|
// unknown service handler. The provided method is a bidi-streaming RPC service
|
||||||
// handler that will be invoked instead of returning the "unimplemented" gRPC
|
// handler that will be invoked instead of returning the "unimplemented" gRPC
|
||||||
// error whenever a request is received for an unregistered service or method.
|
// error whenever a request is received for an unregistered service or method.
|
||||||
// The handling function has full access to the Context of the request and the
|
// The handling function and stream interceptor (if set) have full access to
|
||||||
// stream, and the invocation bypasses interceptors.
|
// the ServerStream, including its Context.
|
||||||
func UnknownServiceHandler(streamHandler StreamHandler) ServerOption {
|
func UnknownServiceHandler(streamHandler StreamHandler) ServerOption {
|
||||||
return newFuncServerOption(func(o *serverOptions) {
|
return newFuncServerOption(func(o *serverOptions) {
|
||||||
o.unknownStreamDesc = &StreamDesc{
|
o.unknownStreamDesc = &StreamDesc{
|
||||||
|
@ -376,6 +378,16 @@ func MaxHeaderListSize(s uint32) ServerOption {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HeaderTableSize returns a ServerOption that sets the size of dynamic
|
||||||
|
// header table for stream.
|
||||||
|
//
|
||||||
|
// This API is EXPERIMENTAL.
|
||||||
|
func HeaderTableSize(s uint32) ServerOption {
|
||||||
|
return newFuncServerOption(func(o *serverOptions) {
|
||||||
|
o.headerTableSize = &s
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
|
@ -386,10 +398,10 @@ func NewServer(opt ...ServerOption) *Server {
|
||||||
s := &Server{
|
s := &Server{
|
||||||
lis: make(map[net.Listener]bool),
|
lis: make(map[net.Listener]bool),
|
||||||
opts: opts,
|
opts: opts,
|
||||||
conns: make(map[io.Closer]bool),
|
conns: make(map[transport.ServerTransport]bool),
|
||||||
m: make(map[string]*service),
|
m: make(map[string]*service),
|
||||||
quit: make(chan struct{}),
|
quit: grpcsync.NewEvent(),
|
||||||
done: make(chan struct{}),
|
done: grpcsync.NewEvent(),
|
||||||
czData: new(channelzData),
|
czData: new(channelzData),
|
||||||
}
|
}
|
||||||
s.cv = sync.NewCond(&s.mu)
|
s.cv = sync.NewCond(&s.mu)
|
||||||
|
@ -556,11 +568,9 @@ func (s *Server) Serve(lis net.Listener) error {
|
||||||
s.serveWG.Add(1)
|
s.serveWG.Add(1)
|
||||||
defer func() {
|
defer func() {
|
||||||
s.serveWG.Done()
|
s.serveWG.Done()
|
||||||
select {
|
if s.quit.HasFired() {
|
||||||
// Stop or GracefulStop called; block until done and return nil.
|
// Stop or GracefulStop called; block until done and return nil.
|
||||||
case <-s.quit:
|
<-s.done.Done()
|
||||||
<-s.done
|
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -603,7 +613,7 @@ func (s *Server) Serve(lis net.Listener) error {
|
||||||
timer := time.NewTimer(tempDelay)
|
timer := time.NewTimer(tempDelay)
|
||||||
select {
|
select {
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
case <-s.quit:
|
case <-s.quit.Done():
|
||||||
timer.Stop()
|
timer.Stop()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -613,10 +623,8 @@ func (s *Server) Serve(lis net.Listener) error {
|
||||||
s.printf("done serving; Accept = %v", err)
|
s.printf("done serving; Accept = %v", err)
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
|
||||||
select {
|
if s.quit.HasFired() {
|
||||||
case <-s.quit:
|
|
||||||
return nil
|
return nil
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -637,6 +645,10 @@ func (s *Server) Serve(lis net.Listener) error {
|
||||||
// handleRawConn forks a goroutine to handle a just-accepted connection that
|
// handleRawConn forks a goroutine to handle a just-accepted connection that
|
||||||
// has not had any I/O performed on it yet.
|
// has not had any I/O performed on it yet.
|
||||||
func (s *Server) handleRawConn(rawConn net.Conn) {
|
func (s *Server) handleRawConn(rawConn net.Conn) {
|
||||||
|
if s.quit.HasFired() {
|
||||||
|
rawConn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
rawConn.SetDeadline(time.Now().Add(s.opts.connectionTimeout))
|
rawConn.SetDeadline(time.Now().Add(s.opts.connectionTimeout))
|
||||||
conn, authInfo, err := s.useTransportAuthenticator(rawConn)
|
conn, authInfo, err := s.useTransportAuthenticator(rawConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -653,14 +665,6 @@ func (s *Server) handleRawConn(rawConn net.Conn) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.mu.Lock()
|
|
||||||
if s.conns == nil {
|
|
||||||
s.mu.Unlock()
|
|
||||||
conn.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.mu.Unlock()
|
|
||||||
|
|
||||||
// Finish handshaking (HTTP2)
|
// Finish handshaking (HTTP2)
|
||||||
st := s.newHTTP2Transport(conn, authInfo)
|
st := s.newHTTP2Transport(conn, authInfo)
|
||||||
if st == nil {
|
if st == nil {
|
||||||
|
@ -693,6 +697,7 @@ func (s *Server) newHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) tr
|
||||||
ReadBufferSize: s.opts.readBufferSize,
|
ReadBufferSize: s.opts.readBufferSize,
|
||||||
ChannelzParentID: s.channelzID,
|
ChannelzParentID: s.channelzID,
|
||||||
MaxHeaderListSize: s.opts.maxHeaderListSize,
|
MaxHeaderListSize: s.opts.maxHeaderListSize,
|
||||||
|
HeaderTableSize: s.opts.headerTableSize,
|
||||||
}
|
}
|
||||||
st, err := transport.NewServerTransport("http2", c, config)
|
st, err := transport.NewServerTransport("http2", c, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -768,6 +773,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
// traceInfo returns a traceInfo and associates it with stream, if tracing is enabled.
|
// traceInfo returns a traceInfo and associates it with stream, if tracing is enabled.
|
||||||
// If tracing is not enabled, it returns nil.
|
// If tracing is not enabled, it returns nil.
|
||||||
func (s *Server) traceInfo(st transport.ServerTransport, stream *transport.Stream) (trInfo *traceInfo) {
|
func (s *Server) traceInfo(st transport.ServerTransport, stream *transport.Stream) (trInfo *traceInfo) {
|
||||||
|
if !EnableTracing {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
tr, ok := trace.FromContext(stream.Context())
|
tr, ok := trace.FromContext(stream.Context())
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
|
@ -786,27 +794,27 @@ func (s *Server) traceInfo(st transport.ServerTransport, stream *transport.Strea
|
||||||
return trInfo
|
return trInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) addConn(c io.Closer) bool {
|
func (s *Server) addConn(st transport.ServerTransport) bool {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
if s.conns == nil {
|
if s.conns == nil {
|
||||||
c.Close()
|
st.Close()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if s.drain {
|
if s.drain {
|
||||||
// Transport added after we drained our existing conns: drain it
|
// Transport added after we drained our existing conns: drain it
|
||||||
// immediately.
|
// immediately.
|
||||||
c.(transport.ServerTransport).Drain()
|
st.Drain()
|
||||||
}
|
}
|
||||||
s.conns[c] = true
|
s.conns[st] = true
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) removeConn(c io.Closer) {
|
func (s *Server) removeConn(st transport.ServerTransport) {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
if s.conns != nil {
|
if s.conns != nil {
|
||||||
delete(s.conns, c)
|
delete(s.conns, st)
|
||||||
s.cv.Broadcast()
|
s.cv.Broadcast()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -857,41 +865,58 @@ func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Str
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, md *MethodDesc, trInfo *traceInfo) (err error) {
|
func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, md *MethodDesc, trInfo *traceInfo) (err error) {
|
||||||
|
sh := s.opts.statsHandler
|
||||||
|
if sh != nil || trInfo != nil || channelz.IsOn() {
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
s.incrCallsStarted()
|
s.incrCallsStarted()
|
||||||
defer func() {
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
s.incrCallsFailed()
|
|
||||||
} else {
|
|
||||||
s.incrCallsSucceeded()
|
|
||||||
}
|
}
|
||||||
}()
|
var statsBegin *stats.Begin
|
||||||
}
|
|
||||||
sh := s.opts.statsHandler
|
|
||||||
if sh != nil {
|
if sh != nil {
|
||||||
beginTime := time.Now()
|
beginTime := time.Now()
|
||||||
begin := &stats.Begin{
|
statsBegin = &stats.Begin{
|
||||||
BeginTime: beginTime,
|
BeginTime: beginTime,
|
||||||
}
|
}
|
||||||
sh.HandleRPC(stream.Context(), begin)
|
sh.HandleRPC(stream.Context(), statsBegin)
|
||||||
|
}
|
||||||
|
if trInfo != nil {
|
||||||
|
trInfo.tr.LazyLog(&trInfo.firstLine, false)
|
||||||
|
}
|
||||||
|
// The deferred error handling for tracing, stats handler and channelz are
|
||||||
|
// combined into one function to reduce stack usage -- a defer takes ~56-64
|
||||||
|
// bytes on the stack, so overflowing the stack will require a stack
|
||||||
|
// re-allocation, which is expensive.
|
||||||
|
//
|
||||||
|
// To maintain behavior similar to separate deferred statements, statements
|
||||||
|
// should be executed in the reverse order. That is, tracing first, stats
|
||||||
|
// handler second, and channelz last. Note that panics *within* defers will
|
||||||
|
// lead to different behavior, but that's an acceptable compromise; that
|
||||||
|
// would be undefined behavior territory anyway.
|
||||||
defer func() {
|
defer func() {
|
||||||
|
if trInfo != nil {
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||||
|
trInfo.tr.SetError()
|
||||||
|
}
|
||||||
|
trInfo.tr.Finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
if sh != nil {
|
||||||
end := &stats.End{
|
end := &stats.End{
|
||||||
BeginTime: beginTime,
|
BeginTime: statsBegin.BeginTime,
|
||||||
EndTime: time.Now(),
|
EndTime: time.Now(),
|
||||||
}
|
}
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
end.Error = toRPCErr(err)
|
end.Error = toRPCErr(err)
|
||||||
}
|
}
|
||||||
sh.HandleRPC(stream.Context(), end)
|
sh.HandleRPC(stream.Context(), end)
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
if trInfo != nil {
|
|
||||||
defer trInfo.tr.Finish()
|
if channelz.IsOn() {
|
||||||
trInfo.tr.LazyLog(&trInfo.firstLine, false)
|
|
||||||
defer func() {
|
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
s.incrCallsFailed()
|
||||||
trInfo.tr.SetError()
|
} else {
|
||||||
|
s.incrCallsSucceeded()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
@ -980,6 +1005,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
sh.HandleRPC(stream.Context(), &stats.InPayload{
|
sh.HandleRPC(stream.Context(), &stats.InPayload{
|
||||||
RecvTime: time.Now(),
|
RecvTime: time.Now(),
|
||||||
Payload: v,
|
Payload: v,
|
||||||
|
WireLength: payInfo.wireLength,
|
||||||
Data: d,
|
Data: d,
|
||||||
Length: len(d),
|
Length: len(d),
|
||||||
})
|
})
|
||||||
|
@ -1077,7 +1103,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
// TODO: Should we be logging if writing status failed here, like above?
|
// TODO: Should we be logging if writing status failed here, like above?
|
||||||
// Should the logging be in WriteStatus? Should we ignore the WriteStatus
|
// Should the logging be in WriteStatus? Should we ignore the WriteStatus
|
||||||
// error or allow the stats handler to see it?
|
// error or allow the stats handler to see it?
|
||||||
err = t.WriteStatus(stream, status.New(codes.OK, ""))
|
err = t.WriteStatus(stream, statusOK)
|
||||||
if binlog != nil {
|
if binlog != nil {
|
||||||
binlog.Log(&binarylog.ServerTrailer{
|
binlog.Log(&binarylog.ServerTrailer{
|
||||||
Trailer: stream.Trailer(),
|
Trailer: stream.Trailer(),
|
||||||
|
@ -1090,31 +1116,15 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, sd *StreamDesc, trInfo *traceInfo) (err error) {
|
func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, sd *StreamDesc, trInfo *traceInfo) (err error) {
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
s.incrCallsStarted()
|
s.incrCallsStarted()
|
||||||
defer func() {
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
s.incrCallsFailed()
|
|
||||||
} else {
|
|
||||||
s.incrCallsSucceeded()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
sh := s.opts.statsHandler
|
sh := s.opts.statsHandler
|
||||||
|
var statsBegin *stats.Begin
|
||||||
if sh != nil {
|
if sh != nil {
|
||||||
beginTime := time.Now()
|
beginTime := time.Now()
|
||||||
begin := &stats.Begin{
|
statsBegin = &stats.Begin{
|
||||||
BeginTime: beginTime,
|
BeginTime: beginTime,
|
||||||
}
|
}
|
||||||
sh.HandleRPC(stream.Context(), begin)
|
sh.HandleRPC(stream.Context(), statsBegin)
|
||||||
defer func() {
|
|
||||||
end := &stats.End{
|
|
||||||
BeginTime: beginTime,
|
|
||||||
EndTime: time.Now(),
|
|
||||||
}
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
end.Error = toRPCErr(err)
|
|
||||||
}
|
|
||||||
sh.HandleRPC(stream.Context(), end)
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
ctx := NewContextWithServerTransportStream(stream.Context(), stream)
|
ctx := NewContextWithServerTransportStream(stream.Context(), stream)
|
||||||
ss := &serverStream{
|
ss := &serverStream{
|
||||||
|
@ -1129,6 +1139,41 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
|
||||||
statsHandler: sh,
|
statsHandler: sh,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sh != nil || trInfo != nil || channelz.IsOn() {
|
||||||
|
// See comment in processUnaryRPC on defers.
|
||||||
|
defer func() {
|
||||||
|
if trInfo != nil {
|
||||||
|
ss.mu.Lock()
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
ss.trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||||
|
ss.trInfo.tr.SetError()
|
||||||
|
}
|
||||||
|
ss.trInfo.tr.Finish()
|
||||||
|
ss.trInfo.tr = nil
|
||||||
|
ss.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
if sh != nil {
|
||||||
|
end := &stats.End{
|
||||||
|
BeginTime: statsBegin.BeginTime,
|
||||||
|
EndTime: time.Now(),
|
||||||
|
}
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
end.Error = toRPCErr(err)
|
||||||
|
}
|
||||||
|
sh.HandleRPC(stream.Context(), end)
|
||||||
|
}
|
||||||
|
|
||||||
|
if channelz.IsOn() {
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
s.incrCallsFailed()
|
||||||
|
} else {
|
||||||
|
s.incrCallsSucceeded()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
ss.binlog = binarylog.GetMethodLogger(stream.Method())
|
ss.binlog = binarylog.GetMethodLogger(stream.Method())
|
||||||
if ss.binlog != nil {
|
if ss.binlog != nil {
|
||||||
md, _ := metadata.FromIncomingContext(ctx)
|
md, _ := metadata.FromIncomingContext(ctx)
|
||||||
|
@ -1182,16 +1227,6 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
|
||||||
|
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(&trInfo.firstLine, false)
|
trInfo.tr.LazyLog(&trInfo.firstLine, false)
|
||||||
defer func() {
|
|
||||||
ss.mu.Lock()
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
ss.trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
|
||||||
ss.trInfo.tr.SetError()
|
|
||||||
}
|
|
||||||
ss.trInfo.tr.Finish()
|
|
||||||
ss.trInfo.tr = nil
|
|
||||||
ss.mu.Unlock()
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
var appErr error
|
var appErr error
|
||||||
var server interface{}
|
var server interface{}
|
||||||
|
@ -1235,7 +1270,7 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
|
||||||
ss.trInfo.tr.LazyLog(stringer("OK"), false)
|
ss.trInfo.tr.LazyLog(stringer("OK"), false)
|
||||||
ss.mu.Unlock()
|
ss.mu.Unlock()
|
||||||
}
|
}
|
||||||
err = t.WriteStatus(ss.s, status.New(codes.OK, ""))
|
err = t.WriteStatus(ss.s, statusOK)
|
||||||
if ss.binlog != nil {
|
if ss.binlog != nil {
|
||||||
ss.binlog.Log(&binarylog.ServerTrailer{
|
ss.binlog.Log(&binarylog.ServerTrailer{
|
||||||
Trailer: ss.s.Trailer(),
|
Trailer: ss.s.Trailer(),
|
||||||
|
@ -1352,15 +1387,11 @@ 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.quitOnce.Do(func() {
|
s.quit.Fire()
|
||||||
close(s.quit)
|
|
||||||
})
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
s.serveWG.Wait()
|
s.serveWG.Wait()
|
||||||
s.doneOnce.Do(func() {
|
s.done.Fire()
|
||||||
close(s.done)
|
|
||||||
})
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
s.channelzRemoveOnce.Do(func() {
|
s.channelzRemoveOnce.Do(func() {
|
||||||
|
@ -1397,15 +1428,8 @@ func (s *Server) Stop() {
|
||||||
// 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.quitOnce.Do(func() {
|
s.quit.Fire()
|
||||||
close(s.quit)
|
defer s.done.Fire()
|
||||||
})
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
s.doneOnce.Do(func() {
|
|
||||||
close(s.done)
|
|
||||||
})
|
|
||||||
}()
|
|
||||||
|
|
||||||
s.channelzRemoveOnce.Do(func() {
|
s.channelzRemoveOnce.Do(func() {
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
|
@ -1423,8 +1447,8 @@ func (s *Server) GracefulStop() {
|
||||||
}
|
}
|
||||||
s.lis = nil
|
s.lis = nil
|
||||||
if !s.drain {
|
if !s.drain {
|
||||||
for c := range s.conns {
|
for st := range s.conns {
|
||||||
c.(transport.ServerTransport).Drain()
|
st.Drain()
|
||||||
}
|
}
|
||||||
s.drain = true
|
s.drain = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,11 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/balancer"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
|
"google.golang.org/grpc/internal"
|
||||||
|
"google.golang.org/grpc/serviceconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
const maxInt = int(^uint(0) >> 1)
|
const maxInt = int(^uint(0) >> 1)
|
||||||
|
@ -61,6 +64,11 @@ type MethodConfig struct {
|
||||||
retryPolicy *retryPolicy
|
retryPolicy *retryPolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type lbConfig struct {
|
||||||
|
name string
|
||||||
|
cfg serviceconfig.LoadBalancingConfig
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceConfig is provided by the service provider and contains parameters for how
|
// ServiceConfig is provided by the service provider and contains parameters for how
|
||||||
// clients that connect to the service should behave.
|
// clients that connect to the service should behave.
|
||||||
//
|
//
|
||||||
|
@ -68,10 +76,18 @@ type MethodConfig struct {
|
||||||
// through name resolver, as specified here
|
// through name resolver, as specified here
|
||||||
// https://github.com/grpc/grpc/blob/master/doc/service_config.md
|
// https://github.com/grpc/grpc/blob/master/doc/service_config.md
|
||||||
type ServiceConfig struct {
|
type ServiceConfig struct {
|
||||||
// LB is the load balancer the service providers recommends. The balancer specified
|
serviceconfig.Config
|
||||||
// via grpc.WithBalancer will override this.
|
|
||||||
|
// LB is the load balancer the service providers recommends. The balancer
|
||||||
|
// specified via grpc.WithBalancer will override this. This is deprecated;
|
||||||
|
// lbConfigs is preferred. If lbConfig and LB are both present, lbConfig
|
||||||
|
// will be used.
|
||||||
LB *string
|
LB *string
|
||||||
|
|
||||||
|
// lbConfig is the service config's load balancing configuration. If
|
||||||
|
// lbConfig and LB are both present, lbConfig will be used.
|
||||||
|
lbConfig *lbConfig
|
||||||
|
|
||||||
// Methods contains a map for the methods in this service. If there is an
|
// Methods contains a map for the methods in this service. If there is an
|
||||||
// exact match for a method (i.e. /service/method) in the map, use the
|
// exact match for a method (i.e. /service/method) in the map, use the
|
||||||
// corresponding MethodConfig. If there's no exact match, look for the
|
// corresponding MethodConfig. If there's no exact match, look for the
|
||||||
|
@ -120,9 +136,9 @@ type retryPolicy struct {
|
||||||
maxAttempts int
|
maxAttempts int
|
||||||
|
|
||||||
// Exponential backoff parameters. The initial retry attempt will occur at
|
// Exponential backoff parameters. The initial retry attempt will occur at
|
||||||
// random(0, initialBackoffMS). In general, the nth attempt will occur at
|
// random(0, initialBackoff). In general, the nth attempt will occur at
|
||||||
// random(0,
|
// random(0,
|
||||||
// min(initialBackoffMS*backoffMultiplier**(n-1), maxBackoffMS)).
|
// min(initialBackoff*backoffMultiplier**(n-1), maxBackoff)).
|
||||||
//
|
//
|
||||||
// These fields are required and must be greater than zero.
|
// These fields are required and must be greater than zero.
|
||||||
initialBackoff time.Duration
|
initialBackoff time.Duration
|
||||||
|
@ -233,20 +249,29 @@ type jsonMC struct {
|
||||||
RetryPolicy *jsonRetryPolicy
|
RetryPolicy *jsonRetryPolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type loadBalancingConfig map[string]json.RawMessage
|
||||||
|
|
||||||
// TODO(lyuxuan): delete this struct after cleaning up old service config implementation.
|
// TODO(lyuxuan): delete this struct after cleaning up old service config implementation.
|
||||||
type jsonSC struct {
|
type jsonSC struct {
|
||||||
LoadBalancingPolicy *string
|
LoadBalancingPolicy *string
|
||||||
|
LoadBalancingConfig *[]loadBalancingConfig
|
||||||
MethodConfig *[]jsonMC
|
MethodConfig *[]jsonMC
|
||||||
RetryThrottling *retryThrottlingPolicy
|
RetryThrottling *retryThrottlingPolicy
|
||||||
HealthCheckConfig *healthCheckConfig
|
HealthCheckConfig *healthCheckConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseServiceConfig(js string) (*ServiceConfig, error) {
|
func init() {
|
||||||
|
internal.ParseServiceConfigForTesting = parseServiceConfig
|
||||||
|
}
|
||||||
|
func parseServiceConfig(js string) *serviceconfig.ParseResult {
|
||||||
|
if len(js) == 0 {
|
||||||
|
return &serviceconfig.ParseResult{Err: fmt.Errorf("no JSON service config provided")}
|
||||||
|
}
|
||||||
var rsc jsonSC
|
var rsc jsonSC
|
||||||
err := json.Unmarshal([]byte(js), &rsc)
|
err := json.Unmarshal([]byte(js), &rsc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
|
grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
|
||||||
return nil, err
|
return &serviceconfig.ParseResult{Err: err}
|
||||||
}
|
}
|
||||||
sc := ServiceConfig{
|
sc := ServiceConfig{
|
||||||
LB: rsc.LoadBalancingPolicy,
|
LB: rsc.LoadBalancingPolicy,
|
||||||
|
@ -255,10 +280,46 @@ func parseServiceConfig(js string) (*ServiceConfig, error) {
|
||||||
healthCheckConfig: rsc.HealthCheckConfig,
|
healthCheckConfig: rsc.HealthCheckConfig,
|
||||||
rawJSONString: js,
|
rawJSONString: js,
|
||||||
}
|
}
|
||||||
if rsc.MethodConfig == nil {
|
if rsc.LoadBalancingConfig != nil {
|
||||||
return &sc, nil
|
for i, lbcfg := range *rsc.LoadBalancingConfig {
|
||||||
|
if len(lbcfg) != 1 {
|
||||||
|
err := fmt.Errorf("invalid loadBalancingConfig: entry %v does not contain exactly 1 policy/config pair: %q", i, lbcfg)
|
||||||
|
grpclog.Warningf(err.Error())
|
||||||
|
return &serviceconfig.ParseResult{Err: err}
|
||||||
|
}
|
||||||
|
var name string
|
||||||
|
var jsonCfg json.RawMessage
|
||||||
|
for name, jsonCfg = range lbcfg {
|
||||||
|
}
|
||||||
|
builder := balancer.Get(name)
|
||||||
|
if builder == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sc.lbConfig = &lbConfig{name: name}
|
||||||
|
if parser, ok := builder.(balancer.ConfigParser); ok {
|
||||||
|
var err error
|
||||||
|
sc.lbConfig.cfg, err = parser.ParseConfig(jsonCfg)
|
||||||
|
if err != nil {
|
||||||
|
return &serviceconfig.ParseResult{Err: fmt.Errorf("error parsing loadBalancingConfig for policy %q: %v", name, err)}
|
||||||
|
}
|
||||||
|
} else if string(jsonCfg) != "{}" {
|
||||||
|
grpclog.Warningf("non-empty balancer configuration %q, but balancer does not implement ParseConfig", string(jsonCfg))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if sc.lbConfig == nil {
|
||||||
|
// We had a loadBalancingConfig field but did not encounter a
|
||||||
|
// supported policy. The config is considered invalid in this
|
||||||
|
// case.
|
||||||
|
err := fmt.Errorf("invalid loadBalancingConfig: no supported policies found")
|
||||||
|
grpclog.Warningf(err.Error())
|
||||||
|
return &serviceconfig.ParseResult{Err: err}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if rsc.MethodConfig == nil {
|
||||||
|
return &serviceconfig.ParseResult{Config: &sc}
|
||||||
|
}
|
||||||
for _, m := range *rsc.MethodConfig {
|
for _, m := range *rsc.MethodConfig {
|
||||||
if m.Name == nil {
|
if m.Name == nil {
|
||||||
continue
|
continue
|
||||||
|
@ -266,7 +327,7 @@ func parseServiceConfig(js string) (*ServiceConfig, error) {
|
||||||
d, err := parseDuration(m.Timeout)
|
d, err := parseDuration(m.Timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
|
grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
|
||||||
return nil, err
|
return &serviceconfig.ParseResult{Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
mc := MethodConfig{
|
mc := MethodConfig{
|
||||||
|
@ -275,7 +336,7 @@ func parseServiceConfig(js string) (*ServiceConfig, error) {
|
||||||
}
|
}
|
||||||
if mc.retryPolicy, err = convertRetryPolicy(m.RetryPolicy); err != nil {
|
if mc.retryPolicy, err = convertRetryPolicy(m.RetryPolicy); err != nil {
|
||||||
grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
|
grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
|
||||||
return nil, err
|
return &serviceconfig.ParseResult{Err: err}
|
||||||
}
|
}
|
||||||
if m.MaxRequestMessageBytes != nil {
|
if m.MaxRequestMessageBytes != nil {
|
||||||
if *m.MaxRequestMessageBytes > int64(maxInt) {
|
if *m.MaxRequestMessageBytes > int64(maxInt) {
|
||||||
|
@ -299,14 +360,14 @@ func parseServiceConfig(js string) (*ServiceConfig, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if sc.retryThrottling != nil {
|
if sc.retryThrottling != nil {
|
||||||
if sc.retryThrottling.MaxTokens <= 0 ||
|
if mt := sc.retryThrottling.MaxTokens; mt <= 0 || mt > 1000 {
|
||||||
sc.retryThrottling.MaxTokens > 1000 ||
|
return &serviceconfig.ParseResult{Err: fmt.Errorf("invalid retry throttling config: maxTokens (%v) out of range (0, 1000]", mt)}
|
||||||
sc.retryThrottling.TokenRatio <= 0 {
|
}
|
||||||
// Illegal throttling config; disable throttling.
|
if tr := sc.retryThrottling.TokenRatio; tr <= 0 {
|
||||||
sc.retryThrottling = nil
|
return &serviceconfig.ParseResult{Err: fmt.Errorf("invalid retry throttling config: tokenRatio (%v) may not be negative", tr)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &sc, nil
|
return &serviceconfig.ParseResult{Config: &sc}
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertRetryPolicy(jrp *jsonRetryPolicy) (p *retryPolicy, err error) {
|
func convertRetryPolicy(jrp *jsonRetryPolicy) (p *retryPolicy, err error) {
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2019 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 serviceconfig defines types and methods for operating on gRPC
|
||||||
|
// service configs.
|
||||||
|
//
|
||||||
|
// This package is EXPERIMENTAL.
|
||||||
|
package serviceconfig
|
||||||
|
|
||||||
|
// Config represents an opaque data structure holding a service config.
|
||||||
|
type Config interface {
|
||||||
|
isServiceConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadBalancingConfig represents an opaque data structure holding a load
|
||||||
|
// balancing config.
|
||||||
|
type LoadBalancingConfig interface {
|
||||||
|
isLoadBalancingConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseResult contains a service config or an error. Exactly one must be
|
||||||
|
// non-nil.
|
||||||
|
type ParseResult struct {
|
||||||
|
Config Config
|
||||||
|
Err error
|
||||||
|
}
|
|
@ -91,6 +91,8 @@ type InHeader struct {
|
||||||
LocalAddr net.Addr
|
LocalAddr net.Addr
|
||||||
// Compression is the compression algorithm used for the RPC.
|
// Compression is the compression algorithm used for the RPC.
|
||||||
Compression string
|
Compression string
|
||||||
|
// Header contains the header metadata received.
|
||||||
|
Header metadata.MD
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsClient indicates if the stats information is from client side.
|
// IsClient indicates if the stats information is from client side.
|
||||||
|
@ -104,6 +106,9 @@ type InTrailer struct {
|
||||||
Client bool
|
Client bool
|
||||||
// WireLength is the wire length of trailer.
|
// WireLength is the wire length of trailer.
|
||||||
WireLength int
|
WireLength int
|
||||||
|
// Trailer contains the trailer metadata received from the server. This
|
||||||
|
// field is only valid if this InTrailer is from the client side.
|
||||||
|
Trailer metadata.MD
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsClient indicates if the stats information is from client side.
|
// IsClient indicates if the stats information is from client side.
|
||||||
|
@ -146,6 +151,8 @@ type OutHeader struct {
|
||||||
LocalAddr net.Addr
|
LocalAddr net.Addr
|
||||||
// Compression is the compression algorithm used for the RPC.
|
// Compression is the compression algorithm used for the RPC.
|
||||||
Compression string
|
Compression string
|
||||||
|
// Header contains the header metadata sent.
|
||||||
|
Header metadata.MD
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsClient indicates if this stats information is from client side.
|
// IsClient indicates if this stats information is from client side.
|
||||||
|
@ -159,6 +166,9 @@ type OutTrailer struct {
|
||||||
Client bool
|
Client bool
|
||||||
// WireLength is the wire length of trailer.
|
// WireLength is the wire length of trailer.
|
||||||
WireLength int
|
WireLength int
|
||||||
|
// Trailer contains the trailer metadata sent to the client. This
|
||||||
|
// field is only valid if this OutTrailer is from the server side.
|
||||||
|
Trailer metadata.MD
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsClient indicates if this stats information is from client side.
|
// IsClient indicates if this stats information is from client side.
|
||||||
|
@ -176,6 +186,7 @@ type End struct {
|
||||||
EndTime time.Time
|
EndTime time.Time
|
||||||
// Trailer contains the trailer metadata received from the server. This
|
// Trailer contains the trailer metadata received from the server. This
|
||||||
// field is only valid if this End is from the client side.
|
// field is only valid if this End is from the client side.
|
||||||
|
// Deprecated: use Trailer in InTrailer instead.
|
||||||
Trailer metadata.MD
|
Trailer metadata.MD
|
||||||
// Error is the error the RPC ended with. It is an error generated from
|
// Error is the error the RPC ended with. It is an error generated from
|
||||||
// status.Status and can be converted back to status.Status using
|
// status.Status and can be converted back to status.Status using
|
||||||
|
|
|
@ -36,8 +36,15 @@ import (
|
||||||
"github.com/golang/protobuf/ptypes"
|
"github.com/golang/protobuf/ptypes"
|
||||||
spb "google.golang.org/genproto/googleapis/rpc/status"
|
spb "google.golang.org/genproto/googleapis/rpc/status"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
internal.StatusRawProto = statusRawProto
|
||||||
|
}
|
||||||
|
|
||||||
|
func statusRawProto(s *Status) *spb.Status { return s.s }
|
||||||
|
|
||||||
// statusError is an alias of a status proto. It implements error and Status,
|
// statusError is an alias of a status proto. It implements error and Status,
|
||||||
// and a nil statusError should never be returned by this package.
|
// and a nil statusError should never be returned by this package.
|
||||||
type statusError spb.Status
|
type statusError spb.Status
|
||||||
|
@ -51,6 +58,17 @@ func (se *statusError) GRPCStatus() *Status {
|
||||||
return &Status{s: (*spb.Status)(se)}
|
return &Status{s: (*spb.Status)(se)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Is implements future error.Is functionality.
|
||||||
|
// A statusError is equivalent if the code and message are identical.
|
||||||
|
func (se *statusError) Is(target error) bool {
|
||||||
|
tse, ok := target.(*statusError)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return proto.Equal((*spb.Status)(se), (*spb.Status)(tse))
|
||||||
|
}
|
||||||
|
|
||||||
// Status represents an RPC status code, message, and details. It is immutable
|
// Status represents an RPC status code, message, and details. It is immutable
|
||||||
// and should be created with New, Newf, or FromProto.
|
// and should be created with New, Newf, or FromProto.
|
||||||
type Status struct {
|
type Status struct {
|
||||||
|
@ -125,7 +143,7 @@ func FromProto(s *spb.Status) *Status {
|
||||||
// Status is returned with codes.Unknown and the original error message.
|
// Status is returned with codes.Unknown and the original error message.
|
||||||
func FromError(err error) (s *Status, ok bool) {
|
func FromError(err error) (s *Status, ok bool) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return &Status{s: &spb.Status{Code: int32(codes.OK)}}, true
|
return nil, true
|
||||||
}
|
}
|
||||||
if se, ok := err.(interface {
|
if se, ok := err.(interface {
|
||||||
GRPCStatus() *Status
|
GRPCStatus() *Status
|
||||||
|
@ -199,7 +217,7 @@ func Code(err error) codes.Code {
|
||||||
func FromContextError(err error) *Status {
|
func FromContextError(err error) *Status {
|
||||||
switch err {
|
switch err {
|
||||||
case nil:
|
case nil:
|
||||||
return New(codes.OK, "")
|
return nil
|
||||||
case context.DeadlineExceeded:
|
case context.DeadlineExceeded:
|
||||||
return New(codes.DeadlineExceeded, err.Error())
|
return New(codes.DeadlineExceeded, err.Error())
|
||||||
case context.Canceled:
|
case context.Canceled:
|
||||||
|
|
|
@ -30,7 +30,6 @@ import (
|
||||||
"golang.org/x/net/trace"
|
"golang.org/x/net/trace"
|
||||||
"google.golang.org/grpc/balancer"
|
"google.golang.org/grpc/balancer"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/connectivity"
|
|
||||||
"google.golang.org/grpc/encoding"
|
"google.golang.org/grpc/encoding"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
"google.golang.org/grpc/internal/balancerload"
|
"google.golang.org/grpc/internal/balancerload"
|
||||||
|
@ -328,13 +327,23 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
||||||
return cs, nil
|
return cs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *clientStream) newAttemptLocked(sh stats.Handler, trInfo *traceInfo) error {
|
// newAttemptLocked creates a new attempt with a transport.
|
||||||
cs.attempt = &csAttempt{
|
// If it succeeds, then it replaces clientStream's attempt with this new attempt.
|
||||||
|
func (cs *clientStream) newAttemptLocked(sh stats.Handler, trInfo *traceInfo) (retErr error) {
|
||||||
|
newAttempt := &csAttempt{
|
||||||
cs: cs,
|
cs: cs,
|
||||||
dc: cs.cc.dopts.dc,
|
dc: cs.cc.dopts.dc,
|
||||||
statsHandler: sh,
|
statsHandler: sh,
|
||||||
trInfo: trInfo,
|
trInfo: trInfo,
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
if retErr != nil {
|
||||||
|
// This attempt is not set in the clientStream, so it's finish won't
|
||||||
|
// be called. Call it here for stats and trace in case they are not
|
||||||
|
// nil.
|
||||||
|
newAttempt.finish(retErr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
if err := cs.ctx.Err(); err != nil {
|
if err := cs.ctx.Err(); err != nil {
|
||||||
return toRPCErr(err)
|
return toRPCErr(err)
|
||||||
|
@ -346,8 +355,9 @@ func (cs *clientStream) newAttemptLocked(sh stats.Handler, trInfo *traceInfo) er
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.firstLine.SetRemoteAddr(t.RemoteAddr())
|
trInfo.firstLine.SetRemoteAddr(t.RemoteAddr())
|
||||||
}
|
}
|
||||||
cs.attempt.t = t
|
newAttempt.t = t
|
||||||
cs.attempt.done = done
|
newAttempt.done = done
|
||||||
|
cs.attempt = newAttempt
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,7 +410,14 @@ type clientStream struct {
|
||||||
numRetries int // exclusive of transparent retry attempt(s)
|
numRetries int // exclusive of transparent retry attempt(s)
|
||||||
numRetriesSincePushback int // retries since pushback; to reset backoff
|
numRetriesSincePushback int // retries since pushback; to reset backoff
|
||||||
finished bool // TODO: replace with atomic cmpxchg or sync.Once?
|
finished bool // TODO: replace with atomic cmpxchg or sync.Once?
|
||||||
attempt *csAttempt // the active client stream attempt
|
// attempt is the active client stream attempt.
|
||||||
|
// The only place where it is written is the newAttemptLocked method and this method never writes nil.
|
||||||
|
// So, attempt can be nil only inside newClientStream function when clientStream is first created.
|
||||||
|
// One of the first things done after clientStream's creation, is to call newAttemptLocked which either
|
||||||
|
// assigns a non nil value to the attempt or returns an error. If an error is returned from newAttemptLocked,
|
||||||
|
// then newClientStream calls finish on the clientStream and returns. So, finish method is the only
|
||||||
|
// place where we need to check if the attempt is nil.
|
||||||
|
attempt *csAttempt
|
||||||
// TODO(hedging): hedging will have multiple attempts simultaneously.
|
// TODO(hedging): hedging will have multiple attempts simultaneously.
|
||||||
committed bool // active attempt committed for retry?
|
committed bool // active attempt committed for retry?
|
||||||
buffer []func(a *csAttempt) error // operations to replay on retry
|
buffer []func(a *csAttempt) error // operations to replay on retry
|
||||||
|
@ -458,8 +475,8 @@ func (cs *clientStream) shouldRetry(err error) error {
|
||||||
if cs.attempt.s != nil {
|
if cs.attempt.s != nil {
|
||||||
<-cs.attempt.s.Done()
|
<-cs.attempt.s.Done()
|
||||||
}
|
}
|
||||||
if cs.firstAttempt && !cs.callInfo.failFast && (cs.attempt.s == nil || cs.attempt.s.Unprocessed()) {
|
if cs.firstAttempt && (cs.attempt.s == nil || cs.attempt.s.Unprocessed()) {
|
||||||
// First attempt, wait-for-ready, stream unprocessed: transparently retry.
|
// First attempt, stream unprocessed: transparently retry.
|
||||||
cs.firstAttempt = false
|
cs.firstAttempt = false
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -471,7 +488,7 @@ func (cs *clientStream) shouldRetry(err error) error {
|
||||||
pushback := 0
|
pushback := 0
|
||||||
hasPushback := false
|
hasPushback := false
|
||||||
if cs.attempt.s != nil {
|
if cs.attempt.s != nil {
|
||||||
if to, toErr := cs.attempt.s.TrailersOnly(); toErr != nil || !to {
|
if !cs.attempt.s.TrailersOnly() {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -806,13 +823,13 @@ func (cs *clientStream) finish(err error) {
|
||||||
}
|
}
|
||||||
if cs.attempt != nil {
|
if cs.attempt != nil {
|
||||||
cs.attempt.finish(err)
|
cs.attempt.finish(err)
|
||||||
}
|
|
||||||
// after functions all rely upon having a stream.
|
// after functions all rely upon having a stream.
|
||||||
if cs.attempt.s != nil {
|
if cs.attempt.s != nil {
|
||||||
for _, o := range cs.opts {
|
for _, o := range cs.opts {
|
||||||
o.after(cs.callInfo)
|
o.after(cs.callInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
cs.cancel()
|
cs.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -965,19 +982,18 @@ func (a *csAttempt) finish(err error) {
|
||||||
a.mu.Unlock()
|
a.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ac *addrConn) newClientStream(ctx context.Context, desc *StreamDesc, method string, t transport.ClientTransport, opts ...CallOption) (_ ClientStream, err error) {
|
// newClientStream creates a ClientStream with the specified transport, on the
|
||||||
ac.mu.Lock()
|
// given addrConn.
|
||||||
if ac.transport != t {
|
//
|
||||||
ac.mu.Unlock()
|
// It's expected that the given transport is either the same one in addrConn, or
|
||||||
return nil, status.Error(codes.Canceled, "the provided transport is no longer valid to use")
|
// is already closed. To avoid race, transport is specified separately, instead
|
||||||
}
|
// of using ac.transpot.
|
||||||
// transition to CONNECTING state when an attempt starts
|
//
|
||||||
if ac.state != connectivity.Connecting {
|
// Main difference between this and ClientConn.NewStream:
|
||||||
ac.updateConnectivityState(connectivity.Connecting)
|
// - no retry
|
||||||
ac.cc.handleSubConnStateChange(ac.acbw, ac.state)
|
// - no service config (or wait for service config)
|
||||||
}
|
// - no tracing or stats
|
||||||
ac.mu.Unlock()
|
func newNonRetryClientStream(ctx context.Context, desc *StreamDesc, method string, t transport.ClientTransport, ac *addrConn, opts ...CallOption) (_ ClientStream, err error) {
|
||||||
|
|
||||||
if t == nil {
|
if t == nil {
|
||||||
// TODO: return RPC error here?
|
// TODO: return RPC error here?
|
||||||
return nil, errors.New("transport provided is nil")
|
return nil, errors.New("transport provided is nil")
|
||||||
|
@ -985,14 +1001,6 @@ func (ac *addrConn) newClientStream(ctx context.Context, desc *StreamDesc, metho
|
||||||
// defaultCallInfo contains unnecessary info(i.e. failfast, maxRetryRPCBufferSize), so we just initialize an empty struct.
|
// defaultCallInfo contains unnecessary info(i.e. failfast, maxRetryRPCBufferSize), so we just initialize an empty struct.
|
||||||
c := &callInfo{}
|
c := &callInfo{}
|
||||||
|
|
||||||
for _, o := range opts {
|
|
||||||
if err := o.before(c); err != nil {
|
|
||||||
return nil, toRPCErr(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.maxReceiveMessageSize = getMaxSize(nil, c.maxReceiveMessageSize, defaultClientMaxReceiveMessageSize)
|
|
||||||
c.maxSendMessageSize = getMaxSize(nil, c.maxSendMessageSize, defaultServerMaxSendMessageSize)
|
|
||||||
|
|
||||||
// Possible context leak:
|
// Possible context leak:
|
||||||
// The cancel function for the child context we create will only be called
|
// The cancel function for the child context we create will only be called
|
||||||
// when RecvMsg returns a non-nil error, if the ClientConn is closed, or if
|
// when RecvMsg returns a non-nil error, if the ClientConn is closed, or if
|
||||||
|
@ -1005,6 +1013,13 @@ func (ac *addrConn) newClientStream(ctx context.Context, desc *StreamDesc, metho
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
for _, o := range opts {
|
||||||
|
if err := o.before(c); err != nil {
|
||||||
|
return nil, toRPCErr(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.maxReceiveMessageSize = getMaxSize(nil, c.maxReceiveMessageSize, defaultClientMaxReceiveMessageSize)
|
||||||
|
c.maxSendMessageSize = getMaxSize(nil, c.maxSendMessageSize, defaultServerMaxSendMessageSize)
|
||||||
if err := setCallInfoCodec(c); err != nil {
|
if err := setCallInfoCodec(c); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1037,6 +1052,7 @@ func (ac *addrConn) newClientStream(ctx context.Context, desc *StreamDesc, metho
|
||||||
callHdr.Creds = c.creds
|
callHdr.Creds = c.creds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use a special addrConnStream to avoid retry.
|
||||||
as := &addrConnStream{
|
as := &addrConnStream{
|
||||||
callHdr: callHdr,
|
callHdr: callHdr,
|
||||||
ac: ac,
|
ac: ac,
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue