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 }