mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #1221 from philips/fixing-stuff
pkg/types: introduce a URLs type
This commit is contained in:
commit
b1fc0feb72
@ -6,6 +6,9 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/pkg/flags"
|
||||||
|
"github.com/coreos/etcd/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cluster is a list of Members that belong to the same raft cluster
|
// Cluster is a list of Members that belong to the same raft cluster
|
||||||
@ -71,7 +74,8 @@ func (c *Cluster) Set(s string) error {
|
|||||||
if len(urls) == 0 || urls[0] == "" {
|
if len(urls) == 0 || urls[0] == "" {
|
||||||
return fmt.Errorf("Empty URL given for %q", name)
|
return fmt.Errorf("Empty URL given for %q", name)
|
||||||
}
|
}
|
||||||
m := newMember(name, urls, nil)
|
|
||||||
|
m := newMember(name, types.URLs(*flags.NewURLsValue(strings.Join(urls, ","))), nil)
|
||||||
err := c.Add(*m)
|
err := c.Add(*m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -5,9 +5,10 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const machineKVPrefix = "/_etcd/machines/"
|
const machineKVPrefix = "/_etcd/machines/"
|
||||||
@ -22,9 +23,8 @@ type Member struct {
|
|||||||
|
|
||||||
// newMember creates a Member without an ID and generates one based on the
|
// newMember creates a Member without an ID and generates one based on the
|
||||||
// name, peer URLs. This is used for bootstrapping.
|
// name, peer URLs. This is used for bootstrapping.
|
||||||
func newMember(name string, peerURLs []string, now *time.Time) *Member {
|
func newMember(name string, peerURLs types.URLs, now *time.Time) *Member {
|
||||||
sort.Strings(peerURLs)
|
m := &Member{Name: name, PeerURLs: peerURLs.StringSlice()}
|
||||||
m := &Member{Name: name, PeerURLs: peerURLs}
|
|
||||||
|
|
||||||
b := []byte(m.Name)
|
b := []byte(m.Name)
|
||||||
for _, p := range m.PeerURLs {
|
for _, p := range m.PeerURLs {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package etcdserver
|
package etcdserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -18,8 +19,8 @@ func TestMemberTime(t *testing.T) {
|
|||||||
mem *Member
|
mem *Member
|
||||||
id int64
|
id int64
|
||||||
}{
|
}{
|
||||||
{newMember("mem1", []string{"http://10.0.0.8:2379"}, nil), 7206348984215161146},
|
{newMember("mem1", []url.URL{{Scheme: "http", Host: "10.0.0.8:2379"}}, nil), 7206348984215161146},
|
||||||
{newMember("mem1", []string{"http://10.0.0.1:2379"}, timeParse("1984-12-23T15:04:05Z")), 5483967913615174889},
|
{newMember("mem1", []url.URL{{Scheme: "http", Host: "10.0.0.1:2379"}}, timeParse("1984-12-23T15:04:05Z")), 5483967913615174889},
|
||||||
}
|
}
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
if tt.mem.ID != tt.id {
|
if tt.mem.ID != tt.id {
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||||
|
"github.com/coreos/etcd/pkg/types"
|
||||||
"github.com/coreos/etcd/raft"
|
"github.com/coreos/etcd/raft"
|
||||||
"github.com/coreos/etcd/raft/raftpb"
|
"github.com/coreos/etcd/raft/raftpb"
|
||||||
"github.com/coreos/etcd/store"
|
"github.com/coreos/etcd/store"
|
||||||
@ -81,7 +82,7 @@ type EtcdServer struct {
|
|||||||
done chan struct{}
|
done chan struct{}
|
||||||
|
|
||||||
Name string
|
Name string
|
||||||
ClientURLs []string
|
ClientURLs types.URLs
|
||||||
|
|
||||||
Node raft.Node
|
Node raft.Node
|
||||||
Store store.Store
|
Store store.Store
|
||||||
@ -340,7 +341,7 @@ func (s *EtcdServer) sync(timeout time.Duration) {
|
|||||||
// TODO: take care of info fetched from cluster store after having reconfig.
|
// TODO: take care of info fetched from cluster store after having reconfig.
|
||||||
func (s *EtcdServer) publish(retryInterval time.Duration) {
|
func (s *EtcdServer) publish(retryInterval time.Duration) {
|
||||||
m := *s.ClusterStore.Get().FindName(s.Name)
|
m := *s.ClusterStore.Get().FindName(s.Name)
|
||||||
m.ClientURLs = s.ClientURLs
|
m.ClientURLs = s.ClientURLs.StringSlice()
|
||||||
b, err := json.Marshal(m)
|
b, err := json.Marshal(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("etcdserver: json marshal error: %v", err)
|
log.Printf("etcdserver: json marshal error: %v", err)
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
@ -836,7 +837,7 @@ func TestPublish(t *testing.T) {
|
|||||||
w := &waitWithResponse{ch: ch}
|
w := &waitWithResponse{ch: ch}
|
||||||
srv := &EtcdServer{
|
srv := &EtcdServer{
|
||||||
Name: "node1",
|
Name: "node1",
|
||||||
ClientURLs: []string{"a", "b"},
|
ClientURLs: []url.URL{{Scheme: "http", Host: "a"}, {Scheme: "http", Host: "b"}},
|
||||||
Node: n,
|
Node: n,
|
||||||
ClusterStore: cs,
|
ClusterStore: cs,
|
||||||
w: w,
|
w: w,
|
||||||
@ -854,7 +855,7 @@ func TestPublish(t *testing.T) {
|
|||||||
if r.Method != "PUT" {
|
if r.Method != "PUT" {
|
||||||
t.Errorf("method = %s, want PUT", r.Method)
|
t.Errorf("method = %s, want PUT", r.Method)
|
||||||
}
|
}
|
||||||
wm := Member{ID: 1, Name: "node1", ClientURLs: []string{"a", "b"}}
|
wm := Member{ID: 1, Name: "node1", ClientURLs: []string{"http://a", "http://b"}}
|
||||||
if r.Path != wm.storeKey() {
|
if r.Path != wm.storeKey() {
|
||||||
t.Errorf("path = %s, want %s", r.Path, wm.storeKey())
|
t.Errorf("path = %s, want %s", r.Path, wm.storeKey())
|
||||||
}
|
}
|
||||||
|
17
main.go
17
main.go
@ -64,10 +64,10 @@ func init() {
|
|||||||
flag.Var(cluster, "bootstrap-config", "Initial cluster configuration for bootstrapping")
|
flag.Var(cluster, "bootstrap-config", "Initial cluster configuration for bootstrapping")
|
||||||
cluster.Set("default=http://localhost:2380,default=http://localhost:7001")
|
cluster.Set("default=http://localhost:2380,default=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.NewURLsValue("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.NewURLsValue("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.NewURLsValue("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(flagtypes.NewURLsValue("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(cors, "cors", "Comma-separated white list of origins for CORS (cross-origin resource sharing).")
|
||||||
|
|
||||||
@ -188,9 +188,14 @@ func startEtcd() {
|
|||||||
|
|
||||||
cls := etcdserver.NewClusterStore(st, *cluster)
|
cls := etcdserver.NewClusterStore(st, *cluster)
|
||||||
|
|
||||||
|
acurls, err := pkg.URLsFromFlags(flag.CommandLine, "advertise-client-urls", "addr", clientTLSInfo)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
s := &etcdserver.EtcdServer{
|
s := &etcdserver.EtcdServer{
|
||||||
Name: *name,
|
Name: *name,
|
||||||
ClientURLs: strings.Split(acurls.String(), ","),
|
ClientURLs: acurls,
|
||||||
Store: st,
|
Store: st,
|
||||||
Node: n,
|
Node: n,
|
||||||
Storage: struct {
|
Storage: struct {
|
||||||
@ -211,7 +216,7 @@ func startEtcd() {
|
|||||||
}
|
}
|
||||||
ph := etcdhttp.NewPeerHandler(s)
|
ph := etcdhttp.NewPeerHandler(s)
|
||||||
|
|
||||||
lpurls, err := pkg.URLsFromFlags(flag.CommandLine, "listen-peer-urls", "peer-bind-addr", clientTLSInfo)
|
lpurls, err := pkg.URLsFromFlags(flag.CommandLine, "listen-peer-urls", "peer-bind-addr", peerTLSInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -101,5 +101,5 @@ func URLsFromFlags(fs *flag.FlagSet, urlsFlagName string, addrFlagName string, t
|
|||||||
return []url.URL{addrURL}, nil
|
return []url.URL{addrURL}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return []url.URL(*fs.Lookup(urlsFlagName).Value.(*flags.URLs)), nil
|
return []url.URL(*fs.Lookup(urlsFlagName).Value.(*flags.URLsValue)), nil
|
||||||
}
|
}
|
||||||
|
@ -77,8 +77,8 @@ func TestURLsFromFlags(t *testing.T) {
|
|||||||
args: []string{"-urls=https://192.0.3.17:2930,http://127.0.0.1:1024"},
|
args: []string{"-urls=https://192.0.3.17:2930,http://127.0.0.1:1024"},
|
||||||
tlsInfo: transport.TLSInfo{},
|
tlsInfo: transport.TLSInfo{},
|
||||||
wantURLs: []url.URL{
|
wantURLs: []url.URL{
|
||||||
url.URL{Scheme: "https", Host: "192.0.3.17:2930"},
|
|
||||||
url.URL{Scheme: "http", Host: "127.0.0.1:1024"},
|
url.URL{Scheme: "http", Host: "127.0.0.1:1024"},
|
||||||
|
url.URL{Scheme: "https", Host: "192.0.3.17:2930"},
|
||||||
},
|
},
|
||||||
wantFail: false,
|
wantFail: false,
|
||||||
},
|
},
|
||||||
@ -117,7 +117,7 @@ func TestURLsFromFlags(t *testing.T) {
|
|||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
fs := flag.NewFlagSet("test", flag.PanicOnError)
|
fs := flag.NewFlagSet("test", flag.PanicOnError)
|
||||||
fs.Var(flags.NewURLs("http://127.0.0.1:2379"), "urls", "")
|
fs.Var(flags.NewURLsValue("http://127.0.0.1:2379"), "urls", "")
|
||||||
fs.Var(&flags.IPAddressPort{}, "addr", "")
|
fs.Var(&flags.IPAddressPort{}, "addr", "")
|
||||||
|
|
||||||
if err := fs.Parse(tt.args); err != nil {
|
if err := fs.Parse(tt.args); err != nil {
|
||||||
|
@ -1,47 +1,27 @@
|
|||||||
package flags
|
package flags
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// URLs implements the flag.Value interface to allow users to define multiple
|
type URLsValue types.URLs
|
||||||
// URLs on the command-line
|
|
||||||
type URLs []url.URL
|
|
||||||
|
|
||||||
// Set parses a command line set of URLs formatted like:
|
// Set parses a command line set of URLs formatted like:
|
||||||
// http://127.0.0.1:7001,http://10.1.1.2:80
|
// http://127.0.0.1:7001,http://10.1.1.2:80
|
||||||
func (us *URLs) Set(s string) error {
|
func (us *URLsValue) Set(s string) error {
|
||||||
strs := strings.Split(s, ",")
|
strs := strings.Split(s, ",")
|
||||||
all := make([]url.URL, len(strs))
|
nus, err := types.NewURLs(strs)
|
||||||
if len(all) == 0 {
|
|
||||||
return errors.New("no valid URLs given")
|
|
||||||
}
|
|
||||||
for i, in := range strs {
|
|
||||||
in = strings.TrimSpace(in)
|
|
||||||
u, err := url.Parse(in)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if u.Scheme != "http" && u.Scheme != "https" {
|
|
||||||
return fmt.Errorf("URL scheme must be http or https: %s", s)
|
*us = URLsValue(nus)
|
||||||
}
|
|
||||||
if _, _, err := net.SplitHostPort(u.Host); err != nil {
|
|
||||||
return fmt.Errorf(`URL address does not have the form "host:port": %s`, s)
|
|
||||||
}
|
|
||||||
if u.Path != "" {
|
|
||||||
return fmt.Errorf("URL must not contain a path: %s", s)
|
|
||||||
}
|
|
||||||
all[i] = *u
|
|
||||||
}
|
|
||||||
*us = all
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (us *URLs) String() string {
|
func (us *URLsValue) String() string {
|
||||||
all := make([]string, len(*us))
|
all := make([]string, len(*us))
|
||||||
for i, u := range *us {
|
for i, u := range *us {
|
||||||
all[i] = u.String()
|
all[i] = u.String()
|
||||||
@ -49,8 +29,8 @@ func (us *URLs) String() string {
|
|||||||
return strings.Join(all, ",")
|
return strings.Join(all, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewURLs(init string) *URLs {
|
func NewURLsValue(init string) *URLsValue {
|
||||||
v := &URLs{}
|
v := &URLsValue{}
|
||||||
v.Set(init)
|
v.Set(init)
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestValidateURLsBad(t *testing.T) {
|
func TestValidateURLsValueBad(t *testing.T) {
|
||||||
tests := []string{
|
tests := []string{
|
||||||
// bad IP specification
|
// bad IP specification
|
||||||
":4001",
|
":4001",
|
||||||
@ -24,14 +24,14 @@ func TestValidateURLsBad(t *testing.T) {
|
|||||||
"http://10.1.1.1",
|
"http://10.1.1.1",
|
||||||
}
|
}
|
||||||
for i, in := range tests {
|
for i, in := range tests {
|
||||||
u := URLs{}
|
u := URLsValue{}
|
||||||
if err := u.Set(in); err == nil {
|
if err := u.Set(in); err == nil {
|
||||||
t.Errorf(`#%d: unexpected nil error for in=%q`, i, in)
|
t.Errorf(`#%d: unexpected nil error for in=%q`, i, in)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateURLsGood(t *testing.T) {
|
func TestValidateURLsValueGood(t *testing.T) {
|
||||||
tests := []string{
|
tests := []string{
|
||||||
"https://1.2.3.4:8080",
|
"https://1.2.3.4:8080",
|
||||||
"http://10.1.1.1:80",
|
"http://10.1.1.1:80",
|
||||||
@ -39,7 +39,7 @@ func TestValidateURLsGood(t *testing.T) {
|
|||||||
"http://:80",
|
"http://:80",
|
||||||
}
|
}
|
||||||
for i, in := range tests {
|
for i, in := range tests {
|
||||||
u := URLs{}
|
u := URLsValue{}
|
||||||
if err := u.Set(in); err != nil {
|
if err := u.Set(in); err != nil {
|
||||||
t.Errorf("#%d: err=%v, want nil for in=%q", i, err, in)
|
t.Errorf("#%d: err=%v, want nil for in=%q", i, err, in)
|
||||||
}
|
}
|
||||||
|
56
pkg/types/urls.go
Normal file
56
pkg/types/urls.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type URLs []url.URL
|
||||||
|
|
||||||
|
func (us *URLs) Sort() {
|
||||||
|
sort.Sort(us)
|
||||||
|
}
|
||||||
|
func (us URLs) Len() int { return len(us) }
|
||||||
|
func (us URLs) Less(i, j int) bool { return us[i].String() < us[j].String() }
|
||||||
|
func (us URLs) Swap(i, j int) { us[i], us[j] = us[j], us[i] }
|
||||||
|
|
||||||
|
func (us URLs) StringSlice() []string {
|
||||||
|
out := make([]string, len(us))
|
||||||
|
for i := range us {
|
||||||
|
out[i] = us[i].String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewURLs(strs []string) (URLs, error) {
|
||||||
|
all := make([]url.URL, len(strs))
|
||||||
|
if len(all) == 0 {
|
||||||
|
return nil, errors.New("no valid URLs given")
|
||||||
|
}
|
||||||
|
for i, in := range strs {
|
||||||
|
in = strings.TrimSpace(in)
|
||||||
|
u, err := url.Parse(in)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if u.Scheme != "http" && u.Scheme != "https" {
|
||||||
|
return nil, fmt.Errorf("URL scheme must be http or https: %s", in)
|
||||||
|
}
|
||||||
|
if _, _, err := net.SplitHostPort(u.Host); err != nil {
|
||||||
|
return nil, fmt.Errorf(`URL address does not have the form "host:port": %s`, in)
|
||||||
|
}
|
||||||
|
if u.Path != "" {
|
||||||
|
return nil, fmt.Errorf("URL must not contain a path: %s", in)
|
||||||
|
}
|
||||||
|
all[i] = *u
|
||||||
|
}
|
||||||
|
us := URLs(all)
|
||||||
|
us.Sort()
|
||||||
|
|
||||||
|
return us, nil
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user