command/connect/proxy: accept -service and -upstream

This commit is contained in:
Mitchell Hashimoto 2018-05-19 00:43:38 -07:00
parent 0487cacd10
commit 42ee214c8a
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
3 changed files with 186 additions and 2 deletions

View File

@ -0,0 +1,54 @@
package proxy
import (
"fmt"
"strconv"
"strings"
"github.com/hashicorp/consul/connect/proxy"
)
// FlagUpstreams implements the flag.Value interface and allows specifying
// the -upstream flag multiple times and keeping track of the name of the
// upstream and the local port.
//
// The syntax of the value is "name:addr" where addr can be "port" or
// "host:port". Examples: "db:8181", "db:127.0.0.10:8282", etc.
type FlagUpstreams map[string]proxy.UpstreamConfig
func (f *FlagUpstreams) String() string {
return fmt.Sprintf("%v", *f)
}
func (f *FlagUpstreams) Set(value string) error {
idx := strings.Index(value, ":")
if idx == -1 {
return fmt.Errorf("Upstream value should be name:addr in %q", value)
}
addr := ""
name := value[:idx]
portRaw := value[idx+1:]
if idx := strings.Index(portRaw, ":"); idx != -1 {
addr = portRaw[:idx]
portRaw = portRaw[idx+1:]
}
port, err := strconv.ParseInt(portRaw, 0, 0)
if err != nil {
return err
}
if *f == nil {
*f = make(map[string]proxy.UpstreamConfig)
}
(*f)[name] = proxy.UpstreamConfig{
LocalBindAddress: addr,
LocalBindPort: int(port),
DestinationName: name,
DestinationType: "service",
}
return nil
}

View File

@ -0,0 +1,106 @@
package proxy
import (
"flag"
"testing"
"github.com/hashicorp/consul/connect/proxy"
"github.com/stretchr/testify/require"
)
func TestFlagUpstreams_impl(t *testing.T) {
var _ flag.Value = new(FlagUpstreams)
}
func TestFlagUpstreams(t *testing.T) {
cases := []struct {
Name string
Input []string
Expected map[string]proxy.UpstreamConfig
Error string
}{
{
"bad format",
[]string{"foo"},
nil,
"should be name:addr",
},
{
"port not int",
[]string{"db:hello"},
nil,
"invalid syntax",
},
{
"4 parts",
[]string{"db:127.0.0.1:8181:foo"},
nil,
"invalid syntax",
},
{
"single value",
[]string{"db:8181"},
map[string]proxy.UpstreamConfig{
"db": proxy.UpstreamConfig{
LocalBindPort: 8181,
DestinationName: "db",
DestinationType: "service",
},
},
"",
},
{
"address specified",
[]string{"db:127.0.0.55:8181"},
map[string]proxy.UpstreamConfig{
"db": proxy.UpstreamConfig{
LocalBindAddress: "127.0.0.55",
LocalBindPort: 8181,
DestinationName: "db",
DestinationType: "service",
},
},
"",
},
{
"repeat value, overwrite",
[]string{"db:8181", "db:8282"},
map[string]proxy.UpstreamConfig{
"db": proxy.UpstreamConfig{
LocalBindPort: 8282,
DestinationName: "db",
DestinationType: "service",
},
},
"",
},
}
for _, tc := range cases {
t.Run(tc.Name, func(t *testing.T) {
require := require.New(t)
var actual map[string]proxy.UpstreamConfig
f := (*FlagUpstreams)(&actual)
var err error
for _, input := range tc.Input {
err = f.Set(input)
// Note we only test the last error. This could make some
// test failures confusing but it shouldn't be too bad.
}
if tc.Error != "" {
require.Error(err)
require.Contains(err.Error(), tc.Error)
return
}
require.Equal(tc.Expected, actual)
})
}
}

View File

@ -42,6 +42,8 @@ type cmd struct {
cfgFile string cfgFile string
proxyID string proxyID string
pprofAddr string pprofAddr string
service string
upstreams map[string]proxyImpl.UpstreamConfig
} }
func (c *cmd) init() { func (c *cmd) init() {
@ -66,6 +68,14 @@ func (c *cmd) init() {
"Enable debugging via pprof. Providing a host:port (or just ':port') "+ "Enable debugging via pprof. Providing a host:port (or just ':port') "+
"enables profiling HTTP endpoints on that address.") "enables profiling HTTP endpoints on that address.")
c.flags.StringVar(&c.service, "service", "",
"Name of the service this proxy is representing.")
c.flags.Var((*FlagUpstreams)(&c.upstreams), "upstream",
"Upstream service to support connecting to. The format should be "+
"'name:addr', such as 'db:8181'. This will make 'db' available "+
"on port 8181.")
c.http = &flags.HTTPFlags{} c.http = &flags.HTTPFlags{}
flags.Merge(c.flags, c.http.ClientFlags()) flags.Merge(c.flags, c.http.ClientFlags())
flags.Merge(c.flags, c.http.ServerFlags()) flags.Merge(c.flags, c.http.ServerFlags())
@ -158,13 +168,27 @@ func (c *cmd) configWatcher(client *api.Client) (proxyImpl.ConfigWatcher, error)
} }
// Use the configured proxy ID // Use the configured proxy ID
if c.proxyID == "" { if c.proxyID != "" {
return proxyImpl.NewAgentConfigWatcher(client, c.proxyID, c.logger)
}
// Otherwise, we're representing a manually specified service.
if c.service == "" {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"-service or -proxy-id must be specified so that proxy can " + "-service or -proxy-id must be specified so that proxy can " +
"configure itself.") "configure itself.")
} }
return proxyImpl.NewAgentConfigWatcher(client, c.proxyID, c.logger) // Convert our upstreams to a slice of configurations
upstreams := make([]proxyImpl.UpstreamConfig, 0, len(c.upstreams))
for _, u := range c.upstreams {
upstreams = append(upstreams, u)
}
return proxyImpl.NewStaticConfigWatcher(&proxyImpl.Config{
ProxiedServiceName: c.service,
Upstreams: upstreams,
}), nil
} }
func (c *cmd) Synopsis() string { func (c *cmd) Synopsis() string {