github.com/matm/etcd@v0.3.1-0.20140328024009-5b4a473f1453/tests/functional/util.go (about) 1 /* 2 Copyright 2013 CoreOS Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package test 18 19 import ( 20 "errors" 21 "fmt" 22 "io/ioutil" 23 "net" 24 "net/http" 25 "os" 26 "strconv" 27 "time" 28 29 "github.com/coreos/etcd/third_party/github.com/coreos/go-etcd/etcd" 30 ) 31 32 var client = http.Client{ 33 Transport: &http.Transport{ 34 Dial: dialTimeoutFast, 35 }, 36 } 37 38 // Sending set commands 39 func Set(stop chan bool) { 40 41 stopSet := false 42 i := 0 43 c := etcd.NewClient(nil) 44 for { 45 key := fmt.Sprintf("%s_%v", "foo", i) 46 47 result, err := c.Set(key, "bar", 0) 48 49 if err != nil || result.Node.Key != "/"+key || result.Node.Value != "bar" { 50 select { 51 case <-stop: 52 stopSet = true 53 54 default: 55 } 56 } 57 58 select { 59 case <-stop: 60 stopSet = true 61 62 default: 63 } 64 65 if stopSet { 66 break 67 } 68 69 i++ 70 } 71 stop <- true 72 } 73 74 func WaitForServer(host string, client http.Client, scheme string) error { 75 path := fmt.Sprintf("%s://%s/v2/keys/", scheme, host) 76 77 var resp *http.Response 78 var err error 79 for i := 0; i < 10; i++ { 80 time.Sleep(1 * time.Second) 81 82 resp, err = client.Get(path) 83 if err == nil && resp.StatusCode == 200 { 84 return nil 85 } 86 } 87 88 return errors.New(fmt.Sprintf("etcd server was not reachable in a long time, last-time response and error: %v; %v", resp, err)) 89 } 90 91 // Create a cluster of etcd nodes 92 func CreateCluster(size int, procAttr *os.ProcAttr, ssl bool) ([][]string, []*os.Process, error) { 93 argGroup := make([][]string, size) 94 95 sslServer1 := []string{"-peer-ca-file=../../fixtures/ca/ca.crt", 96 "-peer-cert-file=../../fixtures/ca/server.crt", 97 "-peer-key-file=../../fixtures/ca/server.key.insecure", 98 } 99 100 sslServer2 := []string{"-peer-ca-file=../../fixtures/ca/ca.crt", 101 "-peer-cert-file=../../fixtures/ca/server2.crt", 102 "-peer-key-file=../../fixtures/ca/server2.key.insecure", 103 } 104 105 for i := 0; i < size; i++ { 106 if i == 0 { 107 argGroup[i] = []string{"etcd", "-data-dir=/tmp/node1", "-name=node1"} 108 if ssl { 109 argGroup[i] = append(argGroup[i], sslServer1...) 110 } 111 } else { 112 strI := strconv.Itoa(i + 1) 113 argGroup[i] = []string{"etcd", "-name=node" + strI, fmt.Sprintf("-addr=127.0.0.1:%d", 4001+i), fmt.Sprintf("-peer-addr=127.0.0.1:%d", 7001+i), "-data-dir=/tmp/node" + strI, "-peers=127.0.0.1:7001"} 114 if ssl { 115 argGroup[i] = append(argGroup[i], sslServer2...) 116 } 117 } 118 } 119 120 etcds := make([]*os.Process, size) 121 122 for i := range etcds { 123 var err error 124 etcds[i], err = os.StartProcess(EtcdBinPath, append(argGroup[i], "-f"), procAttr) 125 if err != nil { 126 return nil, nil, err 127 } 128 129 // The problem is that if the master isn't up then the children 130 // have to retry. This retry can take upwards of 15 seconds 131 // which slows tests way down and some of them fail. 132 if i == 0 { 133 client := buildClient() 134 err = WaitForServer("127.0.0.1:4001", client, "http") 135 if err != nil { 136 return nil, nil, err 137 } 138 } 139 } 140 141 return argGroup, etcds, nil 142 } 143 144 // Destroy all the nodes in the cluster 145 func DestroyCluster(etcds []*os.Process) error { 146 for _, etcd := range etcds { 147 err := etcd.Kill() 148 if err != nil { 149 panic(err.Error()) 150 } 151 etcd.Release() 152 } 153 return nil 154 } 155 156 // 157 func Monitor(size int, allowDeadNum int, leaderChan chan string, all chan bool, stop chan bool) { 158 leaderMap := make(map[int]string) 159 baseAddrFormat := "http://0.0.0.0:400%d" 160 161 for { 162 knownLeader := "unknown" 163 dead := 0 164 var i int 165 166 for i = 0; i < size; i++ { 167 leader, err := getLeader(fmt.Sprintf(baseAddrFormat, i+1)) 168 169 if err == nil { 170 leaderMap[i] = leader 171 172 if knownLeader == "unknown" { 173 knownLeader = leader 174 } else { 175 if leader != knownLeader { 176 break 177 } 178 179 } 180 181 } else { 182 dead++ 183 if dead > allowDeadNum { 184 break 185 } 186 } 187 188 } 189 190 if i == size { 191 select { 192 case <-stop: 193 return 194 case <-leaderChan: 195 leaderChan <- knownLeader 196 default: 197 leaderChan <- knownLeader 198 } 199 200 } 201 if dead == 0 { 202 select { 203 case <-all: 204 all <- true 205 default: 206 all <- true 207 } 208 } 209 210 time.Sleep(time.Millisecond * 10) 211 } 212 213 } 214 215 func getLeader(addr string) (string, error) { 216 217 resp, err := client.Get(addr + "/v1/leader") 218 219 if err != nil { 220 return "", err 221 } 222 223 if resp.StatusCode != http.StatusOK { 224 resp.Body.Close() 225 return "", fmt.Errorf("no leader") 226 } 227 228 b, err := ioutil.ReadAll(resp.Body) 229 230 resp.Body.Close() 231 232 if err != nil { 233 return "", err 234 } 235 236 return string(b), nil 237 238 } 239 240 // Dial with timeout 241 func dialTimeoutFast(network, addr string) (net.Conn, error) { 242 return net.DialTimeout(network, addr, time.Millisecond*10) 243 }