github.com/sl1pm4t/consul@v1.4.5-0.20190325224627-74c31c540f9c/agent/consul/state/state_store_test.go (about)

     1  package state
     2  
     3  import (
     4  	crand "crypto/rand"
     5  	"fmt"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/hashicorp/consul/agent/structs"
    10  	"github.com/hashicorp/consul/types"
    11  	"github.com/hashicorp/go-memdb"
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  func testUUID() string {
    16  	buf := make([]byte, 16)
    17  	if _, err := crand.Read(buf); err != nil {
    18  		panic(fmt.Errorf("failed to read random bytes: %v", err))
    19  	}
    20  
    21  	return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x",
    22  		buf[0:4],
    23  		buf[4:6],
    24  		buf[6:8],
    25  		buf[8:10],
    26  		buf[10:16])
    27  }
    28  
    29  func testStateStore(t *testing.T) *Store {
    30  	s, err := NewStateStore(nil)
    31  	if err != nil {
    32  		t.Fatalf("err: %s", err)
    33  	}
    34  	if s == nil {
    35  		t.Fatalf("missing state store")
    36  	}
    37  	return s
    38  }
    39  
    40  func testRegisterNode(t *testing.T, s *Store, idx uint64, nodeID string) {
    41  	testRegisterNodeWithMeta(t, s, idx, nodeID, nil)
    42  }
    43  
    44  // testRegisterNodeWithChange registers a node and ensures it gets different from previous registration
    45  func testRegisterNodeWithChange(t *testing.T, s *Store, idx uint64, nodeID string) {
    46  	testRegisterNodeWithMeta(t, s, idx, nodeID, map[string]string{
    47  		"version": string(idx),
    48  	})
    49  }
    50  
    51  func testRegisterNodeWithMeta(t *testing.T, s *Store, idx uint64, nodeID string, meta map[string]string) {
    52  	node := &structs.Node{Node: nodeID, Meta: meta}
    53  	if err := s.EnsureNode(idx, node); err != nil {
    54  		t.Fatalf("err: %s", err)
    55  	}
    56  
    57  	tx := s.db.Txn(false)
    58  	defer tx.Abort()
    59  	n, err := tx.First("nodes", "id", nodeID)
    60  	if err != nil {
    61  		t.Fatalf("err: %s", err)
    62  	}
    63  	if result, ok := n.(*structs.Node); !ok || result.Node != nodeID {
    64  		t.Fatalf("bad node: %#v", result)
    65  	}
    66  }
    67  
    68  // testRegisterServiceWithChange registers a service and allow ensuring the consul index is updated
    69  // even if service already exists if using `modifyAccordingIndex`.
    70  // This is done by setting the transaction ID in "version" meta so service will be updated if it already exists
    71  func testRegisterServiceWithChange(t *testing.T, s *Store, idx uint64, nodeID, serviceID string, modifyAccordingIndex bool) {
    72  	meta := make(map[string]string)
    73  	if modifyAccordingIndex {
    74  		meta["version"] = string(idx)
    75  	}
    76  	svc := &structs.NodeService{
    77  		ID:      serviceID,
    78  		Service: serviceID,
    79  		Address: "1.1.1.1",
    80  		Port:    1111,
    81  		Meta:    meta,
    82  	}
    83  	if err := s.EnsureService(idx, nodeID, svc); err != nil {
    84  		t.Fatalf("err: %s", err)
    85  	}
    86  
    87  	tx := s.db.Txn(false)
    88  	defer tx.Abort()
    89  	service, err := tx.First("services", "id", nodeID, serviceID)
    90  	if err != nil {
    91  		t.Fatalf("err: %s", err)
    92  	}
    93  	if result, ok := service.(*structs.ServiceNode); !ok ||
    94  		result.Node != nodeID ||
    95  		result.ServiceID != serviceID {
    96  		t.Fatalf("bad service: %#v", result)
    97  	}
    98  }
    99  
   100  // testRegisterService register a service with given transaction idx
   101  // If the service already exists, transaction number might not be increased
   102  // Use `testRegisterServiceWithChange()` if you want perform a registration that
   103  // ensures the transaction is updated by setting idx in Meta of Service
   104  func testRegisterService(t *testing.T, s *Store, idx uint64, nodeID, serviceID string) {
   105  	testRegisterServiceWithChange(t, s, idx, nodeID, serviceID, false)
   106  }
   107  
   108  func testRegisterCheck(t *testing.T, s *Store, idx uint64,
   109  	nodeID string, serviceID string, checkID types.CheckID, state string) {
   110  	chk := &structs.HealthCheck{
   111  		Node:      nodeID,
   112  		CheckID:   checkID,
   113  		ServiceID: serviceID,
   114  		Status:    state,
   115  	}
   116  	if err := s.EnsureCheck(idx, chk); err != nil {
   117  		t.Fatalf("err: %s", err)
   118  	}
   119  
   120  	tx := s.db.Txn(false)
   121  	defer tx.Abort()
   122  	c, err := tx.First("checks", "id", nodeID, string(checkID))
   123  	if err != nil {
   124  		t.Fatalf("err: %s", err)
   125  	}
   126  	if result, ok := c.(*structs.HealthCheck); !ok ||
   127  		result.Node != nodeID ||
   128  		result.ServiceID != serviceID ||
   129  		result.CheckID != checkID {
   130  		t.Fatalf("bad check: %#v", result)
   131  	}
   132  }
   133  
   134  func testRegisterSidecarProxy(t *testing.T, s *Store, idx uint64, nodeID string, targetServiceID string) {
   135  	svc := &structs.NodeService{
   136  		ID:      targetServiceID + "-sidecar-proxy",
   137  		Service: targetServiceID + "-sidecar-proxy",
   138  		Port:    20000,
   139  		Kind:    structs.ServiceKindConnectProxy,
   140  		Proxy: structs.ConnectProxyConfig{
   141  			DestinationServiceName: targetServiceID,
   142  			DestinationServiceID:   targetServiceID,
   143  		},
   144  	}
   145  	require.NoError(t, s.EnsureService(idx, nodeID, svc))
   146  }
   147  
   148  func testRegisterConnectNativeService(t *testing.T, s *Store, idx uint64, nodeID string, serviceID string) {
   149  	svc := &structs.NodeService{
   150  		ID:      serviceID,
   151  		Service: serviceID,
   152  		Port:    1111,
   153  		Connect: structs.ServiceConnect{
   154  			Native: true,
   155  		},
   156  	}
   157  	require.NoError(t, s.EnsureService(idx, nodeID, svc))
   158  }
   159  
   160  func testSetKey(t *testing.T, s *Store, idx uint64, key, value string) {
   161  	entry := &structs.DirEntry{Key: key, Value: []byte(value)}
   162  	if err := s.KVSSet(idx, entry); err != nil {
   163  		t.Fatalf("err: %s", err)
   164  	}
   165  
   166  	tx := s.db.Txn(false)
   167  	defer tx.Abort()
   168  	e, err := tx.First("kvs", "id", key)
   169  	if err != nil {
   170  		t.Fatalf("err: %s", err)
   171  	}
   172  	if result, ok := e.(*structs.DirEntry); !ok || result.Key != key {
   173  		t.Fatalf("bad kvs entry: %#v", result)
   174  	}
   175  }
   176  
   177  // watchFired is a helper for unit tests that returns if the given watch set
   178  // fired (it doesn't care which watch actually fired). This uses a fixed
   179  // timeout since we already expect the event happened before calling this and
   180  // just need to distinguish a fire from a timeout. We do need a little time to
   181  // allow the watch to set up any goroutines, though.
   182  func watchFired(ws memdb.WatchSet) bool {
   183  	timedOut := ws.Watch(time.After(50 * time.Millisecond))
   184  	return !timedOut
   185  }
   186  
   187  func TestStateStore_Restore_Abort(t *testing.T) {
   188  	s := testStateStore(t)
   189  
   190  	// The detailed restore functions are tested below, this just checks
   191  	// that abort works.
   192  	restore := s.Restore()
   193  	entry := &structs.DirEntry{
   194  		Key:   "foo",
   195  		Value: []byte("bar"),
   196  		RaftIndex: structs.RaftIndex{
   197  			ModifyIndex: 5,
   198  		},
   199  	}
   200  	if err := restore.KVS(entry); err != nil {
   201  		t.Fatalf("err: %s", err)
   202  	}
   203  	restore.Abort()
   204  
   205  	idx, entries, err := s.KVSList(nil, "")
   206  	if err != nil {
   207  		t.Fatalf("err: %s", err)
   208  	}
   209  	if idx != 0 {
   210  		t.Fatalf("bad index: %d", idx)
   211  	}
   212  	if len(entries) != 0 {
   213  		t.Fatalf("bad: %#v", entries)
   214  	}
   215  }
   216  
   217  func TestStateStore_Abandon(t *testing.T) {
   218  	s := testStateStore(t)
   219  	abandonCh := s.AbandonCh()
   220  	s.Abandon()
   221  	select {
   222  	case <-abandonCh:
   223  	default:
   224  		t.Fatalf("bad")
   225  	}
   226  }
   227  
   228  func TestStateStore_maxIndex(t *testing.T) {
   229  	s := testStateStore(t)
   230  
   231  	testRegisterNode(t, s, 0, "foo")
   232  	testRegisterNode(t, s, 1, "bar")
   233  	testRegisterService(t, s, 2, "foo", "consul")
   234  
   235  	if max := s.maxIndex("nodes", "services"); max != 2 {
   236  		t.Fatalf("bad max: %d", max)
   237  	}
   238  }
   239  
   240  func TestStateStore_indexUpdateMaxTxn(t *testing.T) {
   241  	s := testStateStore(t)
   242  
   243  	testRegisterNode(t, s, 0, "foo")
   244  	testRegisterNode(t, s, 1, "bar")
   245  
   246  	tx := s.db.Txn(true)
   247  	if err := indexUpdateMaxTxn(tx, 3, "nodes"); err != nil {
   248  		t.Fatalf("err: %s", err)
   249  	}
   250  	tx.Commit()
   251  
   252  	if max := s.maxIndex("nodes"); max != 3 {
   253  		t.Fatalf("bad max: %d", max)
   254  	}
   255  }