github.com/jpetazzo/etcd@v0.2.1-0.20140113055439-97f1363afac5/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  	"fmt"
    21  	"github.com/coreos/go-etcd/etcd"
    22  	"io/ioutil"
    23  	"net"
    24  	"net/http"
    25  	"os"
    26  	"strconv"
    27  	"time"
    28  )
    29  
    30  var client = http.Client{
    31  	Transport: &http.Transport{
    32  		Dial: dialTimeoutFast,
    33  	},
    34  }
    35  
    36  // Sending set commands
    37  func Set(stop chan bool) {
    38  
    39  	stopSet := false
    40  	i := 0
    41  	c := etcd.NewClient(nil)
    42  	for {
    43  		key := fmt.Sprintf("%s_%v", "foo", i)
    44  
    45  		result, err := c.Set(key, "bar", 0)
    46  
    47  		if err != nil || result.Node.Key != "/"+key || result.Node.Value != "bar" {
    48  			select {
    49  			case <-stop:
    50  				stopSet = true
    51  
    52  			default:
    53  			}
    54  		}
    55  
    56  		select {
    57  		case <-stop:
    58  			stopSet = true
    59  
    60  		default:
    61  		}
    62  
    63  		if stopSet {
    64  			break
    65  		}
    66  
    67  		i++
    68  	}
    69  	stop <- true
    70  }
    71  
    72  // Create a cluster of etcd nodes
    73  func CreateCluster(size int, procAttr *os.ProcAttr, ssl bool) ([][]string, []*os.Process, error) {
    74  	argGroup := make([][]string, size)
    75  
    76  	sslServer1 := []string{"-peer-ca-file=../../fixtures/ca/ca.crt",
    77  		"-peer-cert-file=../../fixtures/ca/server.crt",
    78  		"-peer-key-file=../../fixtures/ca/server.key.insecure",
    79  	}
    80  
    81  	sslServer2 := []string{"-peer-ca-file=../../fixtures/ca/ca.crt",
    82  		"-peer-cert-file=../../fixtures/ca/server2.crt",
    83  		"-peer-key-file=../../fixtures/ca/server2.key.insecure",
    84  	}
    85  
    86  	for i := 0; i < size; i++ {
    87  		if i == 0 {
    88  			argGroup[i] = []string{"etcd", "-data-dir=/tmp/node1", "-name=node1"}
    89  			if ssl {
    90  				argGroup[i] = append(argGroup[i], sslServer1...)
    91  			}
    92  		} else {
    93  			strI := strconv.Itoa(i + 1)
    94  			argGroup[i] = []string{"etcd", "-name=node" + strI, "-addr=127.0.0.1:400" + strI, "-peer-addr=127.0.0.1:700" + strI, "-data-dir=/tmp/node" + strI, "-peers=127.0.0.1:7001"}
    95  			if ssl {
    96  				argGroup[i] = append(argGroup[i], sslServer2...)
    97  			}
    98  		}
    99  	}
   100  
   101  	etcds := make([]*os.Process, size)
   102  
   103  	for i, _ := range etcds {
   104  		var err error
   105  		etcds[i], err = os.StartProcess(EtcdBinPath, append(argGroup[i], "-f"), procAttr)
   106  		if err != nil {
   107  			return nil, nil, err
   108  		}
   109  
   110  		// TODOBP: Change this sleep to wait until the master is up.
   111  		// The problem is that if the master isn't up then the children
   112  		// have to retry. This retry can take upwards of 15 seconds
   113  		// which slows tests way down and some of them fail.
   114  		if i == 0 {
   115  			time.Sleep(time.Second * 2)
   116  		}
   117  	}
   118  
   119  	return argGroup, etcds, nil
   120  }
   121  
   122  // Destroy all the nodes in the cluster
   123  func DestroyCluster(etcds []*os.Process) error {
   124  	for _, etcd := range etcds {
   125  		err := etcd.Kill()
   126  		if err != nil {
   127  			panic(err.Error())
   128  		}
   129  		etcd.Release()
   130  	}
   131  	return nil
   132  }
   133  
   134  //
   135  func Monitor(size int, allowDeadNum int, leaderChan chan string, all chan bool, stop chan bool) {
   136  	leaderMap := make(map[int]string)
   137  	baseAddrFormat := "http://0.0.0.0:400%d"
   138  
   139  	for {
   140  		knownLeader := "unknown"
   141  		dead := 0
   142  		var i int
   143  
   144  		for i = 0; i < size; i++ {
   145  			leader, err := getLeader(fmt.Sprintf(baseAddrFormat, i+1))
   146  
   147  			if err == nil {
   148  				leaderMap[i] = leader
   149  
   150  				if knownLeader == "unknown" {
   151  					knownLeader = leader
   152  				} else {
   153  					if leader != knownLeader {
   154  						break
   155  					}
   156  
   157  				}
   158  
   159  			} else {
   160  				dead++
   161  				if dead > allowDeadNum {
   162  					break
   163  				}
   164  			}
   165  
   166  		}
   167  
   168  		if i == size {
   169  			select {
   170  			case <-stop:
   171  				return
   172  			case <-leaderChan:
   173  				leaderChan <- knownLeader
   174  			default:
   175  				leaderChan <- knownLeader
   176  			}
   177  
   178  		}
   179  		if dead == 0 {
   180  			select {
   181  			case <-all:
   182  				all <- true
   183  			default:
   184  				all <- true
   185  			}
   186  		}
   187  
   188  		time.Sleep(time.Millisecond * 10)
   189  	}
   190  
   191  }
   192  
   193  func getLeader(addr string) (string, error) {
   194  
   195  	resp, err := client.Get(addr + "/v1/leader")
   196  
   197  	if err != nil {
   198  		return "", err
   199  	}
   200  
   201  	if resp.StatusCode != http.StatusOK {
   202  		resp.Body.Close()
   203  		return "", fmt.Errorf("no leader")
   204  	}
   205  
   206  	b, err := ioutil.ReadAll(resp.Body)
   207  
   208  	resp.Body.Close()
   209  
   210  	if err != nil {
   211  		return "", err
   212  	}
   213  
   214  	return string(b), nil
   215  
   216  }
   217  
   218  // Dial with timeout
   219  func dialTimeoutFast(network, addr string) (net.Conn, error) {
   220  	return net.DialTimeout(network, addr, time.Millisecond*10)
   221  }