github.com/sl1pm4t/consul@v1.4.5-0.20190325224627-74c31c540f9c/testrpc/wait.go (about)

     1  package testrpc
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/hashicorp/consul/agent/structs"
     7  	"github.com/hashicorp/consul/testutil/retry"
     8  )
     9  
    10  type rpcFn func(string, interface{}, interface{}) error
    11  
    12  // WaitForLeader ensures we have a leader and a node registration.
    13  func WaitForLeader(t *testing.T, rpc rpcFn, dc string) {
    14  	var out structs.IndexedNodes
    15  	retry.Run(t, func(r *retry.R) {
    16  		args := &structs.DCSpecificRequest{Datacenter: dc}
    17  		if err := rpc("Catalog.ListNodes", args, &out); err != nil {
    18  			r.Fatalf("Catalog.ListNodes failed: %v", err)
    19  		}
    20  		if !out.QueryMeta.KnownLeader {
    21  			r.Fatalf("No leader")
    22  		}
    23  		if out.Index < 2 {
    24  			r.Fatalf("Consul index should be at least 2")
    25  		}
    26  	})
    27  }
    28  
    29  // WaitUntilNoLeader ensures no leader is present, useful for testing lost leadership.
    30  func WaitUntilNoLeader(t *testing.T, rpc rpcFn, dc string) {
    31  	var out structs.IndexedNodes
    32  	retry.Run(t, func(r *retry.R) {
    33  		args := &structs.DCSpecificRequest{Datacenter: dc}
    34  		if err := rpc("Catalog.ListNodes", args, &out); err == nil {
    35  			r.Fatalf("It still has a leader: %#v", out)
    36  		}
    37  		if out.QueryMeta.KnownLeader {
    38  			r.Fatalf("Has still a leader")
    39  		}
    40  	})
    41  }
    42  
    43  type waitOption struct {
    44  	Token string
    45  }
    46  
    47  func WithToken(token string) waitOption {
    48  	return waitOption{Token: token}
    49  }
    50  
    51  // WaitForTestAgent ensures we have a node with serfHealth check registered
    52  func WaitForTestAgent(t *testing.T, rpc rpcFn, dc string, options ...waitOption) {
    53  	var nodes structs.IndexedNodes
    54  	var checks structs.IndexedHealthChecks
    55  
    56  	// first extra arg is an optional acl token
    57  	var token string
    58  	for _, opt := range options {
    59  		if opt.Token != "" {
    60  			token = opt.Token
    61  		}
    62  	}
    63  
    64  	retry.Run(t, func(r *retry.R) {
    65  		dcReq := &structs.DCSpecificRequest{
    66  			Datacenter:   dc,
    67  			QueryOptions: structs.QueryOptions{Token: token},
    68  		}
    69  		if err := rpc("Catalog.ListNodes", dcReq, &nodes); err != nil {
    70  			r.Fatalf("Catalog.ListNodes failed: %v", err)
    71  		}
    72  		if len(nodes.Nodes) == 0 {
    73  			r.Fatalf("No registered nodes")
    74  		}
    75  
    76  		// This assumes that there is a single agent per dc, typically a TestAgent
    77  		nodeReq := &structs.NodeSpecificRequest{
    78  			Datacenter:   dc,
    79  			Node:         nodes.Nodes[0].Node,
    80  			QueryOptions: structs.QueryOptions{Token: token},
    81  		}
    82  		if err := rpc("Health.NodeChecks", nodeReq, &checks); err != nil {
    83  			r.Fatalf("Health.NodeChecks failed: %v", err)
    84  		}
    85  
    86  		var found bool
    87  		for _, check := range checks.HealthChecks {
    88  			if check.CheckID == "serfHealth" {
    89  				found = true
    90  				break
    91  			}
    92  		}
    93  		if !found {
    94  			r.Fatalf("serfHealth check not found")
    95  		}
    96  	})
    97  }