github.com/clly/consul@v1.4.5/agent/consul/state/txn_test.go (about)

     1  package state
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/hashicorp/consul/agent/structs"
    10  	"github.com/hashicorp/consul/api"
    11  	"github.com/hashicorp/consul/types"
    12  	"github.com/pascaldekloe/goe/verify"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  func TestStateStore_Txn_Intention(t *testing.T) {
    17  	require := require.New(t)
    18  	s := testStateStore(t)
    19  
    20  	// Create some intentions.
    21  	ixn1 := &structs.Intention{
    22  		ID:              testUUID(),
    23  		SourceNS:        "default",
    24  		SourceName:      "web",
    25  		DestinationNS:   "default",
    26  		DestinationName: "db",
    27  		Meta:            map[string]string{},
    28  	}
    29  	ixn2 := &structs.Intention{
    30  		ID:              testUUID(),
    31  		SourceNS:        "default",
    32  		SourceName:      "db",
    33  		DestinationNS:   "default",
    34  		DestinationName: "*",
    35  		Action:          structs.IntentionActionDeny,
    36  		Meta:            map[string]string{},
    37  	}
    38  	ixn3 := &structs.Intention{
    39  		ID:              testUUID(),
    40  		SourceNS:        "default",
    41  		SourceName:      "foo",
    42  		DestinationNS:   "default",
    43  		DestinationName: "*",
    44  		Meta:            map[string]string{},
    45  	}
    46  
    47  	// Write the first two to the state store, leave the third
    48  	// to be created by the transaction operation.
    49  	require.NoError(s.IntentionSet(1, ixn1))
    50  	require.NoError(s.IntentionSet(2, ixn2))
    51  
    52  	// Set up a transaction that hits every operation.
    53  	ops := structs.TxnOps{
    54  		&structs.TxnOp{
    55  			Intention: &structs.TxnIntentionOp{
    56  				Op:        structs.IntentionOpUpdate,
    57  				Intention: ixn1,
    58  			},
    59  		},
    60  		&structs.TxnOp{
    61  			Intention: &structs.TxnIntentionOp{
    62  				Op:        structs.IntentionOpDelete,
    63  				Intention: ixn2,
    64  			},
    65  		},
    66  		&structs.TxnOp{
    67  			Intention: &structs.TxnIntentionOp{
    68  				Op:        structs.IntentionOpCreate,
    69  				Intention: ixn3,
    70  			},
    71  		},
    72  	}
    73  	results, errors := s.TxnRW(3, ops)
    74  	if len(errors) > 0 {
    75  		t.Fatalf("err: %v", errors)
    76  	}
    77  
    78  	// Make sure the response looks as expected.
    79  	expected := structs.TxnResults{}
    80  	verify.Values(t, "", results, expected)
    81  
    82  	// Pull the resulting state store contents.
    83  	idx, actual, err := s.Intentions(nil)
    84  	require.NoError(err)
    85  	if idx != 3 {
    86  		t.Fatalf("bad index: %d", idx)
    87  	}
    88  
    89  	// Make sure it looks as expected.
    90  	intentions := structs.Intentions{
    91  		&structs.Intention{
    92  			ID:              ixn1.ID,
    93  			SourceNS:        "default",
    94  			SourceName:      "web",
    95  			DestinationNS:   "default",
    96  			DestinationName: "db",
    97  			Meta:            map[string]string{},
    98  			Precedence:      9,
    99  			RaftIndex: structs.RaftIndex{
   100  				CreateIndex: 1,
   101  				ModifyIndex: 3,
   102  			},
   103  		},
   104  		&structs.Intention{
   105  			ID:              ixn3.ID,
   106  			SourceNS:        "default",
   107  			SourceName:      "foo",
   108  			DestinationNS:   "default",
   109  			DestinationName: "*",
   110  			Meta:            map[string]string{},
   111  			Precedence:      6,
   112  			RaftIndex: structs.RaftIndex{
   113  				CreateIndex: 3,
   114  				ModifyIndex: 3,
   115  			},
   116  		},
   117  	}
   118  	verify.Values(t, "", actual, intentions)
   119  }
   120  
   121  func TestStateStore_Txn_Node(t *testing.T) {
   122  	require := require.New(t)
   123  	s := testStateStore(t)
   124  
   125  	// Create some nodes.
   126  	var nodes [5]structs.Node
   127  	for i := 0; i < len(nodes); i++ {
   128  		nodes[i] = structs.Node{
   129  			Node: fmt.Sprintf("node%d", i+1),
   130  			ID:   types.NodeID(testUUID()),
   131  		}
   132  
   133  		// Leave node5 to be created by an operation.
   134  		if i < 5 {
   135  			s.EnsureNode(uint64(i+1), &nodes[i])
   136  		}
   137  	}
   138  
   139  	// Set up a transaction that hits every operation.
   140  	ops := structs.TxnOps{
   141  		&structs.TxnOp{
   142  			Node: &structs.TxnNodeOp{
   143  				Verb: api.NodeGet,
   144  				Node: nodes[0],
   145  			},
   146  		},
   147  		&structs.TxnOp{
   148  			Node: &structs.TxnNodeOp{
   149  				Verb: api.NodeSet,
   150  				Node: nodes[4],
   151  			},
   152  		},
   153  		&structs.TxnOp{
   154  			Node: &structs.TxnNodeOp{
   155  				Verb: api.NodeCAS,
   156  				Node: structs.Node{
   157  					Node:       "node2",
   158  					ID:         nodes[1].ID,
   159  					Datacenter: "dc2",
   160  					RaftIndex:  structs.RaftIndex{ModifyIndex: 2},
   161  				},
   162  			},
   163  		},
   164  		&structs.TxnOp{
   165  			Node: &structs.TxnNodeOp{
   166  				Verb: api.NodeDelete,
   167  				Node: structs.Node{Node: "node3"},
   168  			},
   169  		},
   170  		&structs.TxnOp{
   171  			Node: &structs.TxnNodeOp{
   172  				Verb: api.NodeDeleteCAS,
   173  				Node: structs.Node{
   174  					Node:      "node4",
   175  					RaftIndex: structs.RaftIndex{ModifyIndex: 4},
   176  				},
   177  			},
   178  		},
   179  	}
   180  	results, errors := s.TxnRW(8, ops)
   181  	if len(errors) > 0 {
   182  		t.Fatalf("err: %v", errors)
   183  	}
   184  
   185  	// Make sure the response looks as expected.
   186  	nodes[1].Datacenter = "dc2"
   187  	nodes[1].ModifyIndex = 8
   188  	expected := structs.TxnResults{
   189  		&structs.TxnResult{
   190  			Node: &nodes[0],
   191  		},
   192  		&structs.TxnResult{
   193  			Node: &nodes[4],
   194  		},
   195  		&structs.TxnResult{
   196  			Node: &nodes[1],
   197  		},
   198  	}
   199  	verify.Values(t, "", results, expected)
   200  
   201  	// Pull the resulting state store contents.
   202  	idx, actual, err := s.Nodes(nil)
   203  	require.NoError(err)
   204  	if idx != 8 {
   205  		t.Fatalf("bad index: %d", idx)
   206  	}
   207  
   208  	// Make sure it looks as expected.
   209  	expectedNodes := structs.Nodes{&nodes[0], &nodes[1], &nodes[4]}
   210  	verify.Values(t, "", actual, expectedNodes)
   211  }
   212  
   213  func TestStateStore_Txn_Service(t *testing.T) {
   214  	require := require.New(t)
   215  	s := testStateStore(t)
   216  
   217  	testRegisterNode(t, s, 1, "node1")
   218  
   219  	// Create some services.
   220  	for i := 1; i <= 4; i++ {
   221  		testRegisterService(t, s, uint64(i+1), "node1", fmt.Sprintf("svc%d", i))
   222  	}
   223  
   224  	// Set up a transaction that hits every operation.
   225  	ops := structs.TxnOps{
   226  		&structs.TxnOp{
   227  			Service: &structs.TxnServiceOp{
   228  				Verb:    api.ServiceGet,
   229  				Node:    "node1",
   230  				Service: structs.NodeService{ID: "svc1"},
   231  			},
   232  		},
   233  		&structs.TxnOp{
   234  			Service: &structs.TxnServiceOp{
   235  				Verb:    api.ServiceSet,
   236  				Node:    "node1",
   237  				Service: structs.NodeService{ID: "svc5"},
   238  			},
   239  		},
   240  		&structs.TxnOp{
   241  			Service: &structs.TxnServiceOp{
   242  				Verb: api.ServiceCAS,
   243  				Node: "node1",
   244  				Service: structs.NodeService{
   245  					ID:        "svc2",
   246  					Tags:      []string{"modified"},
   247  					RaftIndex: structs.RaftIndex{ModifyIndex: 3},
   248  				},
   249  			},
   250  		},
   251  		&structs.TxnOp{
   252  			Service: &structs.TxnServiceOp{
   253  				Verb:    api.ServiceDelete,
   254  				Node:    "node1",
   255  				Service: structs.NodeService{ID: "svc3"},
   256  			},
   257  		},
   258  		&structs.TxnOp{
   259  			Service: &structs.TxnServiceOp{
   260  				Verb: api.ServiceDeleteCAS,
   261  				Node: "node1",
   262  				Service: structs.NodeService{
   263  					ID:        "svc4",
   264  					RaftIndex: structs.RaftIndex{ModifyIndex: 5},
   265  				},
   266  			},
   267  		},
   268  	}
   269  	results, errors := s.TxnRW(6, ops)
   270  	if len(errors) > 0 {
   271  		t.Fatalf("err: %v", errors)
   272  	}
   273  
   274  	// Make sure the response looks as expected.
   275  	expected := structs.TxnResults{
   276  		&structs.TxnResult{
   277  			Service: &structs.NodeService{
   278  				ID:      "svc1",
   279  				Service: "svc1",
   280  				Address: "1.1.1.1",
   281  				Port:    1111,
   282  				Weights: &structs.Weights{Passing: 1, Warning: 1},
   283  				RaftIndex: structs.RaftIndex{
   284  					CreateIndex: 2,
   285  					ModifyIndex: 2,
   286  				},
   287  			},
   288  		},
   289  		&structs.TxnResult{
   290  			Service: &structs.NodeService{
   291  				ID:      "svc5",
   292  				Weights: &structs.Weights{Passing: 1, Warning: 1},
   293  				RaftIndex: structs.RaftIndex{
   294  					CreateIndex: 6,
   295  					ModifyIndex: 6,
   296  				},
   297  			},
   298  		},
   299  		&structs.TxnResult{
   300  			Service: &structs.NodeService{
   301  				ID:      "svc2",
   302  				Tags:    []string{"modified"},
   303  				Weights: &structs.Weights{Passing: 1, Warning: 1},
   304  				RaftIndex: structs.RaftIndex{
   305  					CreateIndex: 3,
   306  					ModifyIndex: 6,
   307  				},
   308  			},
   309  		},
   310  	}
   311  	verify.Values(t, "", results, expected)
   312  
   313  	// Pull the resulting state store contents.
   314  	idx, actual, err := s.NodeServices(nil, "node1")
   315  	require.NoError(err)
   316  	if idx != 6 {
   317  		t.Fatalf("bad index: %d", idx)
   318  	}
   319  
   320  	// Make sure it looks as expected.
   321  	expectedServices := &structs.NodeServices{
   322  		Node: &structs.Node{
   323  			Node: "node1",
   324  			RaftIndex: structs.RaftIndex{
   325  				CreateIndex: 1,
   326  				ModifyIndex: 1,
   327  			},
   328  		},
   329  		Services: map[string]*structs.NodeService{
   330  			"svc1": &structs.NodeService{
   331  				ID:      "svc1",
   332  				Service: "svc1",
   333  				Address: "1.1.1.1",
   334  				Port:    1111,
   335  				RaftIndex: structs.RaftIndex{
   336  					CreateIndex: 2,
   337  					ModifyIndex: 2,
   338  				},
   339  				Weights: &structs.Weights{Passing: 1, Warning: 1},
   340  			},
   341  			"svc5": &structs.NodeService{
   342  				ID: "svc5",
   343  				RaftIndex: structs.RaftIndex{
   344  					CreateIndex: 6,
   345  					ModifyIndex: 6,
   346  				},
   347  				Weights: &structs.Weights{Passing: 1, Warning: 1},
   348  			},
   349  			"svc2": &structs.NodeService{
   350  				ID:   "svc2",
   351  				Tags: []string{"modified"},
   352  				RaftIndex: structs.RaftIndex{
   353  					CreateIndex: 3,
   354  					ModifyIndex: 6,
   355  				},
   356  				Weights: &structs.Weights{Passing: 1, Warning: 1},
   357  			},
   358  		},
   359  	}
   360  	verify.Values(t, "", actual, expectedServices)
   361  }
   362  
   363  func TestStateStore_Txn_Checks(t *testing.T) {
   364  	require := require.New(t)
   365  	s := testStateStore(t)
   366  
   367  	testRegisterNode(t, s, 1, "node1")
   368  
   369  	// Create some checks.
   370  	for i := 1; i <= 4; i++ {
   371  		testRegisterCheck(t, s, uint64(i+1), "node1", "", types.CheckID(fmt.Sprintf("check%d", i)), "failing")
   372  	}
   373  
   374  	// Set up a transaction that hits every operation.
   375  	ops := structs.TxnOps{
   376  		&structs.TxnOp{
   377  			Check: &structs.TxnCheckOp{
   378  				Verb:  api.CheckGet,
   379  				Check: structs.HealthCheck{Node: "node1", CheckID: "check1"},
   380  			},
   381  		},
   382  		&structs.TxnOp{
   383  			Check: &structs.TxnCheckOp{
   384  				Verb:  api.CheckSet,
   385  				Check: structs.HealthCheck{Node: "node1", CheckID: "check5", Status: "passing"},
   386  			},
   387  		},
   388  		&structs.TxnOp{
   389  			Check: &structs.TxnCheckOp{
   390  				Verb: api.CheckCAS,
   391  				Check: structs.HealthCheck{
   392  					Node:      "node1",
   393  					CheckID:   "check2",
   394  					Status:    "warning",
   395  					RaftIndex: structs.RaftIndex{ModifyIndex: 3},
   396  				},
   397  			},
   398  		},
   399  		&structs.TxnOp{
   400  			Check: &structs.TxnCheckOp{
   401  				Verb:  api.CheckDelete,
   402  				Check: structs.HealthCheck{Node: "node1", CheckID: "check3"},
   403  			},
   404  		},
   405  		&structs.TxnOp{
   406  			Check: &structs.TxnCheckOp{
   407  				Verb: api.CheckDeleteCAS,
   408  				Check: structs.HealthCheck{
   409  					Node:      "node1",
   410  					CheckID:   "check4",
   411  					RaftIndex: structs.RaftIndex{ModifyIndex: 5},
   412  				},
   413  			},
   414  		},
   415  	}
   416  	results, errors := s.TxnRW(6, ops)
   417  	if len(errors) > 0 {
   418  		t.Fatalf("err: %v", errors)
   419  	}
   420  
   421  	// Make sure the response looks as expected.
   422  	expected := structs.TxnResults{
   423  		&structs.TxnResult{
   424  			Check: &structs.HealthCheck{
   425  				Node:    "node1",
   426  				CheckID: "check1",
   427  				Status:  "failing",
   428  				RaftIndex: structs.RaftIndex{
   429  					CreateIndex: 2,
   430  					ModifyIndex: 2,
   431  				},
   432  			},
   433  		},
   434  		&structs.TxnResult{
   435  			Check: &structs.HealthCheck{
   436  				Node:    "node1",
   437  				CheckID: "check5",
   438  				Status:  "passing",
   439  				RaftIndex: structs.RaftIndex{
   440  					CreateIndex: 6,
   441  					ModifyIndex: 6,
   442  				},
   443  			},
   444  		},
   445  		&structs.TxnResult{
   446  			Check: &structs.HealthCheck{
   447  				Node:    "node1",
   448  				CheckID: "check2",
   449  				Status:  "warning",
   450  				RaftIndex: structs.RaftIndex{
   451  					CreateIndex: 3,
   452  					ModifyIndex: 6,
   453  				},
   454  			},
   455  		},
   456  	}
   457  	verify.Values(t, "", results, expected)
   458  
   459  	// Pull the resulting state store contents.
   460  	idx, actual, err := s.NodeChecks(nil, "node1")
   461  	require.NoError(err)
   462  	if idx != 6 {
   463  		t.Fatalf("bad index: %d", idx)
   464  	}
   465  
   466  	// Make sure it looks as expected.
   467  	expectedChecks := structs.HealthChecks{
   468  		&structs.HealthCheck{
   469  			Node:    "node1",
   470  			CheckID: "check1",
   471  			Status:  "failing",
   472  			RaftIndex: structs.RaftIndex{
   473  				CreateIndex: 2,
   474  				ModifyIndex: 2,
   475  			},
   476  		},
   477  		&structs.HealthCheck{
   478  			Node:    "node1",
   479  			CheckID: "check2",
   480  			Status:  "warning",
   481  			RaftIndex: structs.RaftIndex{
   482  				CreateIndex: 3,
   483  				ModifyIndex: 6,
   484  			},
   485  		},
   486  		&structs.HealthCheck{
   487  			Node:    "node1",
   488  			CheckID: "check5",
   489  			Status:  "passing",
   490  			RaftIndex: structs.RaftIndex{
   491  				CreateIndex: 6,
   492  				ModifyIndex: 6,
   493  			},
   494  		},
   495  	}
   496  	verify.Values(t, "", actual, expectedChecks)
   497  }
   498  
   499  func TestStateStore_Txn_KVS(t *testing.T) {
   500  	s := testStateStore(t)
   501  
   502  	// Create KV entries in the state store.
   503  	testSetKey(t, s, 1, "foo/delete", "bar")
   504  	testSetKey(t, s, 2, "foo/bar/baz", "baz")
   505  	testSetKey(t, s, 3, "foo/bar/zip", "zip")
   506  	testSetKey(t, s, 4, "foo/zorp", "zorp")
   507  	testSetKey(t, s, 5, "foo/update", "stale")
   508  
   509  	// Make a real session.
   510  	testRegisterNode(t, s, 6, "node1")
   511  	session := testUUID()
   512  	if err := s.SessionCreate(7, &structs.Session{ID: session, Node: "node1"}); err != nil {
   513  		t.Fatalf("err: %s", err)
   514  	}
   515  
   516  	// Set up a transaction that hits every operation.
   517  	ops := structs.TxnOps{
   518  		&structs.TxnOp{
   519  			KV: &structs.TxnKVOp{
   520  				Verb: api.KVGetTree,
   521  				DirEnt: structs.DirEntry{
   522  					Key: "foo/bar",
   523  				},
   524  			},
   525  		},
   526  		&structs.TxnOp{
   527  			KV: &structs.TxnKVOp{
   528  				Verb: api.KVSet,
   529  				DirEnt: structs.DirEntry{
   530  					Key:   "foo/new",
   531  					Value: []byte("one"),
   532  				},
   533  			},
   534  		},
   535  		&structs.TxnOp{
   536  			KV: &structs.TxnKVOp{
   537  				Verb: api.KVDelete,
   538  				DirEnt: structs.DirEntry{
   539  					Key: "foo/zorp",
   540  				},
   541  			},
   542  		},
   543  		&structs.TxnOp{
   544  			KV: &structs.TxnKVOp{
   545  				Verb: api.KVDeleteCAS,
   546  				DirEnt: structs.DirEntry{
   547  					Key: "foo/delete",
   548  					RaftIndex: structs.RaftIndex{
   549  						ModifyIndex: 1,
   550  					},
   551  				},
   552  			},
   553  		},
   554  		&structs.TxnOp{
   555  			KV: &structs.TxnKVOp{
   556  				Verb: api.KVDeleteTree,
   557  				DirEnt: structs.DirEntry{
   558  					Key: "foo/bar",
   559  				},
   560  			},
   561  		},
   562  		&structs.TxnOp{
   563  			KV: &structs.TxnKVOp{
   564  				Verb: api.KVGet,
   565  				DirEnt: structs.DirEntry{
   566  					Key: "foo/update",
   567  				},
   568  			},
   569  		},
   570  		&structs.TxnOp{
   571  			KV: &structs.TxnKVOp{
   572  				Verb: api.KVCheckIndex,
   573  				DirEnt: structs.DirEntry{
   574  					Key: "foo/update",
   575  					RaftIndex: structs.RaftIndex{
   576  						ModifyIndex: 5,
   577  					},
   578  				},
   579  			},
   580  		},
   581  		&structs.TxnOp{
   582  			KV: &structs.TxnKVOp{
   583  				Verb: api.KVCAS,
   584  				DirEnt: structs.DirEntry{
   585  					Key:   "foo/update",
   586  					Value: []byte("new"),
   587  					RaftIndex: structs.RaftIndex{
   588  						ModifyIndex: 5,
   589  					},
   590  				},
   591  			},
   592  		},
   593  		&structs.TxnOp{
   594  			KV: &structs.TxnKVOp{
   595  				Verb: api.KVGet,
   596  				DirEnt: structs.DirEntry{
   597  					Key: "foo/update",
   598  				},
   599  			},
   600  		},
   601  		&structs.TxnOp{
   602  			KV: &structs.TxnKVOp{
   603  				Verb: api.KVCheckIndex,
   604  				DirEnt: structs.DirEntry{
   605  					Key: "foo/update",
   606  					RaftIndex: structs.RaftIndex{
   607  						ModifyIndex: 8,
   608  					},
   609  				},
   610  			},
   611  		},
   612  		&structs.TxnOp{
   613  			KV: &structs.TxnKVOp{
   614  				Verb: api.KVLock,
   615  				DirEnt: structs.DirEntry{
   616  					Key:     "foo/lock",
   617  					Session: session,
   618  				},
   619  			},
   620  		},
   621  		&structs.TxnOp{
   622  			KV: &structs.TxnKVOp{
   623  				Verb: api.KVCheckSession,
   624  				DirEnt: structs.DirEntry{
   625  					Key:     "foo/lock",
   626  					Session: session,
   627  				},
   628  			},
   629  		},
   630  		&structs.TxnOp{
   631  			KV: &structs.TxnKVOp{
   632  				Verb: api.KVUnlock,
   633  				DirEnt: structs.DirEntry{
   634  					Key:     "foo/lock",
   635  					Session: session,
   636  				},
   637  			},
   638  		},
   639  		&structs.TxnOp{
   640  			KV: &structs.TxnKVOp{
   641  				Verb: api.KVCheckSession,
   642  				DirEnt: structs.DirEntry{
   643  					Key:     "foo/lock",
   644  					Session: "",
   645  				},
   646  			},
   647  		},
   648  	}
   649  	results, errors := s.TxnRW(8, ops)
   650  	if len(errors) > 0 {
   651  		t.Fatalf("err: %v", errors)
   652  	}
   653  
   654  	// Make sure the response looks as expected.
   655  	expected := structs.TxnResults{
   656  		&structs.TxnResult{
   657  			KV: &structs.DirEntry{
   658  				Key:   "foo/bar/baz",
   659  				Value: []byte("baz"),
   660  				RaftIndex: structs.RaftIndex{
   661  					CreateIndex: 2,
   662  					ModifyIndex: 2,
   663  				},
   664  			},
   665  		},
   666  		&structs.TxnResult{
   667  			KV: &structs.DirEntry{
   668  				Key:   "foo/bar/zip",
   669  				Value: []byte("zip"),
   670  				RaftIndex: structs.RaftIndex{
   671  					CreateIndex: 3,
   672  					ModifyIndex: 3,
   673  				},
   674  			},
   675  		},
   676  		&structs.TxnResult{
   677  			KV: &structs.DirEntry{
   678  				Key: "foo/new",
   679  				RaftIndex: structs.RaftIndex{
   680  					CreateIndex: 8,
   681  					ModifyIndex: 8,
   682  				},
   683  			},
   684  		},
   685  		&structs.TxnResult{
   686  			KV: &structs.DirEntry{
   687  				Key:   "foo/update",
   688  				Value: []byte("stale"),
   689  				RaftIndex: structs.RaftIndex{
   690  					CreateIndex: 5,
   691  					ModifyIndex: 5,
   692  				},
   693  			},
   694  		},
   695  		&structs.TxnResult{
   696  			KV: &structs.DirEntry{
   697  
   698  				Key: "foo/update",
   699  				RaftIndex: structs.RaftIndex{
   700  					CreateIndex: 5,
   701  					ModifyIndex: 5,
   702  				},
   703  			},
   704  		},
   705  		&structs.TxnResult{
   706  			KV: &structs.DirEntry{
   707  				Key: "foo/update",
   708  				RaftIndex: structs.RaftIndex{
   709  					CreateIndex: 5,
   710  					ModifyIndex: 8,
   711  				},
   712  			},
   713  		},
   714  		&structs.TxnResult{
   715  			KV: &structs.DirEntry{
   716  				Key:   "foo/update",
   717  				Value: []byte("new"),
   718  				RaftIndex: structs.RaftIndex{
   719  					CreateIndex: 5,
   720  					ModifyIndex: 8,
   721  				},
   722  			},
   723  		},
   724  		&structs.TxnResult{
   725  			KV: &structs.DirEntry{
   726  				Key: "foo/update",
   727  				RaftIndex: structs.RaftIndex{
   728  					CreateIndex: 5,
   729  					ModifyIndex: 8,
   730  				},
   731  			},
   732  		},
   733  		&structs.TxnResult{
   734  			KV: &structs.DirEntry{
   735  				Key:       "foo/lock",
   736  				Session:   session,
   737  				LockIndex: 1,
   738  				RaftIndex: structs.RaftIndex{
   739  					CreateIndex: 8,
   740  					ModifyIndex: 8,
   741  				},
   742  			},
   743  		},
   744  		&structs.TxnResult{
   745  			KV: &structs.DirEntry{
   746  				Key:       "foo/lock",
   747  				Session:   session,
   748  				LockIndex: 1,
   749  				RaftIndex: structs.RaftIndex{
   750  					CreateIndex: 8,
   751  					ModifyIndex: 8,
   752  				},
   753  			},
   754  		},
   755  		&structs.TxnResult{
   756  			KV: &structs.DirEntry{
   757  				Key:       "foo/lock",
   758  				LockIndex: 1,
   759  				RaftIndex: structs.RaftIndex{
   760  					CreateIndex: 8,
   761  					ModifyIndex: 8,
   762  				},
   763  			},
   764  		},
   765  		&structs.TxnResult{
   766  			KV: &structs.DirEntry{
   767  				Key:       "foo/lock",
   768  				LockIndex: 1,
   769  				RaftIndex: structs.RaftIndex{
   770  					CreateIndex: 8,
   771  					ModifyIndex: 8,
   772  				},
   773  			},
   774  		},
   775  	}
   776  	if len(results) != len(expected) {
   777  		t.Fatalf("bad: %v", results)
   778  	}
   779  	for i := range results {
   780  		if !reflect.DeepEqual(results[i], expected[i]) {
   781  			t.Fatalf("bad %d", i)
   782  		}
   783  	}
   784  
   785  	// Pull the resulting state store contents.
   786  	idx, actual, err := s.KVSList(nil, "")
   787  	if err != nil {
   788  		t.Fatalf("err: %s", err)
   789  	}
   790  	if idx != 8 {
   791  		t.Fatalf("bad index: %d", idx)
   792  	}
   793  
   794  	// Make sure it looks as expected.
   795  	entries := structs.DirEntries{
   796  		&structs.DirEntry{
   797  			Key:       "foo/lock",
   798  			LockIndex: 1,
   799  			RaftIndex: structs.RaftIndex{
   800  				CreateIndex: 8,
   801  				ModifyIndex: 8,
   802  			},
   803  		},
   804  		&structs.DirEntry{
   805  			Key:   "foo/new",
   806  			Value: []byte("one"),
   807  			RaftIndex: structs.RaftIndex{
   808  				CreateIndex: 8,
   809  				ModifyIndex: 8,
   810  			},
   811  		},
   812  		&structs.DirEntry{
   813  			Key:   "foo/update",
   814  			Value: []byte("new"),
   815  			RaftIndex: structs.RaftIndex{
   816  				CreateIndex: 5,
   817  				ModifyIndex: 8,
   818  			},
   819  		},
   820  	}
   821  	if len(actual) != len(entries) {
   822  		t.Fatalf("bad len: %d != %d", len(actual), len(entries))
   823  	}
   824  	for i := range actual {
   825  		if !reflect.DeepEqual(actual[i], entries[i]) {
   826  			t.Fatalf("bad %d", i)
   827  		}
   828  	}
   829  }
   830  
   831  func TestStateStore_Txn_KVS_Rollback(t *testing.T) {
   832  	s := testStateStore(t)
   833  
   834  	// Create KV entries in the state store.
   835  	testSetKey(t, s, 1, "foo/delete", "bar")
   836  	testSetKey(t, s, 2, "foo/update", "stale")
   837  
   838  	testRegisterNode(t, s, 3, "node1")
   839  	session := testUUID()
   840  	if err := s.SessionCreate(4, &structs.Session{ID: session, Node: "node1"}); err != nil {
   841  		t.Fatalf("err: %s", err)
   842  	}
   843  	ok, err := s.KVSLock(5, &structs.DirEntry{Key: "foo/lock", Value: []byte("foo"), Session: session})
   844  	if !ok || err != nil {
   845  		t.Fatalf("didn't get the lock: %v %s", ok, err)
   846  	}
   847  
   848  	bogus := testUUID()
   849  	if err := s.SessionCreate(6, &structs.Session{ID: bogus, Node: "node1"}); err != nil {
   850  		t.Fatalf("err: %s", err)
   851  	}
   852  
   853  	// This function verifies that the state store wasn't changed.
   854  	verifyStateStore := func(desc string) {
   855  		idx, actual, err := s.KVSList(nil, "")
   856  		if err != nil {
   857  			t.Fatalf("err (%s): %s", desc, err)
   858  		}
   859  		if idx != 5 {
   860  			t.Fatalf("bad index (%s): %d", desc, idx)
   861  		}
   862  
   863  		// Make sure it looks as expected.
   864  		entries := structs.DirEntries{
   865  			&structs.DirEntry{
   866  				Key:   "foo/delete",
   867  				Value: []byte("bar"),
   868  				RaftIndex: structs.RaftIndex{
   869  					CreateIndex: 1,
   870  					ModifyIndex: 1,
   871  				},
   872  			},
   873  			&structs.DirEntry{
   874  				Key:       "foo/lock",
   875  				Value:     []byte("foo"),
   876  				LockIndex: 1,
   877  				Session:   session,
   878  				RaftIndex: structs.RaftIndex{
   879  					CreateIndex: 5,
   880  					ModifyIndex: 5,
   881  				},
   882  			},
   883  			&structs.DirEntry{
   884  				Key:   "foo/update",
   885  				Value: []byte("stale"),
   886  				RaftIndex: structs.RaftIndex{
   887  					CreateIndex: 2,
   888  					ModifyIndex: 2,
   889  				},
   890  			},
   891  		}
   892  		if len(actual) != len(entries) {
   893  			t.Fatalf("bad len (%s): %d != %d", desc, len(actual), len(entries))
   894  		}
   895  		for i := range actual {
   896  			if !reflect.DeepEqual(actual[i], entries[i]) {
   897  				t.Fatalf("bad (%s): op %d: %v != %v", desc, i, *(actual[i]), *(entries[i]))
   898  			}
   899  		}
   900  	}
   901  	verifyStateStore("initial")
   902  
   903  	// Set up a transaction that fails every operation.
   904  	ops := structs.TxnOps{
   905  		&structs.TxnOp{
   906  			KV: &structs.TxnKVOp{
   907  				Verb: api.KVCAS,
   908  				DirEnt: structs.DirEntry{
   909  					Key:   "foo/update",
   910  					Value: []byte("new"),
   911  					RaftIndex: structs.RaftIndex{
   912  						ModifyIndex: 1,
   913  					},
   914  				},
   915  			},
   916  		},
   917  		&structs.TxnOp{
   918  			KV: &structs.TxnKVOp{
   919  				Verb: api.KVLock,
   920  				DirEnt: structs.DirEntry{
   921  					Key:     "foo/lock",
   922  					Session: bogus,
   923  				},
   924  			},
   925  		},
   926  		&structs.TxnOp{
   927  			KV: &structs.TxnKVOp{
   928  				Verb: api.KVUnlock,
   929  				DirEnt: structs.DirEntry{
   930  					Key:     "foo/lock",
   931  					Session: bogus,
   932  				},
   933  			},
   934  		},
   935  		&structs.TxnOp{
   936  			KV: &structs.TxnKVOp{
   937  				Verb: api.KVCheckSession,
   938  				DirEnt: structs.DirEntry{
   939  					Key:     "foo/lock",
   940  					Session: bogus,
   941  				},
   942  			},
   943  		},
   944  		&structs.TxnOp{
   945  			KV: &structs.TxnKVOp{
   946  				Verb: api.KVGet,
   947  				DirEnt: structs.DirEntry{
   948  					Key: "nope",
   949  				},
   950  			},
   951  		},
   952  		&structs.TxnOp{
   953  			KV: &structs.TxnKVOp{
   954  				Verb: api.KVCheckSession,
   955  				DirEnt: structs.DirEntry{
   956  					Key:     "nope",
   957  					Session: bogus,
   958  				},
   959  			},
   960  		},
   961  		&structs.TxnOp{
   962  			KV: &structs.TxnKVOp{
   963  				Verb: api.KVCheckIndex,
   964  				DirEnt: structs.DirEntry{
   965  					Key: "foo/lock",
   966  					RaftIndex: structs.RaftIndex{
   967  						ModifyIndex: 6,
   968  					},
   969  				},
   970  			},
   971  		},
   972  		&structs.TxnOp{
   973  			KV: &structs.TxnKVOp{
   974  				Verb: api.KVCheckIndex,
   975  				DirEnt: structs.DirEntry{
   976  					Key: "nope",
   977  					RaftIndex: structs.RaftIndex{
   978  						ModifyIndex: 6,
   979  					},
   980  				},
   981  			},
   982  		},
   983  		&structs.TxnOp{
   984  			KV: &structs.TxnKVOp{
   985  				Verb: "nope",
   986  				DirEnt: structs.DirEntry{
   987  					Key: "foo/delete",
   988  				},
   989  			},
   990  		},
   991  	}
   992  	results, errors := s.TxnRW(7, ops)
   993  	if len(errors) != len(ops) {
   994  		t.Fatalf("bad len: %d != %d", len(errors), len(ops))
   995  	}
   996  	if len(results) != 0 {
   997  		t.Fatalf("bad len: %d != 0", len(results))
   998  	}
   999  	verifyStateStore("after")
  1000  
  1001  	// Make sure the errors look reasonable.
  1002  	expected := []string{
  1003  		"index is stale",
  1004  		"lock is already held",
  1005  		"lock isn't held, or is held by another session",
  1006  		"current session",
  1007  		`key "nope" doesn't exist`,
  1008  		`key "nope" doesn't exist`,
  1009  		"current modify index",
  1010  		`key "nope" doesn't exist`,
  1011  		"unknown KV verb",
  1012  	}
  1013  	if len(errors) != len(expected) {
  1014  		t.Fatalf("bad len: %d != %d", len(errors), len(expected))
  1015  	}
  1016  	for i, msg := range expected {
  1017  		if errors[i].OpIndex != i {
  1018  			t.Fatalf("bad index: %d != %d", i, errors[i].OpIndex)
  1019  		}
  1020  		if !strings.Contains(errors[i].Error(), msg) {
  1021  			t.Fatalf("bad %d: %v", i, errors[i].Error())
  1022  		}
  1023  	}
  1024  }
  1025  
  1026  func TestStateStore_Txn_KVS_RO(t *testing.T) {
  1027  	s := testStateStore(t)
  1028  
  1029  	// Create KV entries in the state store.
  1030  	testSetKey(t, s, 1, "foo", "bar")
  1031  	testSetKey(t, s, 2, "foo/bar/baz", "baz")
  1032  	testSetKey(t, s, 3, "foo/bar/zip", "zip")
  1033  
  1034  	// Set up a transaction that hits all the read-only operations.
  1035  	ops := structs.TxnOps{
  1036  		&structs.TxnOp{
  1037  			KV: &structs.TxnKVOp{
  1038  				Verb: api.KVGetTree,
  1039  				DirEnt: structs.DirEntry{
  1040  					Key: "foo/bar",
  1041  				},
  1042  			},
  1043  		},
  1044  		&structs.TxnOp{
  1045  			KV: &structs.TxnKVOp{
  1046  				Verb: api.KVGet,
  1047  				DirEnt: structs.DirEntry{
  1048  					Key: "foo",
  1049  				},
  1050  			},
  1051  		},
  1052  		&structs.TxnOp{
  1053  			KV: &structs.TxnKVOp{
  1054  				Verb: api.KVCheckSession,
  1055  				DirEnt: structs.DirEntry{
  1056  					Key:     "foo/bar/baz",
  1057  					Session: "",
  1058  				},
  1059  			},
  1060  		},
  1061  		&structs.TxnOp{
  1062  			KV: &structs.TxnKVOp{
  1063  				Verb: api.KVCheckSession,
  1064  				DirEnt: structs.DirEntry{
  1065  					Key: "foo/bar/zip",
  1066  					RaftIndex: structs.RaftIndex{
  1067  						ModifyIndex: 3,
  1068  					},
  1069  				},
  1070  			},
  1071  		},
  1072  	}
  1073  	results, errors := s.TxnRO(ops)
  1074  	if len(errors) > 0 {
  1075  		t.Fatalf("err: %v", errors)
  1076  	}
  1077  
  1078  	// Make sure the response looks as expected.
  1079  	expected := structs.TxnResults{
  1080  		&structs.TxnResult{
  1081  			KV: &structs.DirEntry{
  1082  				Key:   "foo/bar/baz",
  1083  				Value: []byte("baz"),
  1084  				RaftIndex: structs.RaftIndex{
  1085  					CreateIndex: 2,
  1086  					ModifyIndex: 2,
  1087  				},
  1088  			},
  1089  		},
  1090  		&structs.TxnResult{
  1091  			KV: &structs.DirEntry{
  1092  				Key:   "foo/bar/zip",
  1093  				Value: []byte("zip"),
  1094  				RaftIndex: structs.RaftIndex{
  1095  					CreateIndex: 3,
  1096  					ModifyIndex: 3,
  1097  				},
  1098  			},
  1099  		},
  1100  		&structs.TxnResult{
  1101  			KV: &structs.DirEntry{
  1102  				Key:   "foo",
  1103  				Value: []byte("bar"),
  1104  				RaftIndex: structs.RaftIndex{
  1105  					CreateIndex: 1,
  1106  					ModifyIndex: 1,
  1107  				},
  1108  			},
  1109  		},
  1110  		&structs.TxnResult{
  1111  			KV: &structs.DirEntry{
  1112  				Key: "foo/bar/baz",
  1113  				RaftIndex: structs.RaftIndex{
  1114  					CreateIndex: 2,
  1115  					ModifyIndex: 2,
  1116  				},
  1117  			},
  1118  		},
  1119  		&structs.TxnResult{
  1120  			KV: &structs.DirEntry{
  1121  				Key: "foo/bar/zip",
  1122  				RaftIndex: structs.RaftIndex{
  1123  					CreateIndex: 3,
  1124  					ModifyIndex: 3,
  1125  				},
  1126  			},
  1127  		},
  1128  	}
  1129  	if len(results) != len(expected) {
  1130  		t.Fatalf("bad: %v", results)
  1131  	}
  1132  	for i := range results {
  1133  		if !reflect.DeepEqual(results[i], expected[i]) {
  1134  			t.Fatalf("bad %d", i)
  1135  		}
  1136  	}
  1137  }
  1138  
  1139  func TestStateStore_Txn_KVS_RO_Safety(t *testing.T) {
  1140  	s := testStateStore(t)
  1141  
  1142  	// Create KV entries in the state store.
  1143  	testSetKey(t, s, 1, "foo", "bar")
  1144  	testSetKey(t, s, 2, "foo/bar/baz", "baz")
  1145  	testSetKey(t, s, 3, "foo/bar/zip", "zip")
  1146  
  1147  	// Set up a transaction that hits all the read-only operations.
  1148  	ops := structs.TxnOps{
  1149  		&structs.TxnOp{
  1150  			KV: &structs.TxnKVOp{
  1151  				Verb: api.KVSet,
  1152  				DirEnt: structs.DirEntry{
  1153  					Key:   "foo",
  1154  					Value: []byte("nope"),
  1155  				},
  1156  			},
  1157  		},
  1158  		&structs.TxnOp{
  1159  			KV: &structs.TxnKVOp{
  1160  				Verb: api.KVDelete,
  1161  				DirEnt: structs.DirEntry{
  1162  					Key: "foo/bar/baz",
  1163  				},
  1164  			},
  1165  		},
  1166  		&structs.TxnOp{
  1167  			KV: &structs.TxnKVOp{
  1168  				Verb: api.KVDeleteTree,
  1169  				DirEnt: structs.DirEntry{
  1170  					Key: "foo/bar",
  1171  				},
  1172  			},
  1173  		},
  1174  	}
  1175  	results, errors := s.TxnRO(ops)
  1176  	if len(results) > 0 {
  1177  		t.Fatalf("bad: %v", results)
  1178  	}
  1179  	if len(errors) != len(ops) {
  1180  		t.Fatalf("bad len: %d != %d", len(errors), len(ops))
  1181  	}
  1182  
  1183  	// Make sure the errors look reasonable (tombstone inserts cause the
  1184  	// insert errors during the delete operations).
  1185  	expected := []string{
  1186  		"cannot insert in read-only transaction",
  1187  		"cannot insert in read-only transaction",
  1188  		"failed recursive deleting kvs entry",
  1189  	}
  1190  	if len(errors) != len(expected) {
  1191  		t.Fatalf("bad len: %d != %d", len(errors), len(expected))
  1192  	}
  1193  	for i, msg := range expected {
  1194  		if errors[i].OpIndex != i {
  1195  			t.Fatalf("bad index: %d != %d", i, errors[i].OpIndex)
  1196  		}
  1197  		if !strings.Contains(errors[i].Error(), msg) {
  1198  			t.Fatalf("bad %d: %v", i, errors[i].Error())
  1199  		}
  1200  	}
  1201  }