github.com/coreos/mantle@v0.13.0/platform/local/etcd.go (about) 1 // Copyright 2015 CoreOS, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package local 16 17 import ( 18 "fmt" 19 "io/ioutil" 20 "net" 21 "net/http" 22 "net/url" 23 "os" 24 25 "github.com/coreos/etcd/etcdserver" 26 "github.com/coreos/etcd/etcdserver/api/v2http" 27 "github.com/coreos/etcd/pkg/types" 28 ) 29 30 const ( 31 memberName = "simple" 32 clusterName = "simple-cluster" 33 tempPrefix = "simple-etcd-" 34 35 // No peer URL exists but etcd doesn't allow the value to be empty. 36 peerURL = "http://localhost:0" 37 clusterCfg = memberName + "=" + peerURL 38 ) 39 40 // SimpleEtcd provides a single node etcd server. 41 type SimpleEtcd struct { 42 Port int 43 listener net.Listener 44 server *etcdserver.EtcdServer 45 dataDir string 46 } 47 48 func NewSimpleEtcd() (*SimpleEtcd, error) { 49 var err error 50 se := &SimpleEtcd{} 51 se.listener, err = net.Listen("tcp", ":0") 52 if err != nil { 53 return nil, err 54 } 55 56 se.Port = se.listener.Addr().(*net.TCPAddr).Port 57 clientURLs, err := interfaceURLs(se.Port) 58 if err != nil { 59 se.Destroy() 60 return nil, err 61 } 62 63 se.dataDir, err = ioutil.TempDir("", tempPrefix) 64 if err != nil { 65 se.Destroy() 66 return nil, err 67 } 68 69 peerURLs, err := types.NewURLs([]string{peerURL}) 70 if err != nil { 71 se.Destroy() 72 return nil, err 73 } 74 75 cfg := etcdserver.ServerConfig{ 76 Name: memberName, 77 ClientURLs: clientURLs, 78 PeerURLs: peerURLs, 79 DataDir: se.dataDir, 80 InitialPeerURLsMap: types.URLsMap{ 81 memberName: peerURLs, 82 }, 83 NewCluster: true, 84 TickMs: 100, 85 ElectionTicks: 10, 86 } 87 88 se.server, err = etcdserver.NewServer(cfg) 89 if err != nil { 90 return nil, err 91 } 92 93 se.server.Start() 94 go http.Serve(se.listener, 95 v2http.NewClientHandler(se.server, cfg.ReqTimeout())) 96 97 return se, nil 98 } 99 100 func (se *SimpleEtcd) Destroy() { 101 if se.listener != nil { 102 if err := se.listener.Close(); err != nil { 103 plog.Errorf("Error closing etcd listener: %v", err) 104 } 105 } 106 107 if se.server != nil { 108 se.server.Stop() 109 } 110 111 if se.dataDir != "" { 112 if err := os.RemoveAll(se.dataDir); err != nil { 113 plog.Errorf("Error removing etcd data dir: %v", err) 114 } 115 } 116 } 117 118 // Generate all publishable URLs for a given HTTP port. 119 func interfaceURLs(port int) (types.URLs, error) { 120 allAddrs, err := net.InterfaceAddrs() 121 if err != nil { 122 return []url.URL{}, err 123 } 124 125 var allURLs types.URLs 126 for _, a := range allAddrs { 127 ip, ok := a.(*net.IPNet) 128 if !ok || !ip.IP.IsGlobalUnicast() { 129 continue 130 } 131 132 tcp := net.TCPAddr{ 133 IP: ip.IP, 134 Port: port, 135 } 136 137 u := url.URL{ 138 Scheme: "http", 139 Host: tcp.String(), 140 } 141 allURLs = append(allURLs, u) 142 } 143 144 if len(allAddrs) == 0 { 145 return []url.URL{}, fmt.Errorf("no publishable addresses") 146 } 147 148 return allURLs, nil 149 }