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  }