Merge pull request #1220 from bcwaldon/bkcompat
Backwards-compatibility with v0.4.6 addr-related flagsrelease-2.0
commit
619c7f9fdb
52
main.go
52
main.go
|
@ -5,7 +5,6 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
@ -38,10 +37,6 @@ var (
|
|||
printVersion = flag.Bool("version", false, "Print the version and exit")
|
||||
|
||||
cluster = &etcdserver.Cluster{}
|
||||
lcurls = &flagtypes.URLs{}
|
||||
acurls = &flagtypes.URLs{}
|
||||
lpurls = &flagtypes.URLs{}
|
||||
apurls = &flagtypes.URLs{}
|
||||
cors = &pkg.CORSInfo{}
|
||||
proxyFlag = new(flagtypes.Proxy)
|
||||
|
||||
|
@ -49,7 +44,6 @@ var (
|
|||
peerTLSInfo = transport.TLSInfo{}
|
||||
|
||||
deprecated = []string{
|
||||
"addr",
|
||||
"cluster-active-size",
|
||||
"cluster-remove-delay",
|
||||
"cluster-sync-interval",
|
||||
|
@ -57,7 +51,6 @@ var (
|
|||
"force",
|
||||
"max-result-buffer",
|
||||
"max-retry-attempts",
|
||||
"peer-addr",
|
||||
"peer-heartbeat-interval",
|
||||
"peer-election-timeout",
|
||||
"retry-interval",
|
||||
|
@ -69,19 +62,16 @@ var (
|
|||
|
||||
func init() {
|
||||
flag.Var(cluster, "bootstrap-config", "Initial cluster configuration for bootstrapping")
|
||||
flag.Var(apurls, "advertise-peer-urls", "List of this member's peer URLs to advertise to the rest of the cluster")
|
||||
flag.Var(acurls, "advertise-client-urls", "List of this member's client URLs to advertise to the rest of the cluster")
|
||||
flag.Var(lpurls, "listen-peer-urls", "List of this URLs to listen on for peer traffic")
|
||||
flag.Var(lcurls, "listen-client-urls", "List of this URLs to listen on for client traffic")
|
||||
flag.Var(cors, "cors", "Comma-separated white list of origins for CORS (cross-origin resource sharing).")
|
||||
flag.Var(proxyFlag, "proxy", fmt.Sprintf("Valid values include %s", strings.Join(flagtypes.ProxyValues, ", ")))
|
||||
|
||||
cluster.Set("default=http://localhost:2380,default=http://localhost:7001")
|
||||
lcurls.Set("http://localhost:2379,http://localhost:4001")
|
||||
acurls.Set("http://localhost:2379,http://localhost:4001")
|
||||
lpurls.Set("http://localhost:2380,http://localhost:7001")
|
||||
apurls.Set("http://localhost:2380,http://localhost:7001")
|
||||
|
||||
flag.Var(flagtypes.NewURLs("http://localhost:2380,http://localhost:7001"), "advertise-peer-urls", "List of this member's peer URLs to advertise to the rest of the cluster")
|
||||
flag.Var(flagtypes.NewURLs("http://localhost:2379,http://localhost:4001"), "advertise-client-urls", "List of this member's client URLs to advertise to the rest of the cluster")
|
||||
flag.Var(flagtypes.NewURLs("http://localhost:2380,http://localhost:7001"), "listen-peer-urls", "List of this URLs to listen on for peer traffic")
|
||||
flag.Var(flagtypes.NewURLs("http://localhost:2379,http://localhost:4001"), "listen-client-urls", "List of this URLs to listen on for client traffic")
|
||||
|
||||
flag.Var(cors, "cors", "Comma-separated white list of origins for CORS (cross-origin resource sharing).")
|
||||
|
||||
flag.Var(proxyFlag, "proxy", fmt.Sprintf("Valid values include %s", strings.Join(flagtypes.ProxyValues, ", ")))
|
||||
proxyFlag.Set(flagtypes.ProxyValueOff)
|
||||
|
||||
flag.StringVar(&clientTLSInfo.CAFile, "ca-file", "", "Path to the client server TLS CA file.")
|
||||
|
@ -92,6 +82,12 @@ func init() {
|
|||
flag.StringVar(&peerTLSInfo.CertFile, "peer-cert-file", "", "Path to the peer server TLS cert file.")
|
||||
flag.StringVar(&peerTLSInfo.KeyFile, "peer-key-file", "", "Path to the peer server TLS key file.")
|
||||
|
||||
// backwards-compatibility with v0.4.6
|
||||
flag.Var(&flagtypes.IPAddressPort{}, "addr", "DEPRECATED: Use -advertise-client-urls instead.")
|
||||
flag.Var(&flagtypes.IPAddressPort{}, "bind-addr", "DEPRECATED: Use -listen-client-urls instead.")
|
||||
flag.Var(&flagtypes.IPAddressPort{}, "peer-addr", "DEPRECATED: Use -advertise-peer-urls instead.")
|
||||
flag.Var(&flagtypes.IPAddressPort{}, "peer-bind-addr", "DEPRECATED: Use -listen-peer-urls instead.")
|
||||
|
||||
for _, f := range deprecated {
|
||||
flag.Var(&pkg.DeprecatedFlag{f}, f, "")
|
||||
}
|
||||
|
@ -215,7 +211,12 @@ func startEtcd() {
|
|||
}
|
||||
ph := etcdhttp.NewPeerHandler(s)
|
||||
|
||||
for _, u := range []url.URL(*lpurls) {
|
||||
lpurls, err := pkg.URLsFromFlags(flag.CommandLine, "listen-peer-urls", "peer-bind-addr", clientTLSInfo)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
for _, u := range lpurls {
|
||||
l, err := transport.NewListener(u.Host, peerTLSInfo)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -229,8 +230,13 @@ func startEtcd() {
|
|||
}()
|
||||
}
|
||||
|
||||
lcurls, err := pkg.URLsFromFlags(flag.CommandLine, "listen-client-urls", "bind-addr", clientTLSInfo)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// Start a client server goroutine for each listen address
|
||||
for _, u := range []url.URL(*lcurls) {
|
||||
for _, u := range lcurls {
|
||||
l, err := transport.NewListener(u.Host, clientTLSInfo)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -265,8 +271,12 @@ func startProxy() {
|
|||
ph = proxy.NewReadonlyHandler(ph)
|
||||
}
|
||||
|
||||
lcurls, err := pkg.URLsFromFlags(flag.CommandLine, "listen-client-urls", "bind-addr", clientTLSInfo)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
// Start a proxy server goroutine for each listen address
|
||||
for _, u := range []url.URL(*lcurls) {
|
||||
for _, u := range lcurls {
|
||||
l, err := transport.NewListener(u.Host, clientTLSInfo)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
|
39
pkg/flag.go
39
pkg/flag.go
|
@ -4,8 +4,12 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/etcd/pkg/flags"
|
||||
"github.com/coreos/etcd/pkg/transport"
|
||||
)
|
||||
|
||||
type DeprecatedFlag struct {
|
||||
|
@ -64,3 +68,38 @@ func SetFlagsFromEnv(fs *flag.FlagSet) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
// URLsFromFlags decides what URLs should be using two different flags
|
||||
// as datasources. The first flag's Value must be of type URLs, while
|
||||
// the second must be of type IPAddressPort. If both of these flags
|
||||
// are set, an error will be returned. If only the first flag is set,
|
||||
// the underlying url.URL objects will be returned unmodified. If the
|
||||
// second flag happens to be set, the underlying IPAddressPort will be
|
||||
// converted to a url.URL and returned. The Scheme of the returned
|
||||
// url.URL will be http unless the provided TLSInfo object is non-empty.
|
||||
// If neither of the flags have been explicitly set, the default value
|
||||
// of the first flag will be returned unmodified.
|
||||
func URLsFromFlags(fs *flag.FlagSet, urlsFlagName string, addrFlagName string, tlsInfo transport.TLSInfo) ([]url.URL, error) {
|
||||
visited := make(map[string]struct{})
|
||||
fs.Visit(func(f *flag.Flag) {
|
||||
visited[f.Name] = struct{}{}
|
||||
})
|
||||
|
||||
_, urlsFlagIsSet := visited[urlsFlagName]
|
||||
_, addrFlagIsSet := visited[addrFlagName]
|
||||
|
||||
if addrFlagIsSet {
|
||||
if urlsFlagIsSet {
|
||||
return nil, fmt.Errorf("Set only one of flags -%s and -%s", urlsFlagName, addrFlagName)
|
||||
}
|
||||
|
||||
addr := *fs.Lookup(addrFlagName).Value.(*flags.IPAddressPort)
|
||||
addrURL := url.URL{Scheme: "http", Host: addr.String()}
|
||||
if !tlsInfo.Empty() {
|
||||
addrURL.Scheme = "https"
|
||||
}
|
||||
return []url.URL{addrURL}, nil
|
||||
}
|
||||
|
||||
return []url.URL(*fs.Lookup(urlsFlagName).Value.(*flags.URLs)), nil
|
||||
}
|
||||
|
|
|
@ -2,8 +2,13 @@ package pkg
|
|||
|
||||
import (
|
||||
"flag"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/etcd/pkg/flags"
|
||||
"github.com/coreos/etcd/pkg/transport"
|
||||
)
|
||||
|
||||
func TestSetFlagsFromEnv(t *testing.T) {
|
||||
|
@ -49,3 +54,85 @@ func TestSetFlagsFromEnv(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestURLsFromFlags(t *testing.T) {
|
||||
tests := []struct {
|
||||
args []string
|
||||
tlsInfo transport.TLSInfo
|
||||
wantURLs []url.URL
|
||||
wantFail bool
|
||||
}{
|
||||
// use -urls default when no flags defined
|
||||
{
|
||||
args: []string{},
|
||||
tlsInfo: transport.TLSInfo{},
|
||||
wantURLs: []url.URL{
|
||||
url.URL{Scheme: "http", Host: "127.0.0.1:2379"},
|
||||
},
|
||||
wantFail: false,
|
||||
},
|
||||
|
||||
// explicitly setting -urls should carry through
|
||||
{
|
||||
args: []string{"-urls=https://192.0.3.17:2930,http://127.0.0.1:1024"},
|
||||
tlsInfo: transport.TLSInfo{},
|
||||
wantURLs: []url.URL{
|
||||
url.URL{Scheme: "https", Host: "192.0.3.17:2930"},
|
||||
url.URL{Scheme: "http", Host: "127.0.0.1:1024"},
|
||||
},
|
||||
wantFail: false,
|
||||
},
|
||||
|
||||
// explicitly setting -addr should carry through
|
||||
{
|
||||
args: []string{"-addr=192.0.2.3:1024"},
|
||||
tlsInfo: transport.TLSInfo{},
|
||||
wantURLs: []url.URL{
|
||||
url.URL{Scheme: "http", Host: "192.0.2.3:1024"},
|
||||
},
|
||||
wantFail: false,
|
||||
},
|
||||
|
||||
// scheme prepended to -addr should be https if TLSInfo non-empty
|
||||
{
|
||||
args: []string{"-addr=192.0.2.3:1024"},
|
||||
tlsInfo: transport.TLSInfo{
|
||||
CertFile: "/tmp/foo",
|
||||
KeyFile: "/tmp/bar",
|
||||
},
|
||||
wantURLs: []url.URL{
|
||||
url.URL{Scheme: "https", Host: "192.0.2.3:1024"},
|
||||
},
|
||||
wantFail: false,
|
||||
},
|
||||
|
||||
// explicitly setting both -urls and -addr should fail
|
||||
{
|
||||
args: []string{"-urls=https://127.0.0.1:1024", "-addr=192.0.2.3:1024"},
|
||||
tlsInfo: transport.TLSInfo{},
|
||||
wantURLs: nil,
|
||||
wantFail: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
fs := flag.NewFlagSet("test", flag.PanicOnError)
|
||||
fs.Var(flags.NewURLs("http://127.0.0.1:2379"), "urls", "")
|
||||
fs.Var(&flags.IPAddressPort{}, "addr", "")
|
||||
|
||||
if err := fs.Parse(tt.args); err != nil {
|
||||
t.Errorf("#%d: failed to parse flags: %v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
gotURLs, err := URLsFromFlags(fs, "urls", "addr", tt.tlsInfo)
|
||||
if tt.wantFail != (err != nil) {
|
||||
t.Errorf("#%d: wantFail=%t, got err=%v", i, tt.wantFail, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tt.wantURLs, gotURLs) {
|
||||
t.Errorf("#%d: incorrect URLs\nwant=%#v\ngot=%#v", i, tt.wantURLs, gotURLs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
package flags
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Addrs implements the flag.Value interface to allow users to define multiple
|
||||
// listen addresses on the command-line
|
||||
type Addrs []string
|
||||
|
||||
// Set parses a command line set of listen addresses, formatted like:
|
||||
// 127.0.0.1:7001,10.1.1.2:80
|
||||
func (as *Addrs) Set(s string) error {
|
||||
parsed := make([]string, 0)
|
||||
for _, in := range strings.Split(s, ",") {
|
||||
a := strings.TrimSpace(in)
|
||||
if err := validateAddr(a); err != nil {
|
||||
return err
|
||||
}
|
||||
parsed = append(parsed, a)
|
||||
}
|
||||
if len(parsed) == 0 {
|
||||
return errors.New("no valid addresses given!")
|
||||
}
|
||||
*as = parsed
|
||||
return nil
|
||||
}
|
||||
|
||||
func (as *Addrs) String() string {
|
||||
return strings.Join(*as, ",")
|
||||
}
|
||||
|
||||
// validateAddr ensures that the provided string is a valid address. Valid
|
||||
// addresses are of the form IP:port.
|
||||
// Returns an error if the address is invalid, else nil.
|
||||
func validateAddr(s string) error {
|
||||
parts := strings.SplitN(s, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
return errors.New("bad format in address specification")
|
||||
}
|
||||
if net.ParseIP(parts[0]) == nil {
|
||||
return errors.New("bad IP in address specification")
|
||||
}
|
||||
if _, err := strconv.Atoi(parts[1]); err != nil {
|
||||
return errors.New("bad port in address specification")
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package flags
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBadValidateAddr(t *testing.T) {
|
||||
tests := []string{
|
||||
// bad IP specification
|
||||
":4001",
|
||||
"127.0:8080",
|
||||
"123:456",
|
||||
// bad port specification
|
||||
"127.0.0.1:foo",
|
||||
"127.0.0.1:",
|
||||
// unix sockets not supported
|
||||
"unix://",
|
||||
"unix://tmp/etcd.sock",
|
||||
// bad strings
|
||||
"somewhere",
|
||||
"234#$",
|
||||
"file://foo/bar",
|
||||
"http://hello",
|
||||
}
|
||||
for i, in := range tests {
|
||||
if err := validateAddr(in); err == nil {
|
||||
t.Errorf(`#%d: unexpected nil error for in=%q`, i, in)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAddr(t *testing.T) {
|
||||
tests := []string{
|
||||
"1.2.3.4:8080",
|
||||
"10.1.1.1:80",
|
||||
}
|
||||
for i, in := range tests {
|
||||
if err := validateAddr(in); err != nil {
|
||||
t.Errorf("#%d: err=%v, want nil for in=%q", i, err, in)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package flags
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IPAddressPort implements the flag.Value interface. The argument
|
||||
// is validated as "ip:port".
|
||||
type IPAddressPort struct {
|
||||
IP string
|
||||
Port int
|
||||
}
|
||||
|
||||
func (a *IPAddressPort) Set(arg string) error {
|
||||
arg = strings.TrimSpace(arg)
|
||||
|
||||
parts := strings.SplitN(arg, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
return errors.New("bad format in address specification")
|
||||
}
|
||||
|
||||
if net.ParseIP(parts[0]) == nil {
|
||||
return errors.New("bad IP in address specification")
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return errors.New("bad port in address specification")
|
||||
}
|
||||
|
||||
a.IP = parts[0]
|
||||
a.Port = port
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *IPAddressPort) String() string {
|
||||
return fmt.Sprintf("%s:%d", a.IP, a.Port)
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package flags
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIPAddressPortSet(t *testing.T) {
|
||||
pass := []string{
|
||||
"1.2.3.4:8080",
|
||||
"10.1.1.1:80",
|
||||
}
|
||||
|
||||
fail := []string{
|
||||
// bad IP specification
|
||||
":4001",
|
||||
"127.0:8080",
|
||||
"123:456",
|
||||
// bad port specification
|
||||
"127.0.0.1:foo",
|
||||
"127.0.0.1:",
|
||||
// unix sockets not supported
|
||||
"unix://",
|
||||
"unix://tmp/etcd.sock",
|
||||
// bad strings
|
||||
"somewhere",
|
||||
"234#$",
|
||||
"file://foo/bar",
|
||||
"http://hello",
|
||||
}
|
||||
|
||||
for i, tt := range pass {
|
||||
f := &IPAddressPort{}
|
||||
if err := f.Set(tt); err != nil {
|
||||
t.Errorf("#%d: unexpected error from IPAddressPort.Set(%q): %v", i, tt, err)
|
||||
}
|
||||
}
|
||||
|
||||
for i, tt := range fail {
|
||||
f := &IPAddressPort{}
|
||||
if err := f.Set(tt); err == nil {
|
||||
t.Errorf("#%d: expected error from IPAddressPort.Set(%q)", i, tt, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPAddressPortString(t *testing.T) {
|
||||
f := &IPAddressPort{}
|
||||
if err := f.Set("127.0.0.1:4001"); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
want := "127.0.0.1:4001"
|
||||
got := f.String()
|
||||
if want != got {
|
||||
t.Fatalf("IPAddressPort.String() value should be %q, got %q", want, got)
|
||||
}
|
||||
}
|
|
@ -48,3 +48,9 @@ func (us *URLs) String() string {
|
|||
}
|
||||
return strings.Join(all, ",")
|
||||
}
|
||||
|
||||
func NewURLs(init string) *URLs {
|
||||
v := &URLs{}
|
||||
v.Set(init)
|
||||
return v
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue