github.com/sym3tri/etcd@v0.2.1-0.20140422215517-a563d82f95d6/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  		//
   133  		// Waiting for each server to start when ssl is a workaround.
   134  		// Autotest machines are dramatically slow, and it could spend
   135  		// several seconds to build TSL connections between servers. That
   136  		// is extremely terribe when the second machine joins the cluster
   137  		// because the cluster is out of work at this time. The guy
   138  		// tries to join during this time will fail, and current implementation
   139  		// makes it fail after just one-time try(bug in #661). This
   140  		// makes the cluster start with N-1 machines.
   141  		// TODO(yichengq): It should be fixed.
   142  		if i == 0 || ssl {
   143  			client := buildClient()
   144  			err = WaitForServer("127.0.0.1:400"+strconv.Itoa(i+1), client, "http")
   145  			if err != nil {
   146  				return nil, nil, err
   147  			}
   148  		}
   149  	}
   150  
   151  	return argGroup, etcds, nil
   152  }
   153  
   154  // Destroy all the nodes in the cluster
   155  func DestroyCluster(etcds []*os.Process) error {
   156  	for _, etcd := range etcds {
   157  		if etcd == nil {
   158  			continue
   159  		}
   160  		err := etcd.Kill()
   161  		if err != nil {
   162  			panic(err.Error())
   163  		}
   164  		etcd.Release()
   165  	}
   166  	return nil
   167  }
   168  
   169  //
   170  func Monitor(size int, allowDeadNum int, leaderChan chan string, all chan bool, stop chan bool) {
   171  	leaderMap := make(map[int]string)
   172  	baseAddrFormat := "http://0.0.0.0:400%d"
   173  
   174  	for {
   175  		knownLeader := "unknown"
   176  		dead := 0
   177  		var i int
   178  
   179  		for i = 0; i < size; i++ {
   180  			leader, err := getLeader(fmt.Sprintf(baseAddrFormat, i+1))
   181  
   182  			if err == nil {
   183  				leaderMap[i] = leader
   184  
   185  				if knownLeader == "unknown" {
   186  					knownLeader = leader
   187  				} else {
   188  					if leader != knownLeader {
   189  						break
   190  					}
   191  
   192  				}
   193  
   194  			} else {
   195  				dead++
   196  				if dead > allowDeadNum {
   197  					break
   198  				}
   199  			}
   200  
   201  		}
   202  
   203  		if i == size {
   204  			select {
   205  			case <-stop:
   206  				return
   207  			case <-leaderChan:
   208  				leaderChan <- knownLeader
   209  			default:
   210  				leaderChan <- knownLeader
   211  			}
   212  
   213  		}
   214  		if dead == 0 {
   215  			select {
   216  			case <-all:
   217  				all <- true
   218  			default:
   219  				all <- true
   220  			}
   221  		}
   222  
   223  		time.Sleep(time.Millisecond * 10)
   224  	}
   225  
   226  }
   227  
   228  func getLeader(addr string) (string, error) {
   229  
   230  	resp, err := client.Get(addr + "/v1/leader")
   231  
   232  	if err != nil {
   233  		return "", err
   234  	}
   235  
   236  	if resp.StatusCode != http.StatusOK {
   237  		resp.Body.Close()
   238  		return "", fmt.Errorf("no leader")
   239  	}
   240  
   241  	b, err := ioutil.ReadAll(resp.Body)
   242  
   243  	resp.Body.Close()
   244  
   245  	if err != nil {
   246  		return "", err
   247  	}
   248  
   249  	return string(b), nil
   250  
   251  }
   252  
   253  // Dial with timeout
   254  func dialTimeoutFast(network, addr string) (net.Conn, error) {
   255  	return net.DialTimeout(network, addr, time.Millisecond*10)
   256  }