github.imxd.top/hashicorp/consul@v1.4.5/api/txn_test.go (about)

     1  package api
     2  
     3  import (
     4  	"strings"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/hashicorp/go-uuid"
     9  
    10  	"github.com/pascaldekloe/goe/verify"
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  func TestAPI_ClientTxn(t *testing.T) {
    15  	t.Parallel()
    16  	require := require.New(t)
    17  	c, s := makeClient(t)
    18  	defer s.Stop()
    19  
    20  	session := c.Session()
    21  	txn := c.Txn()
    22  
    23  	// Set up a test service and health check.
    24  	nodeID, err := uuid.GenerateUUID()
    25  	require.NoError(err)
    26  
    27  	catalog := c.Catalog()
    28  	reg := &CatalogRegistration{
    29  		ID:      nodeID,
    30  		Node:    "foo",
    31  		Address: "2.2.2.2",
    32  		Service: &AgentService{
    33  			ID:      "foo1",
    34  			Service: "foo",
    35  		},
    36  		Checks: HealthChecks{
    37  			{
    38  				CheckID: "bar",
    39  				Status:  "critical",
    40  				Definition: HealthCheckDefinition{
    41  					TCP:                                    "1.1.1.1",
    42  					IntervalDuration:                       5 * time.Second,
    43  					TimeoutDuration:                        10 * time.Second,
    44  					DeregisterCriticalServiceAfterDuration: 20 * time.Second,
    45  				},
    46  			},
    47  			{
    48  				CheckID: "baz",
    49  				Status:  "passing",
    50  				Definition: HealthCheckDefinition{
    51  					TCP:                            "2.2.2.2",
    52  					Interval:                       ReadableDuration(40 * time.Second),
    53  					Timeout:                        ReadableDuration(80 * time.Second),
    54  					DeregisterCriticalServiceAfter: ReadableDuration(160 * time.Second),
    55  				},
    56  			},
    57  		},
    58  	}
    59  	_, err = catalog.Register(reg, nil)
    60  	require.NoError(err)
    61  
    62  	node, _, err := catalog.Node("foo", nil)
    63  	require.NoError(err)
    64  	require.Equal(nodeID, node.Node.ID)
    65  
    66  	// Make a session.
    67  	id, _, err := session.CreateNoChecks(nil, nil)
    68  	if err != nil {
    69  		t.Fatalf("err: %v", err)
    70  	}
    71  	defer session.Destroy(id, nil)
    72  
    73  	// Acquire and get the key via a transaction, but don't supply a valid
    74  	// session.
    75  	key := testKey()
    76  	value := []byte("test")
    77  	ops := TxnOps{
    78  		&TxnOp{
    79  			KV: &KVTxnOp{
    80  				Verb:  KVLock,
    81  				Key:   key,
    82  				Value: value,
    83  			},
    84  		},
    85  		&TxnOp{
    86  			KV: &KVTxnOp{
    87  				Verb: KVGet,
    88  				Key:  key,
    89  			},
    90  		},
    91  		&TxnOp{
    92  			Node: &NodeTxnOp{
    93  				Verb: NodeGet,
    94  				Node: Node{Node: "foo"},
    95  			},
    96  		},
    97  		&TxnOp{
    98  			Service: &ServiceTxnOp{
    99  				Verb:    ServiceGet,
   100  				Node:    "foo",
   101  				Service: AgentService{ID: "foo1"},
   102  			},
   103  		},
   104  		&TxnOp{
   105  			Check: &CheckTxnOp{
   106  				Verb:  CheckGet,
   107  				Check: HealthCheck{Node: "foo", CheckID: "bar"},
   108  			},
   109  		},
   110  		&TxnOp{
   111  			Check: &CheckTxnOp{
   112  				Verb:  CheckGet,
   113  				Check: HealthCheck{Node: "foo", CheckID: "baz"},
   114  			},
   115  		},
   116  	}
   117  	ok, ret, _, err := txn.Txn(ops, nil)
   118  	if err != nil {
   119  		t.Fatalf("err: %v", err)
   120  	} else if ok {
   121  		t.Fatalf("transaction should have failed")
   122  	}
   123  
   124  	if ret == nil || len(ret.Errors) != 2 || len(ret.Results) != 0 {
   125  		t.Fatalf("bad: %v", ret.Errors[2])
   126  	}
   127  	if ret.Errors[0].OpIndex != 0 ||
   128  		!strings.Contains(ret.Errors[0].What, "missing session") ||
   129  		!strings.Contains(ret.Errors[1].What, "doesn't exist") {
   130  		t.Fatalf("bad: %v", ret.Errors[0])
   131  	}
   132  
   133  	// Now poke in a real session and try again.
   134  	ops[0].KV.Session = id
   135  	ok, ret, _, err = txn.Txn(ops, nil)
   136  	if err != nil {
   137  		t.Fatalf("err: %v", err)
   138  	} else if !ok {
   139  		t.Fatalf("transaction failure")
   140  	}
   141  
   142  	if ret == nil || len(ret.Errors) != 0 || len(ret.Results) != 6 {
   143  		t.Fatalf("bad: %v", ret)
   144  	}
   145  	expected := TxnResults{
   146  		&TxnResult{
   147  			KV: &KVPair{
   148  				Key:         key,
   149  				Session:     id,
   150  				LockIndex:   1,
   151  				CreateIndex: ret.Results[0].KV.CreateIndex,
   152  				ModifyIndex: ret.Results[0].KV.ModifyIndex,
   153  			},
   154  		},
   155  		&TxnResult{
   156  			KV: &KVPair{
   157  				Key:         key,
   158  				Session:     id,
   159  				Value:       []byte("test"),
   160  				LockIndex:   1,
   161  				CreateIndex: ret.Results[1].KV.CreateIndex,
   162  				ModifyIndex: ret.Results[1].KV.ModifyIndex,
   163  			},
   164  		},
   165  		&TxnResult{
   166  			Node: &Node{
   167  				ID:          nodeID,
   168  				Node:        "foo",
   169  				Address:     "2.2.2.2",
   170  				Datacenter:  "dc1",
   171  				CreateIndex: ret.Results[2].Node.CreateIndex,
   172  				ModifyIndex: ret.Results[2].Node.CreateIndex,
   173  			},
   174  		},
   175  		&TxnResult{
   176  			Service: &CatalogService{
   177  				ID:          "foo1",
   178  				CreateIndex: ret.Results[3].Service.CreateIndex,
   179  				ModifyIndex: ret.Results[3].Service.CreateIndex,
   180  			},
   181  		},
   182  		&TxnResult{
   183  			Check: &HealthCheck{
   184  				Node:    "foo",
   185  				CheckID: "bar",
   186  				Status:  "critical",
   187  				Definition: HealthCheckDefinition{
   188  					TCP:                                    "1.1.1.1",
   189  					Interval:                               ReadableDuration(5 * time.Second),
   190  					IntervalDuration:                       5 * time.Second,
   191  					Timeout:                                ReadableDuration(10 * time.Second),
   192  					TimeoutDuration:                        10 * time.Second,
   193  					DeregisterCriticalServiceAfter:         ReadableDuration(20 * time.Second),
   194  					DeregisterCriticalServiceAfterDuration: 20 * time.Second,
   195  				},
   196  				CreateIndex: ret.Results[4].Check.CreateIndex,
   197  				ModifyIndex: ret.Results[4].Check.CreateIndex,
   198  			},
   199  		},
   200  		&TxnResult{
   201  			Check: &HealthCheck{
   202  				Node:    "foo",
   203  				CheckID: "baz",
   204  				Status:  "passing",
   205  				Definition: HealthCheckDefinition{
   206  					TCP:                                    "2.2.2.2",
   207  					Interval:                               ReadableDuration(40 * time.Second),
   208  					IntervalDuration:                       40 * time.Second,
   209  					Timeout:                                ReadableDuration(80 * time.Second),
   210  					TimeoutDuration:                        80 * time.Second,
   211  					DeregisterCriticalServiceAfter:         ReadableDuration(160 * time.Second),
   212  					DeregisterCriticalServiceAfterDuration: 160 * time.Second,
   213  				},
   214  				CreateIndex: ret.Results[4].Check.CreateIndex,
   215  				ModifyIndex: ret.Results[4].Check.CreateIndex,
   216  			},
   217  		},
   218  	}
   219  	verify.Values(t, "", ret.Results, expected)
   220  
   221  	// Run a read-only transaction.
   222  	ops = TxnOps{
   223  		&TxnOp{
   224  			KV: &KVTxnOp{
   225  				Verb: KVGet,
   226  				Key:  key,
   227  			},
   228  		},
   229  		&TxnOp{
   230  			Node: &NodeTxnOp{
   231  				Verb: NodeGet,
   232  				Node: Node{ID: s.Config.NodeID, Node: s.Config.NodeName},
   233  			},
   234  		},
   235  	}
   236  	ok, ret, _, err = txn.Txn(ops, nil)
   237  	if err != nil {
   238  		t.Fatalf("err: %v", err)
   239  	} else if !ok {
   240  		t.Fatalf("transaction failure")
   241  	}
   242  
   243  	expected = TxnResults{
   244  		&TxnResult{
   245  			KV: &KVPair{
   246  				Key:         key,
   247  				Session:     id,
   248  				Value:       []byte("test"),
   249  				LockIndex:   1,
   250  				CreateIndex: ret.Results[0].KV.CreateIndex,
   251  				ModifyIndex: ret.Results[0].KV.ModifyIndex,
   252  			},
   253  		},
   254  		&TxnResult{
   255  			Node: &Node{
   256  				ID:         s.Config.NodeID,
   257  				Node:       s.Config.NodeName,
   258  				Address:    "127.0.0.1",
   259  				Datacenter: "dc1",
   260  				TaggedAddresses: map[string]string{
   261  					"lan": s.Config.Bind,
   262  					"wan": s.Config.Bind,
   263  				},
   264  				Meta:        map[string]string{"consul-network-segment": ""},
   265  				CreateIndex: ret.Results[1].Node.CreateIndex,
   266  				ModifyIndex: ret.Results[1].Node.ModifyIndex,
   267  			},
   268  		},
   269  	}
   270  	verify.Values(t, "", ret.Results, expected)
   271  
   272  	// Sanity check using the regular GET API.
   273  	kv := c.KV()
   274  	pair, meta, err := kv.Get(key, nil)
   275  	if err != nil {
   276  		t.Fatalf("err: %v", err)
   277  	}
   278  	if pair == nil {
   279  		t.Fatalf("expected value: %#v", pair)
   280  	}
   281  	if pair.LockIndex != 1 {
   282  		t.Fatalf("Expected lock: %v", pair)
   283  	}
   284  	if pair.Session != id {
   285  		t.Fatalf("Expected lock: %v", pair)
   286  	}
   287  	if meta.LastIndex == 0 {
   288  		t.Fatalf("unexpected value: %#v", meta)
   289  	}
   290  }