github.imxd.top/hashicorp/consul@v1.4.5/agent/consul/state/txn.go (about) 1 package state 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/consul/agent/structs" 7 "github.com/hashicorp/consul/api" 8 "github.com/hashicorp/go-memdb" 9 ) 10 11 // txnKVS handles all KV-related operations. 12 func (s *Store) txnKVS(tx *memdb.Txn, idx uint64, op *structs.TxnKVOp) (structs.TxnResults, error) { 13 var entry *structs.DirEntry 14 var err error 15 16 switch op.Verb { 17 case api.KVSet: 18 entry = &op.DirEnt 19 err = s.kvsSetTxn(tx, idx, entry, false) 20 21 case api.KVDelete: 22 err = s.kvsDeleteTxn(tx, idx, op.DirEnt.Key) 23 24 case api.KVDeleteCAS: 25 var ok bool 26 ok, err = s.kvsDeleteCASTxn(tx, idx, op.DirEnt.ModifyIndex, op.DirEnt.Key) 27 if !ok && err == nil { 28 err = fmt.Errorf("failed to delete key %q, index is stale", op.DirEnt.Key) 29 } 30 31 case api.KVDeleteTree: 32 err = s.kvsDeleteTreeTxn(tx, idx, op.DirEnt.Key) 33 34 case api.KVCAS: 35 var ok bool 36 entry = &op.DirEnt 37 ok, err = s.kvsSetCASTxn(tx, idx, entry) 38 if !ok && err == nil { 39 err = fmt.Errorf("failed to set key %q, index is stale", op.DirEnt.Key) 40 } 41 42 case api.KVLock: 43 var ok bool 44 entry = &op.DirEnt 45 ok, err = s.kvsLockTxn(tx, idx, entry) 46 if !ok && err == nil { 47 err = fmt.Errorf("failed to lock key %q, lock is already held", op.DirEnt.Key) 48 } 49 50 case api.KVUnlock: 51 var ok bool 52 entry = &op.DirEnt 53 ok, err = s.kvsUnlockTxn(tx, idx, entry) 54 if !ok && err == nil { 55 err = fmt.Errorf("failed to unlock key %q, lock isn't held, or is held by another session", op.DirEnt.Key) 56 } 57 58 case api.KVGet: 59 _, entry, err = s.kvsGetTxn(tx, nil, op.DirEnt.Key) 60 if entry == nil && err == nil { 61 err = fmt.Errorf("key %q doesn't exist", op.DirEnt.Key) 62 } 63 64 case api.KVGetTree: 65 var entries structs.DirEntries 66 _, entries, err = s.kvsListTxn(tx, nil, op.DirEnt.Key) 67 if err == nil { 68 results := make(structs.TxnResults, 0, len(entries)) 69 for _, e := range entries { 70 result := structs.TxnResult{KV: e} 71 results = append(results, &result) 72 } 73 return results, nil 74 } 75 76 case api.KVCheckSession: 77 entry, err = s.kvsCheckSessionTxn(tx, op.DirEnt.Key, op.DirEnt.Session) 78 79 case api.KVCheckIndex: 80 entry, err = s.kvsCheckIndexTxn(tx, op.DirEnt.Key, op.DirEnt.ModifyIndex) 81 82 case api.KVCheckNotExists: 83 _, entry, err = s.kvsGetTxn(tx, nil, op.DirEnt.Key) 84 if entry != nil && err == nil { 85 err = fmt.Errorf("key %q exists", op.DirEnt.Key) 86 } 87 88 default: 89 err = fmt.Errorf("unknown KV verb %q", op.Verb) 90 } 91 if err != nil { 92 return nil, err 93 } 94 95 // For a GET we keep the value, otherwise we clone and blank out the 96 // value (we have to clone so we don't modify the entry being used by 97 // the state store). 98 if entry != nil { 99 if op.Verb == api.KVGet { 100 result := structs.TxnResult{KV: entry} 101 return structs.TxnResults{&result}, nil 102 } 103 104 clone := entry.Clone() 105 clone.Value = nil 106 result := structs.TxnResult{KV: clone} 107 return structs.TxnResults{&result}, nil 108 } 109 110 return nil, nil 111 } 112 113 // txnIntention handles all Intention-related operations. 114 func (s *Store) txnIntention(tx *memdb.Txn, idx uint64, op *structs.TxnIntentionOp) error { 115 switch op.Op { 116 case structs.IntentionOpCreate, structs.IntentionOpUpdate: 117 return s.intentionSetTxn(tx, idx, op.Intention) 118 case structs.IntentionOpDelete: 119 return s.intentionDeleteTxn(tx, idx, op.Intention.ID) 120 default: 121 return fmt.Errorf("unknown Intention op %q", op.Op) 122 } 123 } 124 125 // txnNode handles all Node-related operations. 126 func (s *Store) txnNode(tx *memdb.Txn, idx uint64, op *structs.TxnNodeOp) (structs.TxnResults, error) { 127 var entry *structs.Node 128 var err error 129 130 getNode := func() (*structs.Node, error) { 131 if op.Node.ID != "" { 132 return getNodeIDTxn(tx, op.Node.ID) 133 } else { 134 return getNodeTxn(tx, op.Node.Node) 135 } 136 } 137 138 switch op.Verb { 139 case api.NodeGet: 140 entry, err = getNode() 141 if entry == nil && err == nil { 142 err = fmt.Errorf("node %q doesn't exist", op.Node.Node) 143 } 144 145 case api.NodeSet: 146 err = s.ensureNodeTxn(tx, idx, &op.Node) 147 if err == nil { 148 entry, err = getNode() 149 } 150 151 case api.NodeCAS: 152 var ok bool 153 ok, err = s.ensureNodeCASTxn(tx, idx, &op.Node) 154 if !ok && err == nil { 155 err = fmt.Errorf("failed to set node %q, index is stale", op.Node.Node) 156 break 157 } 158 entry, err = getNode() 159 160 case api.NodeDelete: 161 err = s.deleteNodeTxn(tx, idx, op.Node.Node) 162 163 case api.NodeDeleteCAS: 164 var ok bool 165 ok, err = s.deleteNodeCASTxn(tx, idx, op.Node.ModifyIndex, op.Node.Node) 166 if !ok && err == nil { 167 err = fmt.Errorf("failed to delete node %q, index is stale", op.Node.Node) 168 } 169 170 default: 171 err = fmt.Errorf("unknown Node verb %q", op.Verb) 172 } 173 if err != nil { 174 return nil, err 175 } 176 177 // For a GET we keep the value, otherwise we clone and blank out the 178 // value (we have to clone so we don't modify the entry being used by 179 // the state store). 180 if entry != nil { 181 if op.Verb == api.NodeGet { 182 result := structs.TxnResult{Node: entry} 183 return structs.TxnResults{&result}, nil 184 } 185 186 clone := *entry 187 result := structs.TxnResult{Node: &clone} 188 return structs.TxnResults{&result}, nil 189 } 190 191 return nil, nil 192 } 193 194 // txnService handles all Service-related operations. 195 func (s *Store) txnService(tx *memdb.Txn, idx uint64, op *structs.TxnServiceOp) (structs.TxnResults, error) { 196 var entry *structs.NodeService 197 var err error 198 199 switch op.Verb { 200 case api.ServiceGet: 201 entry, err = s.getNodeServiceTxn(tx, op.Node, op.Service.ID) 202 if entry == nil && err == nil { 203 err = fmt.Errorf("service %q on node %q doesn't exist", op.Service.ID, op.Node) 204 } 205 206 case api.ServiceSet: 207 err = s.ensureServiceTxn(tx, idx, op.Node, &op.Service) 208 entry, err = s.getNodeServiceTxn(tx, op.Node, op.Service.ID) 209 210 case api.ServiceCAS: 211 var ok bool 212 ok, err = s.ensureServiceCASTxn(tx, idx, op.Node, &op.Service) 213 if !ok && err == nil { 214 err = fmt.Errorf("failed to set service %q on node %q, index is stale", op.Service.ID, op.Node) 215 break 216 } 217 entry, err = s.getNodeServiceTxn(tx, op.Node, op.Service.ID) 218 219 case api.ServiceDelete: 220 err = s.deleteServiceTxn(tx, idx, op.Node, op.Service.ID) 221 222 case api.ServiceDeleteCAS: 223 var ok bool 224 ok, err = s.deleteServiceCASTxn(tx, idx, op.Service.ModifyIndex, op.Node, op.Service.ID) 225 if !ok && err == nil { 226 err = fmt.Errorf("failed to delete service %q on node %q, index is stale", op.Service.ID, op.Node) 227 } 228 229 default: 230 err = fmt.Errorf("unknown Service verb %q", op.Verb) 231 } 232 if err != nil { 233 return nil, err 234 } 235 236 // For a GET we keep the value, otherwise we clone and blank out the 237 // value (we have to clone so we don't modify the entry being used by 238 // the state store). 239 if entry != nil { 240 if op.Verb == api.ServiceGet { 241 result := structs.TxnResult{Service: entry} 242 return structs.TxnResults{&result}, nil 243 } 244 245 clone := *entry 246 result := structs.TxnResult{Service: &clone} 247 return structs.TxnResults{&result}, nil 248 } 249 250 return nil, nil 251 } 252 253 // txnCheck handles all Check-related operations. 254 func (s *Store) txnCheck(tx *memdb.Txn, idx uint64, op *structs.TxnCheckOp) (structs.TxnResults, error) { 255 var entry *structs.HealthCheck 256 var err error 257 258 switch op.Verb { 259 case api.CheckGet: 260 _, entry, err = s.getNodeCheckTxn(tx, op.Check.Node, op.Check.CheckID) 261 if entry == nil && err == nil { 262 err = fmt.Errorf("check %q on node %q doesn't exist", op.Check.CheckID, op.Check.Node) 263 } 264 265 case api.CheckSet: 266 err = s.ensureCheckTxn(tx, idx, &op.Check) 267 if err == nil { 268 _, entry, err = s.getNodeCheckTxn(tx, op.Check.Node, op.Check.CheckID) 269 } 270 271 case api.CheckCAS: 272 var ok bool 273 entry = &op.Check 274 ok, err = s.ensureCheckCASTxn(tx, idx, entry) 275 if !ok && err == nil { 276 err = fmt.Errorf("failed to set check %q on node %q, index is stale", entry.CheckID, entry.Node) 277 break 278 } 279 _, entry, err = s.getNodeCheckTxn(tx, op.Check.Node, op.Check.CheckID) 280 281 case api.CheckDelete: 282 err = s.deleteCheckTxn(tx, idx, op.Check.Node, op.Check.CheckID) 283 284 case api.CheckDeleteCAS: 285 var ok bool 286 ok, err = s.deleteCheckCASTxn(tx, idx, op.Check.ModifyIndex, op.Check.Node, op.Check.CheckID) 287 if !ok && err == nil { 288 err = fmt.Errorf("failed to delete check %q on node %q, index is stale", op.Check.CheckID, op.Check.Node) 289 } 290 291 default: 292 err = fmt.Errorf("unknown Check verb %q", op.Verb) 293 } 294 if err != nil { 295 return nil, err 296 } 297 298 // For a GET we keep the value, otherwise we clone and blank out the 299 // value (we have to clone so we don't modify the entry being used by 300 // the state store). 301 if entry != nil { 302 if op.Verb == api.CheckGet { 303 result := structs.TxnResult{Check: entry} 304 return structs.TxnResults{&result}, nil 305 } 306 307 clone := entry.Clone() 308 result := structs.TxnResult{Check: clone} 309 return structs.TxnResults{&result}, nil 310 } 311 312 return nil, nil 313 } 314 315 // txnDispatch runs the given operations inside the state store transaction. 316 func (s *Store) txnDispatch(tx *memdb.Txn, idx uint64, ops structs.TxnOps) (structs.TxnResults, structs.TxnErrors) { 317 results := make(structs.TxnResults, 0, len(ops)) 318 errors := make(structs.TxnErrors, 0, len(ops)) 319 for i, op := range ops { 320 var ret structs.TxnResults 321 var err error 322 323 // Dispatch based on the type of operation. 324 switch { 325 case op.KV != nil: 326 ret, err = s.txnKVS(tx, idx, op.KV) 327 case op.Intention != nil: 328 err = s.txnIntention(tx, idx, op.Intention) 329 case op.Node != nil: 330 ret, err = s.txnNode(tx, idx, op.Node) 331 case op.Service != nil: 332 ret, err = s.txnService(tx, idx, op.Service) 333 case op.Check != nil: 334 ret, err = s.txnCheck(tx, idx, op.Check) 335 default: 336 err = fmt.Errorf("no operation specified") 337 } 338 339 // Accumulate the results. 340 results = append(results, ret...) 341 342 // Capture any error along with the index of the operation that 343 // failed. 344 if err != nil { 345 errors = append(errors, &structs.TxnError{ 346 OpIndex: i, 347 What: err.Error(), 348 }) 349 } 350 } 351 352 if len(errors) > 0 { 353 return nil, errors 354 } 355 356 return results, nil 357 } 358 359 // TxnRW tries to run the given operations all inside a single transaction. If 360 // any of the operations fail, the entire transaction will be rolled back. This 361 // is done in a full write transaction on the state store, so reads and writes 362 // are possible 363 func (s *Store) TxnRW(idx uint64, ops structs.TxnOps) (structs.TxnResults, structs.TxnErrors) { 364 tx := s.db.Txn(true) 365 defer tx.Abort() 366 367 results, errors := s.txnDispatch(tx, idx, ops) 368 if len(errors) > 0 { 369 return nil, errors 370 } 371 372 tx.Commit() 373 return results, nil 374 } 375 376 // TxnRO runs the given operations inside a single read transaction in the state 377 // store. You must verify outside this function that no write operations are 378 // present, otherwise you'll get an error from the state store. 379 func (s *Store) TxnRO(ops structs.TxnOps) (structs.TxnResults, structs.TxnErrors) { 380 tx := s.db.Txn(false) 381 defer tx.Abort() 382 383 results, errors := s.txnDispatch(tx, 0, ops) 384 if len(errors) > 0 { 385 return nil, errors 386 } 387 388 return results, nil 389 }