github.imxd.top/hashicorp/consul@v1.4.5/agent/consul/state/catalog.go (about) 1 package state 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/hashicorp/consul/agent/structs" 8 "github.com/hashicorp/consul/api" 9 "github.com/hashicorp/consul/types" 10 "github.com/hashicorp/go-memdb" 11 uuid "github.com/hashicorp/go-uuid" 12 ) 13 14 const ( 15 servicesTableName = "services" 16 17 // serviceLastExtinctionIndexName keeps track of the last raft index when the last instance 18 // of any service was unregistered. This is used by blocking queries on missing services. 19 serviceLastExtinctionIndexName = "service_last_extinction" 20 ) 21 22 // nodesTableSchema returns a new table schema used for storing node 23 // information. 24 func nodesTableSchema() *memdb.TableSchema { 25 return &memdb.TableSchema{ 26 Name: "nodes", 27 Indexes: map[string]*memdb.IndexSchema{ 28 "id": &memdb.IndexSchema{ 29 Name: "id", 30 AllowMissing: false, 31 Unique: true, 32 Indexer: &memdb.StringFieldIndex{ 33 Field: "Node", 34 Lowercase: true, 35 }, 36 }, 37 "uuid": &memdb.IndexSchema{ 38 Name: "uuid", 39 AllowMissing: true, 40 Unique: true, 41 Indexer: &memdb.UUIDFieldIndex{ 42 Field: "ID", 43 }, 44 }, 45 "meta": &memdb.IndexSchema{ 46 Name: "meta", 47 AllowMissing: true, 48 Unique: false, 49 Indexer: &memdb.StringMapFieldIndex{ 50 Field: "Meta", 51 Lowercase: false, 52 }, 53 }, 54 }, 55 } 56 } 57 58 // servicesTableSchema returns a new table schema used to store information 59 // about services. 60 func servicesTableSchema() *memdb.TableSchema { 61 return &memdb.TableSchema{ 62 Name: "services", 63 Indexes: map[string]*memdb.IndexSchema{ 64 "id": &memdb.IndexSchema{ 65 Name: "id", 66 AllowMissing: false, 67 Unique: true, 68 Indexer: &memdb.CompoundIndex{ 69 Indexes: []memdb.Indexer{ 70 &memdb.StringFieldIndex{ 71 Field: "Node", 72 Lowercase: true, 73 }, 74 &memdb.StringFieldIndex{ 75 Field: "ServiceID", 76 Lowercase: true, 77 }, 78 }, 79 }, 80 }, 81 "node": &memdb.IndexSchema{ 82 Name: "node", 83 AllowMissing: false, 84 Unique: false, 85 Indexer: &memdb.StringFieldIndex{ 86 Field: "Node", 87 Lowercase: true, 88 }, 89 }, 90 "service": &memdb.IndexSchema{ 91 Name: "service", 92 AllowMissing: true, 93 Unique: false, 94 Indexer: &memdb.StringFieldIndex{ 95 Field: "ServiceName", 96 Lowercase: true, 97 }, 98 }, 99 "connect": &memdb.IndexSchema{ 100 Name: "connect", 101 AllowMissing: true, 102 Unique: false, 103 Indexer: &IndexConnectService{}, 104 }, 105 }, 106 } 107 } 108 109 // checksTableSchema returns a new table schema used for storing and indexing 110 // health check information. Health checks have a number of different attributes 111 // we want to filter by, so this table is a bit more complex. 112 func checksTableSchema() *memdb.TableSchema { 113 return &memdb.TableSchema{ 114 Name: "checks", 115 Indexes: map[string]*memdb.IndexSchema{ 116 "id": &memdb.IndexSchema{ 117 Name: "id", 118 AllowMissing: false, 119 Unique: true, 120 Indexer: &memdb.CompoundIndex{ 121 Indexes: []memdb.Indexer{ 122 &memdb.StringFieldIndex{ 123 Field: "Node", 124 Lowercase: true, 125 }, 126 &memdb.StringFieldIndex{ 127 Field: "CheckID", 128 Lowercase: true, 129 }, 130 }, 131 }, 132 }, 133 "status": &memdb.IndexSchema{ 134 Name: "status", 135 AllowMissing: false, 136 Unique: false, 137 Indexer: &memdb.StringFieldIndex{ 138 Field: "Status", 139 Lowercase: false, 140 }, 141 }, 142 "service": &memdb.IndexSchema{ 143 Name: "service", 144 AllowMissing: true, 145 Unique: false, 146 Indexer: &memdb.StringFieldIndex{ 147 Field: "ServiceName", 148 Lowercase: true, 149 }, 150 }, 151 "node": &memdb.IndexSchema{ 152 Name: "node", 153 AllowMissing: true, 154 Unique: false, 155 Indexer: &memdb.StringFieldIndex{ 156 Field: "Node", 157 Lowercase: true, 158 }, 159 }, 160 "node_service_check": &memdb.IndexSchema{ 161 Name: "node_service_check", 162 AllowMissing: true, 163 Unique: false, 164 Indexer: &memdb.CompoundIndex{ 165 Indexes: []memdb.Indexer{ 166 &memdb.StringFieldIndex{ 167 Field: "Node", 168 Lowercase: true, 169 }, 170 &memdb.FieldSetIndex{ 171 Field: "ServiceID", 172 }, 173 }, 174 }, 175 }, 176 "node_service": &memdb.IndexSchema{ 177 Name: "node_service", 178 AllowMissing: true, 179 Unique: false, 180 Indexer: &memdb.CompoundIndex{ 181 Indexes: []memdb.Indexer{ 182 &memdb.StringFieldIndex{ 183 Field: "Node", 184 Lowercase: true, 185 }, 186 &memdb.StringFieldIndex{ 187 Field: "ServiceID", 188 Lowercase: true, 189 }, 190 }, 191 }, 192 }, 193 }, 194 } 195 } 196 197 func init() { 198 registerSchema(nodesTableSchema) 199 registerSchema(servicesTableSchema) 200 registerSchema(checksTableSchema) 201 } 202 203 const ( 204 // minUUIDLookupLen is used as a minimum length of a node name required before 205 // we test to see if the name is actually a UUID and perform an ID-based node 206 // lookup. 207 minUUIDLookupLen = 2 208 ) 209 210 func resizeNodeLookupKey(s string) string { 211 l := len(s) 212 213 if l%2 != 0 { 214 return s[0 : l-1] 215 } 216 217 return s 218 } 219 220 // Nodes is used to pull the full list of nodes for use during snapshots. 221 func (s *Snapshot) Nodes() (memdb.ResultIterator, error) { 222 iter, err := s.tx.Get("nodes", "id") 223 if err != nil { 224 return nil, err 225 } 226 return iter, nil 227 } 228 229 // Services is used to pull the full list of services for a given node for use 230 // during snapshots. 231 func (s *Snapshot) Services(node string) (memdb.ResultIterator, error) { 232 iter, err := s.tx.Get("services", "node", node) 233 if err != nil { 234 return nil, err 235 } 236 return iter, nil 237 } 238 239 // Checks is used to pull the full list of checks for a given node for use 240 // during snapshots. 241 func (s *Snapshot) Checks(node string) (memdb.ResultIterator, error) { 242 iter, err := s.tx.Get("checks", "node", node) 243 if err != nil { 244 return nil, err 245 } 246 return iter, nil 247 } 248 249 // Registration is used to make sure a node, service, and check registration is 250 // performed within a single transaction to avoid race conditions on state 251 // updates. 252 func (s *Restore) Registration(idx uint64, req *structs.RegisterRequest) error { 253 if err := s.store.ensureRegistrationTxn(s.tx, idx, req); err != nil { 254 return err 255 } 256 return nil 257 } 258 259 // EnsureRegistration is used to make sure a node, service, and check 260 // registration is performed within a single transaction to avoid race 261 // conditions on state updates. 262 func (s *Store) EnsureRegistration(idx uint64, req *structs.RegisterRequest) error { 263 tx := s.db.Txn(true) 264 defer tx.Abort() 265 266 if err := s.ensureRegistrationTxn(tx, idx, req); err != nil { 267 return err 268 } 269 270 tx.Commit() 271 return nil 272 } 273 274 func (s *Store) ensureCheckIfNodeMatches(tx *memdb.Txn, idx uint64, node string, check *structs.HealthCheck) error { 275 if check.Node != node { 276 return fmt.Errorf("check node %q does not match node %q", 277 check.Node, node) 278 } 279 if err := s.ensureCheckTxn(tx, idx, check); err != nil { 280 return fmt.Errorf("failed inserting check: %s on node %q", err, check.Node) 281 } 282 return nil 283 } 284 285 // ensureRegistrationTxn is used to make sure a node, service, and check 286 // registration is performed within a single transaction to avoid race 287 // conditions on state updates. 288 func (s *Store) ensureRegistrationTxn(tx *memdb.Txn, idx uint64, req *structs.RegisterRequest) error { 289 // Create a node structure. 290 node := &structs.Node{ 291 ID: req.ID, 292 Node: req.Node, 293 Address: req.Address, 294 Datacenter: req.Datacenter, 295 TaggedAddresses: req.TaggedAddresses, 296 Meta: req.NodeMeta, 297 } 298 299 // Since this gets called for all node operations (service and check 300 // updates) and churn on the node itself is basically none after the 301 // node updates itself the first time, it's worth seeing if we need to 302 // modify the node at all so we prevent watch churn and useless writes 303 // and modify index bumps on the node. 304 { 305 existing, err := tx.First("nodes", "id", node.Node) 306 if err != nil { 307 return fmt.Errorf("node lookup failed: %s", err) 308 } 309 if existing == nil || req.ChangesNode(existing.(*structs.Node)) { 310 if err := s.ensureNodeTxn(tx, idx, node); err != nil { 311 return fmt.Errorf("failed inserting node: %s", err) 312 } 313 } 314 } 315 316 // Add the service, if any. We perform a similar check as we do for the 317 // node info above to make sure we actually need to update the service 318 // definition in order to prevent useless churn if nothing has changed. 319 if req.Service != nil { 320 existing, err := tx.First("services", "id", req.Node, req.Service.ID) 321 if err != nil { 322 return fmt.Errorf("failed service lookup: %s", err) 323 } 324 if existing == nil || !(existing.(*structs.ServiceNode).ToNodeService()).IsSame(req.Service) { 325 if err := s.ensureServiceTxn(tx, idx, req.Node, req.Service); err != nil { 326 return fmt.Errorf("failed inserting service: %s", err) 327 328 } 329 } 330 } 331 332 // Add the checks, if any. 333 if req.Check != nil { 334 if err := s.ensureCheckIfNodeMatches(tx, idx, req.Node, req.Check); err != nil { 335 return err 336 } 337 } 338 for _, check := range req.Checks { 339 if err := s.ensureCheckIfNodeMatches(tx, idx, req.Node, check); err != nil { 340 return err 341 } 342 } 343 344 return nil 345 } 346 347 // EnsureNode is used to upsert node registration or modification. 348 func (s *Store) EnsureNode(idx uint64, node *structs.Node) error { 349 tx := s.db.Txn(true) 350 defer tx.Abort() 351 352 // Call the node upsert 353 if err := s.ensureNodeTxn(tx, idx, node); err != nil { 354 return err 355 } 356 357 tx.Commit() 358 return nil 359 } 360 361 // ensureNoNodeWithSimilarNameTxn checks that no other node has conflict in its name 362 // If allowClashWithoutID then, getting a conflict on another node without ID will be allowed 363 func (s *Store) ensureNoNodeWithSimilarNameTxn(tx *memdb.Txn, node *structs.Node, allowClashWithoutID bool) error { 364 // Retrieve all of the nodes 365 enodes, err := tx.Get("nodes", "id") 366 if err != nil { 367 return fmt.Errorf("Cannot lookup all nodes: %s", err) 368 } 369 for nodeIt := enodes.Next(); nodeIt != nil; nodeIt = enodes.Next() { 370 enode := nodeIt.(*structs.Node) 371 if strings.EqualFold(node.Node, enode.Node) && node.ID != enode.ID { 372 if !(enode.ID == "" && allowClashWithoutID) { 373 return fmt.Errorf("Node name %s is reserved by node %s with name %s", node.Node, enode.ID, enode.Node) 374 } 375 } 376 } 377 return nil 378 } 379 380 // ensureNodeCASTxn updates a node only if the existing index matches the given index. 381 // Returns a bool indicating if a write happened and any error. 382 func (s *Store) ensureNodeCASTxn(tx *memdb.Txn, idx uint64, node *structs.Node) (bool, error) { 383 // Retrieve the existing entry. 384 existing, err := getNodeTxn(tx, node.Node) 385 if err != nil { 386 return false, err 387 } 388 389 // Check if the we should do the set. A ModifyIndex of 0 means that 390 // we are doing a set-if-not-exists. 391 if node.ModifyIndex == 0 && existing != nil { 392 return false, nil 393 } 394 if node.ModifyIndex != 0 && existing == nil { 395 return false, nil 396 } 397 if existing != nil && node.ModifyIndex != 0 && node.ModifyIndex != existing.ModifyIndex { 398 return false, nil 399 } 400 401 // Perform the update. 402 if err := s.ensureNodeTxn(tx, idx, node); err != nil { 403 return false, err 404 } 405 406 return true, nil 407 } 408 409 // ensureNodeTxn is the inner function called to actually create a node 410 // registration or modify an existing one in the state store. It allows 411 // passing in a memdb transaction so it may be part of a larger txn. 412 func (s *Store) ensureNodeTxn(tx *memdb.Txn, idx uint64, node *structs.Node) error { 413 // See if there's an existing node with this UUID, and make sure the 414 // name is the same. 415 var n *structs.Node 416 if node.ID != "" { 417 existing, err := getNodeIDTxn(tx, node.ID) 418 if err != nil { 419 return fmt.Errorf("node lookup failed: %s", err) 420 } 421 if existing != nil { 422 n = existing 423 if n.Node != node.Node { 424 // Lets first get all nodes and check whether name do match, we do not allow clash on nodes without ID 425 dupNameError := s.ensureNoNodeWithSimilarNameTxn(tx, node, false) 426 if dupNameError != nil { 427 return fmt.Errorf("Error while renaming Node ID: %q: %s", node.ID, dupNameError) 428 } 429 // We are actually renaming a node, remove its reference first 430 err := s.deleteNodeTxn(tx, idx, n.Node) 431 if err != nil { 432 return fmt.Errorf("Error while renaming Node ID: %q from %s to %s", 433 node.ID, n.Node, node.Node) 434 } 435 } 436 } else { 437 // We allow to "steal" another node name that would have no ID 438 // It basically means that we allow upgrading a node without ID and add the ID 439 dupNameError := s.ensureNoNodeWithSimilarNameTxn(tx, node, true) 440 if dupNameError != nil { 441 return fmt.Errorf("Error while renaming Node ID: %q: %s", node.ID, dupNameError) 442 } 443 } 444 } 445 // TODO: else Node.ID == "" should be forbidden in future Consul releases 446 // See https://github.com/hashicorp/consul/pull/3983 for context 447 448 // Check for an existing node by name to support nodes with no IDs. 449 if n == nil { 450 existing, err := tx.First("nodes", "id", node.Node) 451 if err != nil { 452 return fmt.Errorf("node name lookup failed: %s", err) 453 } 454 455 if existing != nil { 456 n = existing.(*structs.Node) 457 } 458 // WARNING, for compatibility reasons with tests, we do not check 459 // for case insensitive matches, which may lead to DB corruption 460 // See https://github.com/hashicorp/consul/pull/3983 for context 461 } 462 463 // Get the indexes. 464 if n != nil { 465 node.CreateIndex = n.CreateIndex 466 node.ModifyIndex = n.ModifyIndex 467 // We do not need to update anything 468 if node.IsSame(n) { 469 return nil 470 } 471 node.ModifyIndex = idx 472 } else { 473 node.CreateIndex = idx 474 node.ModifyIndex = idx 475 } 476 477 // Insert the node and update the index. 478 if err := tx.Insert("nodes", node); err != nil { 479 return fmt.Errorf("failed inserting node: %s", err) 480 } 481 if err := tx.Insert("index", &IndexEntry{"nodes", idx}); err != nil { 482 return fmt.Errorf("failed updating index: %s", err) 483 } 484 // Update the node's service indexes as the node information is included 485 // in health queries and we would otherwise miss node updates in some cases 486 // for those queries. 487 if err := s.updateAllServiceIndexesOfNode(tx, idx, node.Node); err != nil { 488 return fmt.Errorf("failed updating index: %s", err) 489 } 490 491 return nil 492 } 493 494 // GetNode is used to retrieve a node registration by node name ID. 495 func (s *Store) GetNode(id string) (uint64, *structs.Node, error) { 496 tx := s.db.Txn(false) 497 defer tx.Abort() 498 499 // Get the table index. 500 idx := maxIndexTxn(tx, "nodes") 501 502 // Retrieve the node from the state store 503 node, err := getNodeTxn(tx, id) 504 if err != nil { 505 return 0, nil, fmt.Errorf("node lookup failed: %s", err) 506 } 507 return idx, node, nil 508 } 509 510 func getNodeTxn(tx *memdb.Txn, nodeName string) (*structs.Node, error) { 511 node, err := tx.First("nodes", "id", nodeName) 512 if err != nil { 513 return nil, fmt.Errorf("node lookup failed: %s", err) 514 } 515 if node != nil { 516 return node.(*structs.Node), nil 517 } 518 return nil, nil 519 } 520 521 func getNodeIDTxn(tx *memdb.Txn, id types.NodeID) (*structs.Node, error) { 522 strnode := string(id) 523 uuidValue, err := uuid.ParseUUID(strnode) 524 if err != nil { 525 return nil, fmt.Errorf("node lookup by ID failed, wrong UUID: %v for '%s'", err, strnode) 526 } 527 528 node, err := tx.First("nodes", "uuid", uuidValue) 529 if err != nil { 530 return nil, fmt.Errorf("node lookup by ID failed: %s", err) 531 } 532 if node != nil { 533 return node.(*structs.Node), nil 534 } 535 return nil, nil 536 } 537 538 // GetNodeID is used to retrieve a node registration by node ID. 539 func (s *Store) GetNodeID(id types.NodeID) (uint64, *structs.Node, error) { 540 tx := s.db.Txn(false) 541 defer tx.Abort() 542 543 // Get the table index. 544 idx := maxIndexTxn(tx, "nodes") 545 546 // Retrieve the node from the state store 547 node, err := getNodeIDTxn(tx, id) 548 return idx, node, err 549 } 550 551 // Nodes is used to return all of the known nodes. 552 func (s *Store) Nodes(ws memdb.WatchSet) (uint64, structs.Nodes, error) { 553 tx := s.db.Txn(false) 554 defer tx.Abort() 555 556 // Get the table index. 557 idx := maxIndexTxn(tx, "nodes") 558 559 // Retrieve all of the nodes 560 nodes, err := tx.Get("nodes", "id") 561 if err != nil { 562 return 0, nil, fmt.Errorf("failed nodes lookup: %s", err) 563 } 564 ws.Add(nodes.WatchCh()) 565 566 // Create and return the nodes list. 567 var results structs.Nodes 568 for node := nodes.Next(); node != nil; node = nodes.Next() { 569 results = append(results, node.(*structs.Node)) 570 } 571 return idx, results, nil 572 } 573 574 // NodesByMeta is used to return all nodes with the given metadata key/value pairs. 575 func (s *Store) NodesByMeta(ws memdb.WatchSet, filters map[string]string) (uint64, structs.Nodes, error) { 576 tx := s.db.Txn(false) 577 defer tx.Abort() 578 579 // Get the table index. 580 idx := maxIndexTxn(tx, "nodes") 581 582 // Retrieve all of the nodes 583 var args []interface{} 584 for key, value := range filters { 585 args = append(args, key, value) 586 break 587 } 588 nodes, err := tx.Get("nodes", "meta", args...) 589 if err != nil { 590 return 0, nil, fmt.Errorf("failed nodes lookup: %s", err) 591 } 592 ws.Add(nodes.WatchCh()) 593 594 // Create and return the nodes list. 595 var results structs.Nodes 596 for node := nodes.Next(); node != nil; node = nodes.Next() { 597 n := node.(*structs.Node) 598 if len(filters) <= 1 || structs.SatisfiesMetaFilters(n.Meta, filters) { 599 results = append(results, n) 600 } 601 } 602 return idx, results, nil 603 } 604 605 // DeleteNode is used to delete a given node by its ID. 606 func (s *Store) DeleteNode(idx uint64, nodeName string) error { 607 tx := s.db.Txn(true) 608 defer tx.Abort() 609 610 // Call the node deletion. 611 if err := s.deleteNodeTxn(tx, idx, nodeName); err != nil { 612 return err 613 } 614 615 tx.Commit() 616 return nil 617 } 618 619 // deleteNodeCASTxn is used to try doing a node delete operation with a given 620 // raft index. If the CAS index specified is not equal to the last observed index for 621 // the given check, then the call is a noop, otherwise a normal check delete is invoked. 622 func (s *Store) deleteNodeCASTxn(tx *memdb.Txn, idx, cidx uint64, nodeName string) (bool, error) { 623 // Look up the node. 624 node, err := getNodeTxn(tx, nodeName) 625 if err != nil { 626 return false, err 627 } 628 if node == nil { 629 return false, nil 630 } 631 632 // If the existing index does not match the provided CAS 633 // index arg, then we shouldn't update anything and can safely 634 // return early here. 635 if node.ModifyIndex != cidx { 636 return false, nil 637 } 638 639 // Call the actual deletion if the above passed. 640 if err := s.deleteNodeTxn(tx, idx, nodeName); err != nil { 641 return false, err 642 } 643 644 return true, nil 645 } 646 647 // deleteNodeTxn is the inner method used for removing a node from 648 // the store within a given transaction. 649 func (s *Store) deleteNodeTxn(tx *memdb.Txn, idx uint64, nodeName string) error { 650 // Look up the node. 651 node, err := tx.First("nodes", "id", nodeName) 652 if err != nil { 653 return fmt.Errorf("node lookup failed: %s", err) 654 } 655 if node == nil { 656 return nil 657 } 658 659 // Delete all services associated with the node and update the service index. 660 services, err := tx.Get("services", "node", nodeName) 661 if err != nil { 662 return fmt.Errorf("failed service lookup: %s", err) 663 } 664 var sids []string 665 for service := services.Next(); service != nil; service = services.Next() { 666 svc := service.(*structs.ServiceNode) 667 sids = append(sids, svc.ServiceID) 668 if err := tx.Insert("index", &IndexEntry{serviceIndexName(svc.ServiceName), idx}); err != nil { 669 return fmt.Errorf("failed updating index: %s", err) 670 } 671 } 672 673 // Do the delete in a separate loop so we don't trash the iterator. 674 for _, sid := range sids { 675 if err := s.deleteServiceTxn(tx, idx, nodeName, sid); err != nil { 676 return err 677 } 678 } 679 680 // Delete all checks associated with the node. This will invalidate 681 // sessions as necessary. 682 checks, err := tx.Get("checks", "node", nodeName) 683 if err != nil { 684 return fmt.Errorf("failed check lookup: %s", err) 685 } 686 var cids []types.CheckID 687 for check := checks.Next(); check != nil; check = checks.Next() { 688 cids = append(cids, check.(*structs.HealthCheck).CheckID) 689 } 690 691 // Do the delete in a separate loop so we don't trash the iterator. 692 for _, cid := range cids { 693 if err := s.deleteCheckTxn(tx, idx, nodeName, cid); err != nil { 694 return err 695 } 696 } 697 698 // Delete any coordinates associated with this node. 699 coords, err := tx.Get("coordinates", "node", nodeName) 700 if err != nil { 701 return fmt.Errorf("failed coordinate lookup: %s", err) 702 } 703 for coord := coords.Next(); coord != nil; coord = coords.Next() { 704 if err := tx.Delete("coordinates", coord); err != nil { 705 return fmt.Errorf("failed deleting coordinate: %s", err) 706 } 707 if err := tx.Insert("index", &IndexEntry{"coordinates", idx}); err != nil { 708 return fmt.Errorf("failed updating index: %s", err) 709 } 710 } 711 712 // Delete the node and update the index. 713 if err := tx.Delete("nodes", node); err != nil { 714 return fmt.Errorf("failed deleting node: %s", err) 715 } 716 if err := tx.Insert("index", &IndexEntry{"nodes", idx}); err != nil { 717 return fmt.Errorf("failed updating index: %s", err) 718 } 719 720 // Invalidate any sessions for this node. 721 sessions, err := tx.Get("sessions", "node", nodeName) 722 if err != nil { 723 return fmt.Errorf("failed session lookup: %s", err) 724 } 725 var ids []string 726 for sess := sessions.Next(); sess != nil; sess = sessions.Next() { 727 ids = append(ids, sess.(*structs.Session).ID) 728 } 729 730 // Do the delete in a separate loop so we don't trash the iterator. 731 for _, id := range ids { 732 if err := s.deleteSessionTxn(tx, idx, id); err != nil { 733 return fmt.Errorf("failed session delete: %s", err) 734 } 735 } 736 737 return nil 738 } 739 740 // EnsureService is called to upsert creation of a given NodeService. 741 func (s *Store) EnsureService(idx uint64, node string, svc *structs.NodeService) error { 742 tx := s.db.Txn(true) 743 defer tx.Abort() 744 745 // Call the service registration upsert 746 if err := s.ensureServiceTxn(tx, idx, node, svc); err != nil { 747 return err 748 } 749 750 tx.Commit() 751 return nil 752 } 753 754 // ensureServiceCASTxn updates a service only if the existing index matches the given index. 755 // Returns a bool indicating if a write happened and any error. 756 func (s *Store) ensureServiceCASTxn(tx *memdb.Txn, idx uint64, node string, svc *structs.NodeService) (bool, error) { 757 // Retrieve the existing service. 758 existing, err := tx.First("services", "id", node, svc.ID) 759 if err != nil { 760 return false, fmt.Errorf("failed service lookup: %s", err) 761 } 762 763 // Check if the we should do the set. A ModifyIndex of 0 means that 764 // we are doing a set-if-not-exists. 765 if svc.ModifyIndex == 0 && existing != nil { 766 return false, nil 767 } 768 if svc.ModifyIndex != 0 && existing == nil { 769 return false, nil 770 } 771 e, ok := existing.(*structs.Node) 772 if ok && svc.ModifyIndex != 0 && svc.ModifyIndex != e.ModifyIndex { 773 return false, nil 774 } 775 776 // Perform the update. 777 if err := s.ensureServiceTxn(tx, idx, node, svc); err != nil { 778 return false, err 779 } 780 781 return true, nil 782 } 783 784 // ensureServiceTxn is used to upsert a service registration within an 785 // existing memdb transaction. 786 func (s *Store) ensureServiceTxn(tx *memdb.Txn, idx uint64, node string, svc *structs.NodeService) error { 787 // Check for existing service 788 existing, err := tx.First("services", "id", node, svc.ID) 789 if err != nil { 790 return fmt.Errorf("failed service lookup: %s", err) 791 } 792 793 if err = structs.ValidateMetadata(svc.Meta, false); err != nil { 794 return fmt.Errorf("Invalid Service Meta for node %s and serviceID %s: %v", node, svc.ID, err) 795 } 796 // Create the service node entry and populate the indexes. Note that 797 // conversion doesn't populate any of the node-specific information. 798 // That's always populated when we read from the state store. 799 entry := svc.ToServiceNode(node) 800 // Get the node 801 n, err := tx.First("nodes", "id", node) 802 if err != nil { 803 return fmt.Errorf("failed node lookup: %s", err) 804 } 805 if n == nil { 806 return ErrMissingNode 807 } 808 if existing != nil { 809 serviceNode := existing.(*structs.ServiceNode) 810 entry.CreateIndex = serviceNode.CreateIndex 811 entry.ModifyIndex = serviceNode.ModifyIndex 812 // We cannot return here because: we want to keep existing behavior (ex: failed node lookup -> ErrMissingNode) 813 // It might be modified in future, but it requires changing many unit tests 814 // Enforcing saving the entry also ensures that if we add default values in .ToServiceNode() 815 // those values will be saved even if node is not really modified for a while. 816 if entry.IsSameService(serviceNode) { 817 return nil 818 } 819 } else { 820 entry.CreateIndex = idx 821 } 822 entry.ModifyIndex = idx 823 824 // Insert the service and update the index 825 if err := tx.Insert("services", entry); err != nil { 826 return fmt.Errorf("failed inserting service: %s", err) 827 } 828 if err := tx.Insert("index", &IndexEntry{"services", idx}); err != nil { 829 return fmt.Errorf("failed updating index: %s", err) 830 } 831 if err := tx.Insert("index", &IndexEntry{serviceIndexName(svc.Service), idx}); err != nil { 832 return fmt.Errorf("failed updating index: %s", err) 833 } 834 835 return nil 836 } 837 838 // Services returns all services along with a list of associated tags. 839 func (s *Store) Services(ws memdb.WatchSet) (uint64, structs.Services, error) { 840 tx := s.db.Txn(false) 841 defer tx.Abort() 842 843 // Get the table index. 844 idx := maxIndexTxn(tx, "services") 845 846 // List all the services. 847 services, err := tx.Get("services", "id") 848 if err != nil { 849 return 0, nil, fmt.Errorf("failed querying services: %s", err) 850 } 851 ws.Add(services.WatchCh()) 852 853 // Rip through the services and enumerate them and their unique set of 854 // tags. 855 unique := make(map[string]map[string]struct{}) 856 for service := services.Next(); service != nil; service = services.Next() { 857 svc := service.(*structs.ServiceNode) 858 tags, ok := unique[svc.ServiceName] 859 if !ok { 860 unique[svc.ServiceName] = make(map[string]struct{}) 861 tags = unique[svc.ServiceName] 862 } 863 for _, tag := range svc.ServiceTags { 864 tags[tag] = struct{}{} 865 } 866 } 867 868 // Generate the output structure. 869 var results = make(structs.Services) 870 for service, tags := range unique { 871 results[service] = make([]string, 0) 872 for tag := range tags { 873 results[service] = append(results[service], tag) 874 } 875 } 876 return idx, results, nil 877 } 878 879 // ServicesByNodeMeta returns all services, filtered by the given node metadata. 880 func (s *Store) ServicesByNodeMeta(ws memdb.WatchSet, filters map[string]string) (uint64, structs.Services, error) { 881 tx := s.db.Txn(false) 882 defer tx.Abort() 883 884 // Get the table index. 885 idx := maxIndexTxn(tx, "services", "nodes") 886 887 // Retrieve all of the nodes with the meta k/v pair 888 var args []interface{} 889 for key, value := range filters { 890 args = append(args, key, value) 891 break 892 } 893 nodes, err := tx.Get("nodes", "meta", args...) 894 if err != nil { 895 return 0, nil, fmt.Errorf("failed nodes lookup: %s", err) 896 } 897 ws.Add(nodes.WatchCh()) 898 899 // We don't want to track an unlimited number of services, so we pull a 900 // top-level watch to use as a fallback. 901 allServices, err := tx.Get("services", "id") 902 if err != nil { 903 return 0, nil, fmt.Errorf("failed services lookup: %s", err) 904 } 905 allServicesCh := allServices.WatchCh() 906 907 // Populate the services map 908 unique := make(map[string]map[string]struct{}) 909 for node := nodes.Next(); node != nil; node = nodes.Next() { 910 n := node.(*structs.Node) 911 if len(filters) > 1 && !structs.SatisfiesMetaFilters(n.Meta, filters) { 912 continue 913 } 914 915 // List all the services on the node 916 services, err := tx.Get("services", "node", n.Node) 917 if err != nil { 918 return 0, nil, fmt.Errorf("failed querying services: %s", err) 919 } 920 ws.AddWithLimit(watchLimit, services.WatchCh(), allServicesCh) 921 922 // Rip through the services and enumerate them and their unique set of 923 // tags. 924 for service := services.Next(); service != nil; service = services.Next() { 925 svc := service.(*structs.ServiceNode) 926 tags, ok := unique[svc.ServiceName] 927 if !ok { 928 unique[svc.ServiceName] = make(map[string]struct{}) 929 tags = unique[svc.ServiceName] 930 } 931 for _, tag := range svc.ServiceTags { 932 tags[tag] = struct{}{} 933 } 934 } 935 } 936 937 // Generate the output structure. 938 var results = make(structs.Services) 939 for service, tags := range unique { 940 results[service] = make([]string, 0) 941 for tag := range tags { 942 results[service] = append(results[service], tag) 943 } 944 } 945 return idx, results, nil 946 } 947 948 // maxIndexForService return the maximum Raft Index for a service 949 // If the index is not set for the service, it will return the missing 950 // service index. 951 // The service_last_extinction is set to the last raft index when a service 952 // was unregistered (or 0 if no services were ever unregistered). This 953 // allows blocking queries to 954 // * return when the last instance of a service is removed 955 // * block until an instance for this service is available, or another 956 // service is unregistered. 957 func maxIndexForService(tx *memdb.Txn, serviceName string, serviceExists, checks bool) uint64 { 958 idx, _ := maxIndexAndWatchChForService(tx, serviceName, serviceExists, checks) 959 return idx 960 } 961 962 // maxIndexAndWatchChForService return the maximum Raft Index for a service. If 963 // the index is not set for the service, it will return the missing service 964 // index. The service_last_extinction is set to the last raft index when a 965 // service was unregistered (or 0 if no services were ever unregistered). This 966 // allows blocking queries to 967 // * return when the last instance of a service is removed 968 // * block until an instance for this service is available, or another 969 // service is unregistered. 970 // 971 // It also _may_ return a watch chan to add to a WatchSet. It will only return 972 // one if the service exists, and has a service index. If it doesn't then nil is 973 // returned for the chan. This allows for blocking watchers to _only_ watch this 974 // one chan in the common case, falling back to watching all touched MemDB 975 // indexes in more complicated cases. 976 func maxIndexAndWatchChForService(tx *memdb.Txn, serviceName string, serviceExists, checks bool) (uint64, <-chan struct{}) { 977 if !serviceExists { 978 res, err := tx.First("index", "id", serviceLastExtinctionIndexName) 979 if missingIdx, ok := res.(*IndexEntry); ok && err == nil { 980 // Not safe to only watch the extinction index as it's not updated when 981 // new instances come along so return nil watchCh. 982 return missingIdx.Value, nil 983 } 984 } 985 986 ch, res, err := tx.FirstWatch("index", "id", serviceIndexName(serviceName)) 987 if idx, ok := res.(*IndexEntry); ok && err == nil { 988 return idx.Value, ch 989 } 990 if checks { 991 return maxIndexTxn(tx, "nodes", "services", "checks"), nil 992 } 993 994 return maxIndexTxn(tx, "nodes", "services"), nil 995 } 996 997 // ConnectServiceNodes returns the nodes associated with a Connect 998 // compatible destination for the given service name. This will include 999 // both proxies and native integrations. 1000 func (s *Store) ConnectServiceNodes(ws memdb.WatchSet, serviceName string) (uint64, structs.ServiceNodes, error) { 1001 return s.serviceNodes(ws, serviceName, true) 1002 } 1003 1004 // ServiceNodes returns the nodes associated with a given service name. 1005 func (s *Store) ServiceNodes(ws memdb.WatchSet, serviceName string) (uint64, structs.ServiceNodes, error) { 1006 return s.serviceNodes(ws, serviceName, false) 1007 } 1008 1009 func (s *Store) serviceNodes(ws memdb.WatchSet, serviceName string, connect bool) (uint64, structs.ServiceNodes, error) { 1010 tx := s.db.Txn(false) 1011 defer tx.Abort() 1012 1013 // Function for lookup 1014 var f func() (memdb.ResultIterator, error) 1015 if !connect { 1016 f = func() (memdb.ResultIterator, error) { 1017 return tx.Get("services", "service", serviceName) 1018 } 1019 } else { 1020 f = func() (memdb.ResultIterator, error) { 1021 return tx.Get("services", "connect", serviceName) 1022 } 1023 } 1024 1025 // List all the services. 1026 services, err := f() 1027 if err != nil { 1028 return 0, nil, fmt.Errorf("failed service lookup: %s", err) 1029 } 1030 ws.Add(services.WatchCh()) 1031 1032 var results structs.ServiceNodes 1033 for service := services.Next(); service != nil; service = services.Next() { 1034 results = append(results, service.(*structs.ServiceNode)) 1035 } 1036 1037 // Fill in the node details. 1038 results, err = s.parseServiceNodes(tx, ws, results) 1039 if err != nil { 1040 return 0, nil, fmt.Errorf("failed parsing service nodes: %s", err) 1041 } 1042 1043 // Get the table index. 1044 idx := maxIndexForService(tx, serviceName, len(results) > 0, false) 1045 1046 return idx, results, nil 1047 } 1048 1049 // ServiceTagNodes returns the nodes associated with a given service, filtering 1050 // out services that don't contain the given tags. 1051 func (s *Store) ServiceTagNodes(ws memdb.WatchSet, service string, tags []string) (uint64, structs.ServiceNodes, error) { 1052 tx := s.db.Txn(false) 1053 defer tx.Abort() 1054 1055 // List all the services. 1056 services, err := tx.Get("services", "service", service) 1057 if err != nil { 1058 return 0, nil, fmt.Errorf("failed service lookup: %s", err) 1059 } 1060 ws.Add(services.WatchCh()) 1061 1062 // Gather all the services and apply the tag filter. 1063 serviceExists := false 1064 var results structs.ServiceNodes 1065 for service := services.Next(); service != nil; service = services.Next() { 1066 svc := service.(*structs.ServiceNode) 1067 serviceExists = true 1068 if !serviceTagsFilter(svc, tags) { 1069 results = append(results, svc) 1070 } 1071 } 1072 1073 // Fill in the node details. 1074 results, err = s.parseServiceNodes(tx, ws, results) 1075 if err != nil { 1076 return 0, nil, fmt.Errorf("failed parsing service nodes: %s", err) 1077 } 1078 // Get the table index. 1079 idx := maxIndexForService(tx, service, serviceExists, false) 1080 1081 return idx, results, nil 1082 } 1083 1084 // serviceTagFilter returns true (should filter) if the given service node 1085 // doesn't contain the given tag. 1086 func serviceTagFilter(sn *structs.ServiceNode, tag string) bool { 1087 tag = strings.ToLower(tag) 1088 1089 // Look for the lower cased version of the tag. 1090 for _, t := range sn.ServiceTags { 1091 if strings.ToLower(t) == tag { 1092 return false 1093 } 1094 } 1095 1096 // If we didn't hit the tag above then we should filter. 1097 return true 1098 } 1099 1100 // serviceTagsFilter returns true (should filter) if the given service node 1101 // doesn't contain the given set of tags. 1102 func serviceTagsFilter(sn *structs.ServiceNode, tags []string) bool { 1103 for _, tag := range tags { 1104 if serviceTagFilter(sn, tag) { 1105 // If any one of the expected tags was not found, filter the service 1106 return true 1107 } 1108 } 1109 1110 // If all tags were found, don't filter the service 1111 return false 1112 } 1113 1114 // ServiceAddressNodes returns the nodes associated with a given service, filtering 1115 // out services that don't match the given serviceAddress 1116 func (s *Store) ServiceAddressNodes(ws memdb.WatchSet, address string) (uint64, structs.ServiceNodes, error) { 1117 tx := s.db.Txn(false) 1118 defer tx.Abort() 1119 1120 // List all the services. 1121 services, err := tx.Get("services", "id") 1122 if err != nil { 1123 return 0, nil, fmt.Errorf("failed service lookup: %s", err) 1124 } 1125 ws.Add(services.WatchCh()) 1126 1127 // Gather all the services and apply the tag filter. 1128 var results structs.ServiceNodes 1129 for service := services.Next(); service != nil; service = services.Next() { 1130 svc := service.(*structs.ServiceNode) 1131 if svc.ServiceAddress == address { 1132 results = append(results, svc) 1133 } 1134 } 1135 1136 // Fill in the node details. 1137 results, err = s.parseServiceNodes(tx, ws, results) 1138 if err != nil { 1139 return 0, nil, fmt.Errorf("failed parsing service nodes: %s", err) 1140 } 1141 return 0, results, nil 1142 } 1143 1144 // parseServiceNodes iterates over a services query and fills in the node details, 1145 // returning a ServiceNodes slice. 1146 func (s *Store) parseServiceNodes(tx *memdb.Txn, ws memdb.WatchSet, services structs.ServiceNodes) (structs.ServiceNodes, error) { 1147 // We don't want to track an unlimited number of nodes, so we pull a 1148 // top-level watch to use as a fallback. 1149 allNodes, err := tx.Get("nodes", "id") 1150 if err != nil { 1151 return nil, fmt.Errorf("failed nodes lookup: %s", err) 1152 } 1153 allNodesCh := allNodes.WatchCh() 1154 1155 // Fill in the node data for each service instance. 1156 var results structs.ServiceNodes 1157 for _, sn := range services { 1158 // Note that we have to clone here because we don't want to 1159 // modify the node-related fields on the object in the database, 1160 // which is what we are referencing. 1161 s := sn.PartialClone() 1162 1163 // Grab the corresponding node record. 1164 watchCh, n, err := tx.FirstWatch("nodes", "id", sn.Node) 1165 if err != nil { 1166 return nil, fmt.Errorf("failed node lookup: %s", err) 1167 } 1168 ws.AddWithLimit(watchLimit, watchCh, allNodesCh) 1169 1170 // Populate the node-related fields. The tagged addresses may be 1171 // used by agents to perform address translation if they are 1172 // configured to do that. 1173 node := n.(*structs.Node) 1174 s.ID = node.ID 1175 s.Address = node.Address 1176 s.Datacenter = node.Datacenter 1177 s.TaggedAddresses = node.TaggedAddresses 1178 s.NodeMeta = node.Meta 1179 1180 results = append(results, s) 1181 } 1182 return results, nil 1183 } 1184 1185 // NodeService is used to retrieve a specific service associated with the given 1186 // node. 1187 func (s *Store) NodeService(nodeName string, serviceID string) (uint64, *structs.NodeService, error) { 1188 tx := s.db.Txn(false) 1189 defer tx.Abort() 1190 1191 // Get the table index. 1192 idx := maxIndexTxn(tx, "services") 1193 1194 // Query the service 1195 service, err := s.getNodeServiceTxn(tx, nodeName, serviceID) 1196 if err != nil { 1197 return 0, nil, fmt.Errorf("failed querying service for node %q: %s", nodeName, err) 1198 } 1199 1200 return idx, service, nil 1201 } 1202 1203 func (s *Store) getNodeServiceTxn(tx *memdb.Txn, nodeName, serviceID string) (*structs.NodeService, error) { 1204 // Query the service 1205 service, err := tx.First("services", "id", nodeName, serviceID) 1206 if err != nil { 1207 return nil, fmt.Errorf("failed querying service for node %q: %s", nodeName, err) 1208 } 1209 1210 if service != nil { 1211 return service.(*structs.ServiceNode).ToNodeService(), nil 1212 } 1213 1214 return nil, nil 1215 } 1216 1217 // NodeServices is used to query service registrations by node name or UUID. 1218 func (s *Store) NodeServices(ws memdb.WatchSet, nodeNameOrID string) (uint64, *structs.NodeServices, error) { 1219 tx := s.db.Txn(false) 1220 defer tx.Abort() 1221 1222 // Get the table index. 1223 idx := maxIndexTxn(tx, "nodes", "services") 1224 1225 // Query the node by node name 1226 watchCh, n, err := tx.FirstWatch("nodes", "id", nodeNameOrID) 1227 if err != nil { 1228 return 0, nil, fmt.Errorf("node lookup failed: %s", err) 1229 } 1230 1231 if n != nil { 1232 ws.Add(watchCh) 1233 } else { 1234 if len(nodeNameOrID) < minUUIDLookupLen { 1235 ws.Add(watchCh) 1236 return 0, nil, nil 1237 } 1238 1239 // Attempt to lookup the node by its node ID 1240 iter, err := tx.Get("nodes", "uuid_prefix", resizeNodeLookupKey(nodeNameOrID)) 1241 if err != nil { 1242 ws.Add(watchCh) 1243 // TODO(sean@): We could/should log an error re: the uuid_prefix lookup 1244 // failing once a logger has been introduced to the catalog. 1245 return 0, nil, nil 1246 } 1247 1248 n = iter.Next() 1249 if n == nil { 1250 // No nodes matched, even with the Node ID: add a watch on the node name. 1251 ws.Add(watchCh) 1252 return 0, nil, nil 1253 } 1254 1255 idWatchCh := iter.WatchCh() 1256 if iter.Next() != nil { 1257 // More than one match present: Watch on the node name channel and return 1258 // an empty result (node lookups can not be ambiguous). 1259 ws.Add(watchCh) 1260 return 0, nil, nil 1261 } 1262 1263 ws.Add(idWatchCh) 1264 } 1265 1266 node := n.(*structs.Node) 1267 nodeName := node.Node 1268 1269 // Read all of the services 1270 services, err := tx.Get("services", "node", nodeName) 1271 if err != nil { 1272 return 0, nil, fmt.Errorf("failed querying services for node %q: %s", nodeName, err) 1273 } 1274 ws.Add(services.WatchCh()) 1275 1276 // Initialize the node services struct 1277 ns := &structs.NodeServices{ 1278 Node: node, 1279 Services: make(map[string]*structs.NodeService), 1280 } 1281 1282 // Add all of the services to the map. 1283 for service := services.Next(); service != nil; service = services.Next() { 1284 svc := service.(*structs.ServiceNode).ToNodeService() 1285 ns.Services[svc.ID] = svc 1286 } 1287 1288 return idx, ns, nil 1289 } 1290 1291 // DeleteService is used to delete a given service associated with a node. 1292 func (s *Store) DeleteService(idx uint64, nodeName, serviceID string) error { 1293 tx := s.db.Txn(true) 1294 defer tx.Abort() 1295 1296 // Call the service deletion 1297 if err := s.deleteServiceTxn(tx, idx, nodeName, serviceID); err != nil { 1298 return err 1299 } 1300 1301 tx.Commit() 1302 return nil 1303 } 1304 1305 func serviceIndexName(name string) string { 1306 return fmt.Sprintf("service.%s", name) 1307 } 1308 1309 // deleteServiceCASTxn is used to try doing a service delete operation with a given 1310 // raft index. If the CAS index specified is not equal to the last observed index for 1311 // the given service, then the call is a noop, otherwise a normal delete is invoked. 1312 func (s *Store) deleteServiceCASTxn(tx *memdb.Txn, idx, cidx uint64, nodeName, serviceID string) (bool, error) { 1313 // Look up the service. 1314 service, err := s.getNodeServiceTxn(tx, nodeName, serviceID) 1315 if err != nil { 1316 return false, fmt.Errorf("service lookup failed: %s", err) 1317 } 1318 if service == nil { 1319 return false, nil 1320 } 1321 1322 // If the existing index does not match the provided CAS 1323 // index arg, then we shouldn't update anything and can safely 1324 // return early here. 1325 if service.ModifyIndex != cidx { 1326 return false, nil 1327 } 1328 1329 // Call the actual deletion if the above passed. 1330 if err := s.deleteServiceTxn(tx, idx, nodeName, serviceID); err != nil { 1331 return false, err 1332 } 1333 1334 return true, nil 1335 } 1336 1337 // deleteServiceTxn is the inner method called to remove a service 1338 // registration within an existing transaction. 1339 func (s *Store) deleteServiceTxn(tx *memdb.Txn, idx uint64, nodeName, serviceID string) error { 1340 // Look up the service. 1341 service, err := tx.First("services", "id", nodeName, serviceID) 1342 if err != nil { 1343 return fmt.Errorf("failed service lookup: %s", err) 1344 } 1345 if service == nil { 1346 return nil 1347 } 1348 1349 // Delete any checks associated with the service. This will invalidate 1350 // sessions as necessary. 1351 checks, err := tx.Get("checks", "node_service", nodeName, serviceID) 1352 if err != nil { 1353 return fmt.Errorf("failed service check lookup: %s", err) 1354 } 1355 var cids []types.CheckID 1356 for check := checks.Next(); check != nil; check = checks.Next() { 1357 cids = append(cids, check.(*structs.HealthCheck).CheckID) 1358 } 1359 1360 // Do the delete in a separate loop so we don't trash the iterator. 1361 for _, cid := range cids { 1362 if err := s.deleteCheckTxn(tx, idx, nodeName, cid); err != nil { 1363 return err 1364 } 1365 } 1366 1367 // Update the index. 1368 if err := tx.Insert("index", &IndexEntry{"checks", idx}); err != nil { 1369 return fmt.Errorf("failed updating index: %s", err) 1370 } 1371 1372 // Delete the service and update the index 1373 if err := tx.Delete("services", service); err != nil { 1374 return fmt.Errorf("failed deleting service: %s", err) 1375 } 1376 if err := tx.Insert("index", &IndexEntry{"services", idx}); err != nil { 1377 return fmt.Errorf("failed updating index: %s", err) 1378 } 1379 1380 svc := service.(*structs.ServiceNode) 1381 if remainingService, err := tx.First("services", "service", svc.ServiceName); err == nil { 1382 if remainingService != nil { 1383 // We have at least one remaining service, update the index 1384 if err := tx.Insert("index", &IndexEntry{serviceIndexName(svc.ServiceName), idx}); err != nil { 1385 return fmt.Errorf("failed updating index: %s", err) 1386 } 1387 } else { 1388 // There are no more service instances, cleanup the service.<serviceName> index 1389 serviceIndex, err := tx.First("index", "id", serviceIndexName(svc.ServiceName)) 1390 if err == nil && serviceIndex != nil { 1391 // we found service.<serviceName> index, garbage collect it 1392 if errW := tx.Delete("index", serviceIndex); errW != nil { 1393 return fmt.Errorf("[FAILED] deleting serviceIndex %s: %s", svc.ServiceName, err) 1394 } 1395 } 1396 1397 if err := tx.Insert("index", &IndexEntry{serviceLastExtinctionIndexName, idx}); err != nil { 1398 return fmt.Errorf("failed updating missing service index: %s", err) 1399 } 1400 1401 } 1402 } else { 1403 return fmt.Errorf("Could not find any service %s: %s", svc.ServiceName, err) 1404 } 1405 return nil 1406 } 1407 1408 // EnsureCheck is used to store a check registration in the db. 1409 func (s *Store) EnsureCheck(idx uint64, hc *structs.HealthCheck) error { 1410 tx := s.db.Txn(true) 1411 defer tx.Abort() 1412 1413 // Call the check registration 1414 if err := s.ensureCheckTxn(tx, idx, hc); err != nil { 1415 return err 1416 } 1417 1418 tx.Commit() 1419 return nil 1420 } 1421 1422 // updateAllServiceIndexesOfNode updates the Raft index of all the services associated with this node 1423 func (s *Store) updateAllServiceIndexesOfNode(tx *memdb.Txn, idx uint64, nodeID string) error { 1424 services, err := tx.Get("services", "node", nodeID) 1425 if err != nil { 1426 return fmt.Errorf("failed updating services for node %s: %s", nodeID, err) 1427 } 1428 for service := services.Next(); service != nil; service = services.Next() { 1429 svc := service.(*structs.ServiceNode).ToNodeService() 1430 if err := tx.Insert("index", &IndexEntry{serviceIndexName(svc.Service), idx}); err != nil { 1431 return fmt.Errorf("failed updating index: %s", err) 1432 } 1433 } 1434 return nil 1435 } 1436 1437 // ensureCheckCASTxn updates a check only if the existing index matches the given index. 1438 // Returns a bool indicating if a write happened and any error. 1439 func (s *Store) ensureCheckCASTxn(tx *memdb.Txn, idx uint64, hc *structs.HealthCheck) (bool, error) { 1440 // Retrieve the existing entry. 1441 _, existing, err := s.getNodeCheckTxn(tx, hc.Node, hc.CheckID) 1442 if err != nil { 1443 return false, fmt.Errorf("failed health check lookup: %s", err) 1444 } 1445 1446 // Check if the we should do the set. A ModifyIndex of 0 means that 1447 // we are doing a set-if-not-exists. 1448 if hc.ModifyIndex == 0 && existing != nil { 1449 return false, nil 1450 } 1451 if hc.ModifyIndex != 0 && existing == nil { 1452 return false, nil 1453 } 1454 if existing != nil && hc.ModifyIndex != 0 && hc.ModifyIndex != existing.ModifyIndex { 1455 return false, nil 1456 } 1457 1458 // Perform the update. 1459 if err := s.ensureCheckTxn(tx, idx, hc); err != nil { 1460 return false, err 1461 } 1462 1463 return true, nil 1464 } 1465 1466 // ensureCheckTransaction is used as the inner method to handle inserting 1467 // a health check into the state store. It ensures safety against inserting 1468 // checks with no matching node or service. 1469 func (s *Store) ensureCheckTxn(tx *memdb.Txn, idx uint64, hc *structs.HealthCheck) error { 1470 // Check if we have an existing health check 1471 existing, err := tx.First("checks", "id", hc.Node, string(hc.CheckID)) 1472 if err != nil { 1473 return fmt.Errorf("failed health check lookup: %s", err) 1474 } 1475 1476 // Set the indexes 1477 if existing != nil { 1478 existingCheck := existing.(*structs.HealthCheck) 1479 hc.CreateIndex = existingCheck.CreateIndex 1480 hc.ModifyIndex = existingCheck.ModifyIndex 1481 } else { 1482 hc.CreateIndex = idx 1483 hc.ModifyIndex = idx 1484 } 1485 1486 // Use the default check status if none was provided 1487 if hc.Status == "" { 1488 hc.Status = api.HealthCritical 1489 } 1490 1491 // Get the node 1492 node, err := tx.First("nodes", "id", hc.Node) 1493 if err != nil { 1494 return fmt.Errorf("failed node lookup: %s", err) 1495 } 1496 if node == nil { 1497 return ErrMissingNode 1498 } 1499 1500 modified := true 1501 // If the check is associated with a service, check that we have 1502 // a registration for the service. 1503 if hc.ServiceID != "" { 1504 service, err := tx.First("services", "id", hc.Node, hc.ServiceID) 1505 if err != nil { 1506 return fmt.Errorf("failed service lookup: %s", err) 1507 } 1508 if service == nil { 1509 return ErrMissingService 1510 } 1511 1512 // Copy in the service name and tags 1513 svc := service.(*structs.ServiceNode) 1514 hc.ServiceName = svc.ServiceName 1515 hc.ServiceTags = svc.ServiceTags 1516 if existing != nil && existing.(*structs.HealthCheck).IsSame(hc) { 1517 modified = false 1518 } else { 1519 // Check has been modified, we trigger a index service change 1520 if err = tx.Insert("index", &IndexEntry{serviceIndexName(svc.ServiceName), idx}); err != nil { 1521 return fmt.Errorf("failed updating index: %s", err) 1522 } 1523 } 1524 } else { 1525 if existing != nil && existing.(*structs.HealthCheck).IsSame(hc) { 1526 modified = false 1527 } else { 1528 // Since the check has been modified, it impacts all services of node 1529 // Update the status for all the services associated with this node 1530 err = s.updateAllServiceIndexesOfNode(tx, idx, hc.Node) 1531 if err != nil { 1532 return err 1533 } 1534 } 1535 } 1536 1537 // Delete any sessions for this check if the health is critical. 1538 if hc.Status == api.HealthCritical { 1539 mappings, err := tx.Get("session_checks", "node_check", hc.Node, string(hc.CheckID)) 1540 if err != nil { 1541 return fmt.Errorf("failed session checks lookup: %s", err) 1542 } 1543 1544 var ids []string 1545 for mapping := mappings.Next(); mapping != nil; mapping = mappings.Next() { 1546 ids = append(ids, mapping.(*sessionCheck).Session) 1547 } 1548 1549 // Delete the session in a separate loop so we don't trash the 1550 // iterator. 1551 for _, id := range ids { 1552 if err := s.deleteSessionTxn(tx, idx, id); err != nil { 1553 return fmt.Errorf("failed deleting session: %s", err) 1554 } 1555 } 1556 } 1557 if modified { 1558 // We update the modify index, ONLY if something has changed, thus 1559 // With constant output, no change is seen when watching a service 1560 // With huge number of nodes where anti-entropy updates continuously 1561 // the checks, but not the values within the check 1562 hc.ModifyIndex = idx 1563 } 1564 1565 // Persist the check registration in the db. 1566 if err := tx.Insert("checks", hc); err != nil { 1567 return fmt.Errorf("failed inserting check: %s", err) 1568 } 1569 if err := tx.Insert("index", &IndexEntry{"checks", idx}); err != nil { 1570 return fmt.Errorf("failed updating index: %s", err) 1571 } 1572 1573 return nil 1574 } 1575 1576 // NodeCheck is used to retrieve a specific check associated with the given 1577 // node. 1578 func (s *Store) NodeCheck(nodeName string, checkID types.CheckID) (uint64, *structs.HealthCheck, error) { 1579 tx := s.db.Txn(false) 1580 defer tx.Abort() 1581 1582 return s.getNodeCheckTxn(tx, nodeName, checkID) 1583 } 1584 1585 // nodeCheckTxn is used as the inner method to handle reading a health check 1586 // from the state store. 1587 func (s *Store) getNodeCheckTxn(tx *memdb.Txn, nodeName string, checkID types.CheckID) (uint64, *structs.HealthCheck, error) { 1588 // Get the table index. 1589 idx := maxIndexTxn(tx, "checks") 1590 1591 // Return the check. 1592 check, err := tx.First("checks", "id", nodeName, string(checkID)) 1593 if err != nil { 1594 return 0, nil, fmt.Errorf("failed check lookup: %s", err) 1595 } 1596 1597 if check != nil { 1598 return idx, check.(*structs.HealthCheck), nil 1599 } 1600 return idx, nil, nil 1601 } 1602 1603 // NodeChecks is used to retrieve checks associated with the 1604 // given node from the state store. 1605 func (s *Store) NodeChecks(ws memdb.WatchSet, nodeName string) (uint64, structs.HealthChecks, error) { 1606 tx := s.db.Txn(false) 1607 defer tx.Abort() 1608 1609 // Get the table index. 1610 idx := maxIndexTxn(tx, "checks") 1611 1612 // Return the checks. 1613 iter, err := tx.Get("checks", "node", nodeName) 1614 if err != nil { 1615 return 0, nil, fmt.Errorf("failed check lookup: %s", err) 1616 } 1617 ws.Add(iter.WatchCh()) 1618 1619 var results structs.HealthChecks 1620 for check := iter.Next(); check != nil; check = iter.Next() { 1621 results = append(results, check.(*structs.HealthCheck)) 1622 } 1623 return idx, results, nil 1624 } 1625 1626 // ServiceChecks is used to get all checks associated with a 1627 // given service ID. The query is performed against a service 1628 // _name_ instead of a service ID. 1629 func (s *Store) ServiceChecks(ws memdb.WatchSet, serviceName string) (uint64, structs.HealthChecks, error) { 1630 tx := s.db.Txn(false) 1631 defer tx.Abort() 1632 1633 // Get the table index. 1634 idx := maxIndexTxn(tx, "checks") 1635 1636 // Return the checks. 1637 iter, err := tx.Get("checks", "service", serviceName) 1638 if err != nil { 1639 return 0, nil, fmt.Errorf("failed check lookup: %s", err) 1640 } 1641 ws.Add(iter.WatchCh()) 1642 1643 var results structs.HealthChecks 1644 for check := iter.Next(); check != nil; check = iter.Next() { 1645 results = append(results, check.(*structs.HealthCheck)) 1646 } 1647 return idx, results, nil 1648 } 1649 1650 // ServiceChecksByNodeMeta is used to get all checks associated with a 1651 // given service ID, filtered by the given node metadata values. The query 1652 // is performed against a service _name_ instead of a service ID. 1653 func (s *Store) ServiceChecksByNodeMeta(ws memdb.WatchSet, serviceName string, 1654 filters map[string]string) (uint64, structs.HealthChecks, error) { 1655 1656 tx := s.db.Txn(false) 1657 defer tx.Abort() 1658 1659 // Get the table index. 1660 idx := maxIndexForService(tx, serviceName, true, true) 1661 // Return the checks. 1662 iter, err := tx.Get("checks", "service", serviceName) 1663 if err != nil { 1664 return 0, nil, fmt.Errorf("failed check lookup: %s", err) 1665 } 1666 ws.Add(iter.WatchCh()) 1667 1668 return s.parseChecksByNodeMeta(tx, ws, idx, iter, filters) 1669 } 1670 1671 // ChecksInState is used to query the state store for all checks 1672 // which are in the provided state. 1673 func (s *Store) ChecksInState(ws memdb.WatchSet, state string) (uint64, structs.HealthChecks, error) { 1674 tx := s.db.Txn(false) 1675 defer tx.Abort() 1676 1677 // Get the table index. 1678 idx := maxIndexTxn(tx, "checks") 1679 1680 // Query all checks if HealthAny is passed, otherwise use the index. 1681 var iter memdb.ResultIterator 1682 var err error 1683 if state == api.HealthAny { 1684 iter, err = tx.Get("checks", "status") 1685 } else { 1686 iter, err = tx.Get("checks", "status", state) 1687 } 1688 if err != nil { 1689 return 0, nil, fmt.Errorf("failed check lookup: %s", err) 1690 } 1691 ws.Add(iter.WatchCh()) 1692 1693 var results structs.HealthChecks 1694 for check := iter.Next(); check != nil; check = iter.Next() { 1695 results = append(results, check.(*structs.HealthCheck)) 1696 } 1697 return idx, results, nil 1698 } 1699 1700 // ChecksInStateByNodeMeta is used to query the state store for all checks 1701 // which are in the provided state, filtered by the given node metadata values. 1702 func (s *Store) ChecksInStateByNodeMeta(ws memdb.WatchSet, state string, filters map[string]string) (uint64, structs.HealthChecks, error) { 1703 tx := s.db.Txn(false) 1704 defer tx.Abort() 1705 1706 // Get the table index. 1707 idx := maxIndexTxn(tx, "nodes", "checks") 1708 1709 // Query all checks if HealthAny is passed, otherwise use the index. 1710 var iter memdb.ResultIterator 1711 var err error 1712 if state == api.HealthAny { 1713 iter, err = tx.Get("checks", "status") 1714 if err != nil { 1715 return 0, nil, fmt.Errorf("failed check lookup: %s", err) 1716 } 1717 } else { 1718 iter, err = tx.Get("checks", "status", state) 1719 if err != nil { 1720 return 0, nil, fmt.Errorf("failed check lookup: %s", err) 1721 } 1722 } 1723 ws.Add(iter.WatchCh()) 1724 1725 return s.parseChecksByNodeMeta(tx, ws, idx, iter, filters) 1726 } 1727 1728 // parseChecksByNodeMeta is a helper function used to deduplicate some 1729 // repetitive code for returning health checks filtered by node metadata fields. 1730 func (s *Store) parseChecksByNodeMeta(tx *memdb.Txn, ws memdb.WatchSet, 1731 idx uint64, iter memdb.ResultIterator, filters map[string]string) (uint64, structs.HealthChecks, error) { 1732 1733 // We don't want to track an unlimited number of nodes, so we pull a 1734 // top-level watch to use as a fallback. 1735 allNodes, err := tx.Get("nodes", "id") 1736 if err != nil { 1737 return 0, nil, fmt.Errorf("failed nodes lookup: %s", err) 1738 } 1739 allNodesCh := allNodes.WatchCh() 1740 1741 // Only take results for nodes that satisfy the node metadata filters. 1742 var results structs.HealthChecks 1743 for check := iter.Next(); check != nil; check = iter.Next() { 1744 healthCheck := check.(*structs.HealthCheck) 1745 watchCh, node, err := tx.FirstWatch("nodes", "id", healthCheck.Node) 1746 if err != nil { 1747 return 0, nil, fmt.Errorf("failed node lookup: %s", err) 1748 } 1749 if node == nil { 1750 return 0, nil, ErrMissingNode 1751 } 1752 1753 // Add even the filtered nodes so we wake up if the node metadata 1754 // changes. 1755 ws.AddWithLimit(watchLimit, watchCh, allNodesCh) 1756 if structs.SatisfiesMetaFilters(node.(*structs.Node).Meta, filters) { 1757 results = append(results, healthCheck) 1758 } 1759 } 1760 return idx, results, nil 1761 } 1762 1763 // DeleteCheck is used to delete a health check registration. 1764 func (s *Store) DeleteCheck(idx uint64, node string, checkID types.CheckID) error { 1765 tx := s.db.Txn(true) 1766 defer tx.Abort() 1767 1768 // Call the check deletion 1769 if err := s.deleteCheckTxn(tx, idx, node, checkID); err != nil { 1770 return err 1771 } 1772 1773 tx.Commit() 1774 return nil 1775 } 1776 1777 // deleteCheckCASTxn is used to try doing a check delete operation with a given 1778 // raft index. If the CAS index specified is not equal to the last observed index for 1779 // the given check, then the call is a noop, otherwise a normal check delete is invoked. 1780 func (s *Store) deleteCheckCASTxn(tx *memdb.Txn, idx, cidx uint64, node string, checkID types.CheckID) (bool, error) { 1781 // Try to retrieve the existing health check. 1782 _, hc, err := s.getNodeCheckTxn(tx, node, checkID) 1783 if err != nil { 1784 return false, fmt.Errorf("check lookup failed: %s", err) 1785 } 1786 if hc == nil { 1787 return false, nil 1788 } 1789 1790 // If the existing index does not match the provided CAS 1791 // index arg, then we shouldn't update anything and can safely 1792 // return early here. 1793 if hc.ModifyIndex != cidx { 1794 return false, nil 1795 } 1796 1797 // Call the actual deletion if the above passed. 1798 if err := s.deleteCheckTxn(tx, idx, node, checkID); err != nil { 1799 return false, err 1800 } 1801 1802 return true, nil 1803 } 1804 1805 // deleteCheckTxn is the inner method used to call a health 1806 // check deletion within an existing transaction. 1807 func (s *Store) deleteCheckTxn(tx *memdb.Txn, idx uint64, node string, checkID types.CheckID) error { 1808 // Try to retrieve the existing health check. 1809 hc, err := tx.First("checks", "id", node, string(checkID)) 1810 if err != nil { 1811 return fmt.Errorf("check lookup failed: %s", err) 1812 } 1813 if hc == nil { 1814 return nil 1815 } 1816 existing := hc.(*structs.HealthCheck) 1817 if existing != nil { 1818 // When no service is linked to this service, update all services of node 1819 if existing.ServiceID != "" { 1820 if err = tx.Insert("index", &IndexEntry{serviceIndexName(existing.ServiceName), idx}); err != nil { 1821 return fmt.Errorf("failed updating index: %s", err) 1822 } 1823 } else { 1824 err = s.updateAllServiceIndexesOfNode(tx, idx, existing.Node) 1825 if err != nil { 1826 return fmt.Errorf("Failed to update services linked to deleted healthcheck: %s", err) 1827 } 1828 if err := tx.Insert("index", &IndexEntry{"services", idx}); err != nil { 1829 return fmt.Errorf("failed updating index: %s", err) 1830 } 1831 } 1832 } 1833 1834 // Delete the check from the DB and update the index. 1835 if err := tx.Delete("checks", hc); err != nil { 1836 return fmt.Errorf("failed removing check: %s", err) 1837 } 1838 if err := tx.Insert("index", &IndexEntry{"checks", idx}); err != nil { 1839 return fmt.Errorf("failed updating index: %s", err) 1840 } 1841 1842 // Delete any sessions for this check. 1843 mappings, err := tx.Get("session_checks", "node_check", node, string(checkID)) 1844 if err != nil { 1845 return fmt.Errorf("failed session checks lookup: %s", err) 1846 } 1847 var ids []string 1848 for mapping := mappings.Next(); mapping != nil; mapping = mappings.Next() { 1849 ids = append(ids, mapping.(*sessionCheck).Session) 1850 } 1851 1852 // Do the delete in a separate loop so we don't trash the iterator. 1853 for _, id := range ids { 1854 if err := s.deleteSessionTxn(tx, idx, id); err != nil { 1855 return fmt.Errorf("failed deleting session: %s", err) 1856 } 1857 } 1858 1859 return nil 1860 } 1861 1862 // CheckServiceNodes is used to query all nodes and checks for a given service. 1863 func (s *Store) CheckServiceNodes(ws memdb.WatchSet, serviceName string) (uint64, structs.CheckServiceNodes, error) { 1864 return s.checkServiceNodes(ws, serviceName, false) 1865 } 1866 1867 // CheckConnectServiceNodes is used to query all nodes and checks for Connect 1868 // compatible endpoints for a given service. 1869 func (s *Store) CheckConnectServiceNodes(ws memdb.WatchSet, serviceName string) (uint64, structs.CheckServiceNodes, error) { 1870 return s.checkServiceNodes(ws, serviceName, true) 1871 } 1872 1873 func (s *Store) checkServiceNodes(ws memdb.WatchSet, serviceName string, connect bool) (uint64, structs.CheckServiceNodes, error) { 1874 tx := s.db.Txn(false) 1875 defer tx.Abort() 1876 1877 // Function for lookup 1878 var f func() (memdb.ResultIterator, error) 1879 if !connect { 1880 f = func() (memdb.ResultIterator, error) { 1881 return tx.Get("services", "service", serviceName) 1882 } 1883 } else { 1884 f = func() (memdb.ResultIterator, error) { 1885 return tx.Get("services", "connect", serviceName) 1886 } 1887 } 1888 1889 // Query the state store for the service. 1890 iter, err := f() 1891 if err != nil { 1892 return 0, nil, fmt.Errorf("failed service lookup: %s", err) 1893 } 1894 // Note we decide if we want to watch this iterator or not down below. We need 1895 // to see if it returned anything first. 1896 1897 // Return the results. 1898 var results structs.ServiceNodes 1899 for service := iter.Next(); service != nil; service = iter.Next() { 1900 results = append(results, service.(*structs.ServiceNode)) 1901 } 1902 1903 // Get the table index. 1904 idx, ch := maxIndexAndWatchChForService(tx, serviceName, len(results) > 0, true) 1905 1906 // Create a nil watchset to pass below, we'll only pass the real one if we 1907 // need to. Nil watchers are safe/allowed and saves some allocation too. 1908 var fallbackWS memdb.WatchSet 1909 if ch == nil { 1910 // There was no explicit channel returned that corresponds to the service 1911 // index. That means we need to fallback to watching everything we touch in 1912 // the DB as normal. We plumb the caller's watchset through (note it's a map 1913 // so this is a by-reference assignment.) 1914 fallbackWS = ws 1915 // We also need to watch the iterator from earlier too. 1916 fallbackWS.Add(iter.WatchCh()) 1917 } else { 1918 // There was a valid service index, and non-empty result. In this case it is 1919 // sufficient just to watch the service index's chan since that _must_ be 1920 // written to if the result of this method is going to change. This saves us 1921 // watching potentially thousands of watch chans for large services which 1922 // may need many goroutines. It also avoid the performance cliff that is hit 1923 // when watchLimit is hit (~682 service instances). See 1924 // https://github.com/hashicorp/consul/issues/4984 1925 ws.Add(ch) 1926 } 1927 1928 return s.parseCheckServiceNodes(tx, fallbackWS, idx, serviceName, results, err) 1929 } 1930 1931 // CheckServiceTagNodes is used to query all nodes and checks for a given 1932 // service, filtering out services that don't contain the given tag. 1933 func (s *Store) CheckServiceTagNodes(ws memdb.WatchSet, serviceName string, tags []string) (uint64, structs.CheckServiceNodes, error) { 1934 tx := s.db.Txn(false) 1935 defer tx.Abort() 1936 1937 // Query the state store for the service. 1938 iter, err := tx.Get("services", "service", serviceName) 1939 if err != nil { 1940 return 0, nil, fmt.Errorf("failed service lookup: %s", err) 1941 } 1942 ws.Add(iter.WatchCh()) 1943 1944 // Return the results, filtering by tag. 1945 serviceExists := false 1946 var results structs.ServiceNodes 1947 for service := iter.Next(); service != nil; service = iter.Next() { 1948 svc := service.(*structs.ServiceNode) 1949 serviceExists = true 1950 if !serviceTagsFilter(svc, tags) { 1951 results = append(results, svc) 1952 } 1953 } 1954 1955 // Get the table index. 1956 idx := maxIndexForService(tx, serviceName, serviceExists, true) 1957 return s.parseCheckServiceNodes(tx, ws, idx, serviceName, results, err) 1958 } 1959 1960 // parseCheckServiceNodes is used to parse through a given set of services, 1961 // and query for an associated node and a set of checks. This is the inner 1962 // method used to return a rich set of results from a more simple query. 1963 func (s *Store) parseCheckServiceNodes( 1964 tx *memdb.Txn, ws memdb.WatchSet, idx uint64, 1965 serviceName string, services structs.ServiceNodes, 1966 err error) (uint64, structs.CheckServiceNodes, error) { 1967 if err != nil { 1968 return 0, nil, err 1969 } 1970 1971 // Special-case the zero return value to nil, since this ends up in 1972 // external APIs. 1973 if len(services) == 0 { 1974 return idx, nil, nil 1975 } 1976 1977 // We don't want to track an unlimited number of nodes, so we pull a 1978 // top-level watch to use as a fallback. 1979 allNodes, err := tx.Get("nodes", "id") 1980 if err != nil { 1981 return 0, nil, fmt.Errorf("failed nodes lookup: %s", err) 1982 } 1983 allNodesCh := allNodes.WatchCh() 1984 1985 // We need a similar fallback for checks. Since services need the 1986 // status of node + service-specific checks, we pull in a top-level 1987 // watch over all checks. 1988 allChecks, err := tx.Get("checks", "id") 1989 if err != nil { 1990 return 0, nil, fmt.Errorf("failed checks lookup: %s", err) 1991 } 1992 allChecksCh := allChecks.WatchCh() 1993 1994 results := make(structs.CheckServiceNodes, 0, len(services)) 1995 for _, sn := range services { 1996 // Retrieve the node. 1997 watchCh, n, err := tx.FirstWatch("nodes", "id", sn.Node) 1998 if err != nil { 1999 return 0, nil, fmt.Errorf("failed node lookup: %s", err) 2000 } 2001 ws.AddWithLimit(watchLimit, watchCh, allNodesCh) 2002 2003 if n == nil { 2004 return 0, nil, ErrMissingNode 2005 } 2006 node := n.(*structs.Node) 2007 2008 // First add the node-level checks. These always apply to any 2009 // service on the node. 2010 var checks structs.HealthChecks 2011 iter, err := tx.Get("checks", "node_service_check", sn.Node, false) 2012 if err != nil { 2013 return 0, nil, err 2014 } 2015 ws.AddWithLimit(watchLimit, iter.WatchCh(), allChecksCh) 2016 for check := iter.Next(); check != nil; check = iter.Next() { 2017 checks = append(checks, check.(*structs.HealthCheck)) 2018 } 2019 2020 // Now add the service-specific checks. 2021 iter, err = tx.Get("checks", "node_service", sn.Node, sn.ServiceID) 2022 if err != nil { 2023 return 0, nil, err 2024 } 2025 ws.AddWithLimit(watchLimit, iter.WatchCh(), allChecksCh) 2026 for check := iter.Next(); check != nil; check = iter.Next() { 2027 checks = append(checks, check.(*structs.HealthCheck)) 2028 } 2029 2030 // Append to the results. 2031 results = append(results, structs.CheckServiceNode{ 2032 Node: node, 2033 Service: sn.ToNodeService(), 2034 Checks: checks, 2035 }) 2036 } 2037 2038 return idx, results, nil 2039 } 2040 2041 // NodeInfo is used to generate a dump of a single node. The dump includes 2042 // all services and checks which are registered against the node. 2043 func (s *Store) NodeInfo(ws memdb.WatchSet, node string) (uint64, structs.NodeDump, error) { 2044 tx := s.db.Txn(false) 2045 defer tx.Abort() 2046 2047 // Get the table index. 2048 idx := maxIndexTxn(tx, "nodes", "services", "checks") 2049 2050 // Query the node by the passed node 2051 nodes, err := tx.Get("nodes", "id", node) 2052 if err != nil { 2053 return 0, nil, fmt.Errorf("failed node lookup: %s", err) 2054 } 2055 ws.Add(nodes.WatchCh()) 2056 return s.parseNodes(tx, ws, idx, nodes) 2057 } 2058 2059 // NodeDump is used to generate a dump of all nodes. This call is expensive 2060 // as it has to query every node, service, and check. The response can also 2061 // be quite large since there is currently no filtering applied. 2062 func (s *Store) NodeDump(ws memdb.WatchSet) (uint64, structs.NodeDump, error) { 2063 tx := s.db.Txn(false) 2064 defer tx.Abort() 2065 2066 // Get the table index. 2067 idx := maxIndexTxn(tx, "nodes", "services", "checks") 2068 2069 // Fetch all of the registered nodes 2070 nodes, err := tx.Get("nodes", "id") 2071 if err != nil { 2072 return 0, nil, fmt.Errorf("failed node lookup: %s", err) 2073 } 2074 ws.Add(nodes.WatchCh()) 2075 return s.parseNodes(tx, ws, idx, nodes) 2076 } 2077 2078 // parseNodes takes an iterator over a set of nodes and returns a struct 2079 // containing the nodes along with all of their associated services 2080 // and/or health checks. 2081 func (s *Store) parseNodes(tx *memdb.Txn, ws memdb.WatchSet, idx uint64, 2082 iter memdb.ResultIterator) (uint64, structs.NodeDump, error) { 2083 2084 // We don't want to track an unlimited number of services, so we pull a 2085 // top-level watch to use as a fallback. 2086 allServices, err := tx.Get("services", "id") 2087 if err != nil { 2088 return 0, nil, fmt.Errorf("failed services lookup: %s", err) 2089 } 2090 allServicesCh := allServices.WatchCh() 2091 2092 // We need a similar fallback for checks. 2093 allChecks, err := tx.Get("checks", "id") 2094 if err != nil { 2095 return 0, nil, fmt.Errorf("failed checks lookup: %s", err) 2096 } 2097 allChecksCh := allChecks.WatchCh() 2098 2099 var results structs.NodeDump 2100 for n := iter.Next(); n != nil; n = iter.Next() { 2101 node := n.(*structs.Node) 2102 2103 // Create the wrapped node 2104 dump := &structs.NodeInfo{ 2105 ID: node.ID, 2106 Node: node.Node, 2107 Address: node.Address, 2108 TaggedAddresses: node.TaggedAddresses, 2109 Meta: node.Meta, 2110 } 2111 2112 // Query the node services 2113 services, err := tx.Get("services", "node", node.Node) 2114 if err != nil { 2115 return 0, nil, fmt.Errorf("failed services lookup: %s", err) 2116 } 2117 ws.AddWithLimit(watchLimit, services.WatchCh(), allServicesCh) 2118 for service := services.Next(); service != nil; service = services.Next() { 2119 ns := service.(*structs.ServiceNode).ToNodeService() 2120 dump.Services = append(dump.Services, ns) 2121 } 2122 2123 // Query the node checks 2124 checks, err := tx.Get("checks", "node", node.Node) 2125 if err != nil { 2126 return 0, nil, fmt.Errorf("failed node lookup: %s", err) 2127 } 2128 ws.AddWithLimit(watchLimit, checks.WatchCh(), allChecksCh) 2129 for check := checks.Next(); check != nil; check = checks.Next() { 2130 hc := check.(*structs.HealthCheck) 2131 dump.Checks = append(dump.Checks, hc) 2132 } 2133 2134 // Add the result to the slice 2135 results = append(results, dump) 2136 } 2137 return idx, results, nil 2138 }