github.com/coreos/mantle@v0.13.0/kola/tests/etcd/util.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 etcd
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/json"
    20  	"fmt"
    21  	"math/rand"
    22  	"strconv"
    23  	"strings"
    24  	"time"
    25  
    26  	"github.com/coreos/mantle/kola/cluster"
    27  	"github.com/coreos/mantle/platform"
    28  	"github.com/coreos/mantle/util"
    29  )
    30  
    31  // GetClusterHealth polls etcdctl cluster-health command until success
    32  // or maximum retries have been reached. Can be effectively used to
    33  // block a test until the etcd cluster is up and running.
    34  func GetClusterHealth(c cluster.TestCluster, m platform.Machine, csize int) error {
    35  	var err error
    36  	var b []byte
    37  
    38  	checker := func() error {
    39  		b, err := c.SSH(m, "etcdctl cluster-health")
    40  		if err != nil {
    41  			return err
    42  		}
    43  
    44  		// repsonse should include "healthy" for each machine and for cluster
    45  		if strings.Count(string(b), "healthy") != (csize*2)+1 {
    46  			return fmt.Errorf("unexpected etcdctl output")
    47  		}
    48  
    49  		plog.Infof("cluster healthy")
    50  		return nil
    51  	}
    52  
    53  	err = util.Retry(15, 10*time.Second, checker)
    54  	if err != nil {
    55  		return fmt.Errorf("health polling failed: %v: %s", err, b)
    56  	}
    57  
    58  	return nil
    59  }
    60  
    61  // setKeys sets n random keys and values across each machine in a
    62  // cluster and returns these values to later be checked with checkKeys.
    63  // If all the values don't get set due to a machine that is down and
    64  // error is NOT returned. An error is returned if no keys are able to be
    65  // set.
    66  func setKeys(c cluster.TestCluster, n int) (map[string]string, error) {
    67  	var written = map[string]string{}
    68  	for _, m := range c.Machines() {
    69  		for i := 0; i < n; i++ {
    70  			// random key and value, may overwrwite previous sets if
    71  			// collision which is fine
    72  			key := strconv.Itoa(rand.Int())[0:3]
    73  			value := strconv.Itoa(rand.Int())[0:3]
    74  
    75  			b, err := c.SSH(m, fmt.Sprintf("curl -s -w %%{http_code} -s http://127.0.0.1:2379/v2/keys/%v -XPUT -d value=%v", key, value))
    76  			if err != nil {
    77  				return nil, err
    78  			}
    79  
    80  			// check for 201 or 200 resp header
    81  			if !bytes.HasSuffix(b, []byte("200")) && !bytes.HasSuffix(b, []byte("201")) {
    82  				continue
    83  			}
    84  
    85  			written[key] = value
    86  		}
    87  	}
    88  	if len(written) == 0 {
    89  		return nil, fmt.Errorf("failed to write any keys")
    90  	}
    91  
    92  	plog.Infof("wrote %v keys", len(written))
    93  	return written, nil
    94  }
    95  
    96  // checkKeys tests that each node in the cluster has the full provided
    97  // key set in keyMap. Quorum get must be used.
    98  func checkKeys(c cluster.TestCluster, keyMap map[string]string) error {
    99  	for i, m := range c.Machines() {
   100  		for k, v := range keyMap {
   101  			cmd := fmt.Sprintf("curl -s http://127.0.0.1:2379/v2/keys/%v?quorum=true", k)
   102  
   103  			b, err := c.SSH(m, cmd)
   104  			if err != nil {
   105  				return fmt.Errorf("error curling key: %v", err)
   106  			}
   107  
   108  			var jsonMap map[string]interface{}
   109  			err = json.Unmarshal(b, &jsonMap)
   110  			if err != nil {
   111  				return err
   112  			}
   113  
   114  			// error code?
   115  			errorCode, ok := jsonMap["errorCode"]
   116  			if ok {
   117  				msg := jsonMap["message"]
   118  				return fmt.Errorf("machine %v errorCode %v: %v: %s", i, errorCode, msg, b)
   119  			}
   120  
   121  			node, ok := jsonMap["node"]
   122  			if !ok {
   123  				return fmt.Errorf("retrieving key in CheckKeys, no node in resp")
   124  			}
   125  
   126  			n := node.(map[string]interface{})
   127  			value, ok := n["value"]
   128  			if !ok {
   129  				return fmt.Errorf("retrieving key in CheckKeys, no value in resp")
   130  			}
   131  
   132  			if value != v {
   133  				return fmt.Errorf("checkKeys got incorrect value! expected:%v got: %v", v, value)
   134  			}
   135  		}
   136  	}
   137  	plog.Infof("checked %v keys", len(keyMap))
   138  	return nil
   139  }