github.com/Aestek/consul@v1.2.4-0.20190309222502-b2c31e33971a/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 services, err := tx.Get("services", "node", node.Node) 488 if err != nil { 489 return fmt.Errorf("failed querying services: %s", err) 490 } 491 updatedServices := map[string]struct{}{} 492 for service := services.Next(); service != nil; service = services.Next() { 493 svc := service.(*structs.ServiceNode) 494 if _, ok := updatedServices[svc.ServiceName]; ok { 495 continue 496 } 497 if err := tx.Insert("index", &IndexEntry{serviceIndexName(svc.ServiceName), idx}); err != nil { 498 return fmt.Errorf("failed updating index: %s", err) 499 } 500 updatedServices[svc.ServiceName] = struct{}{} 501 } 502 503 return nil 504 } 505 506 // GetNode is used to retrieve a node registration by node name ID. 507 func (s *Store) GetNode(id string) (uint64, *structs.Node, error) { 508 tx := s.db.Txn(false) 509 defer tx.Abort() 510 511 // Get the table index. 512 idx := maxIndexTxn(tx, "nodes") 513 514 // Retrieve the node from the state store 515 node, err := getNodeTxn(tx, id) 516 if err != nil { 517 return 0, nil, fmt.Errorf("node lookup failed: %s", err) 518 } 519 return idx, node, nil 520 } 521 522 func getNodeTxn(tx *memdb.Txn, nodeName string) (*structs.Node, error) { 523 node, err := tx.First("nodes", "id", nodeName) 524 if err != nil { 525 return nil, fmt.Errorf("node lookup failed: %s", err) 526 } 527 if node != nil { 528 return node.(*structs.Node), nil 529 } 530 return nil, nil 531 } 532 533 func getNodeIDTxn(tx *memdb.Txn, id types.NodeID) (*structs.Node, error) { 534 strnode := string(id) 535 uuidValue, err := uuid.ParseUUID(strnode) 536 if err != nil { 537 return nil, fmt.Errorf("node lookup by ID failed, wrong UUID: %v for '%s'", err, strnode) 538 } 539 540 node, err := tx.First("nodes", "uuid", uuidValue) 541 if err != nil { 542 return nil, fmt.Errorf("node lookup by ID failed: %s", err) 543 } 544 if node != nil { 545 return node.(*structs.Node), nil 546 } 547 return nil, nil 548 } 549 550 // GetNodeID is used to retrieve a node registration by node ID. 551 func (s *Store) GetNodeID(id types.NodeID) (uint64, *structs.Node, error) { 552 tx := s.db.Txn(false) 553 defer tx.Abort() 554 555 // Get the table index. 556 idx := maxIndexTxn(tx, "nodes") 557 558 // Retrieve the node from the state store 559 node, err := getNodeIDTxn(tx, id) 560 return idx, node, err 561 } 562 563 // Nodes is used to return all of the known nodes. 564 func (s *Store) Nodes(ws memdb.WatchSet) (uint64, structs.Nodes, error) { 565 tx := s.db.Txn(false) 566 defer tx.Abort() 567 568 // Get the table index. 569 idx := maxIndexTxn(tx, "nodes") 570 571 // Retrieve all of the nodes 572 nodes, err := tx.Get("nodes", "id") 573 if err != nil { 574 return 0, nil, fmt.Errorf("failed nodes lookup: %s", err) 575 } 576 ws.Add(nodes.WatchCh()) 577 578 // Create and return the nodes list. 579 var results structs.Nodes 580 for node := nodes.Next(); node != nil; node = nodes.Next() { 581 results = append(results, node.(*structs.Node)) 582 } 583 return idx, results, nil 584 } 585 586 // NodesByMeta is used to return all nodes with the given metadata key/value pairs. 587 func (s *Store) NodesByMeta(ws memdb.WatchSet, filters map[string]string) (uint64, structs.Nodes, error) { 588 tx := s.db.Txn(false) 589 defer tx.Abort() 590 591 // Get the table index. 592 idx := maxIndexTxn(tx, "nodes") 593 594 // Retrieve all of the nodes 595 var args []interface{} 596 for key, value := range filters { 597 args = append(args, key, value) 598 break 599 } 600 nodes, err := tx.Get("nodes", "meta", args...) 601 if err != nil { 602 return 0, nil, fmt.Errorf("failed nodes lookup: %s", err) 603 } 604 ws.Add(nodes.WatchCh()) 605 606 // Create and return the nodes list. 607 var results structs.Nodes 608 for node := nodes.Next(); node != nil; node = nodes.Next() { 609 n := node.(*structs.Node) 610 if len(filters) <= 1 || structs.SatisfiesMetaFilters(n.Meta, filters) { 611 results = append(results, n) 612 } 613 } 614 return idx, results, nil 615 } 616 617 // DeleteNode is used to delete a given node by its ID. 618 func (s *Store) DeleteNode(idx uint64, nodeName string) error { 619 tx := s.db.Txn(true) 620 defer tx.Abort() 621 622 // Call the node deletion. 623 if err := s.deleteNodeTxn(tx, idx, nodeName); err != nil { 624 return err 625 } 626 627 tx.Commit() 628 return nil 629 } 630 631 // deleteNodeCASTxn is used to try doing a node delete operation with a given 632 // raft index. If the CAS index specified is not equal to the last observed index for 633 // the given check, then the call is a noop, otherwise a normal check delete is invoked. 634 func (s *Store) deleteNodeCASTxn(tx *memdb.Txn, idx, cidx uint64, nodeName string) (bool, error) { 635 // Look up the node. 636 node, err := getNodeTxn(tx, nodeName) 637 if err != nil { 638 return false, err 639 } 640 if node == nil { 641 return false, nil 642 } 643 644 // If the existing index does not match the provided CAS 645 // index arg, then we shouldn't update anything and can safely 646 // return early here. 647 if node.ModifyIndex != cidx { 648 return false, nil 649 } 650 651 // Call the actual deletion if the above passed. 652 if err := s.deleteNodeTxn(tx, idx, nodeName); err != nil { 653 return false, err 654 } 655 656 return true, nil 657 } 658 659 // deleteNodeTxn is the inner method used for removing a node from 660 // the store within a given transaction. 661 func (s *Store) deleteNodeTxn(tx *memdb.Txn, idx uint64, nodeName string) error { 662 // Look up the node. 663 node, err := tx.First("nodes", "id", nodeName) 664 if err != nil { 665 return fmt.Errorf("node lookup failed: %s", err) 666 } 667 if node == nil { 668 return nil 669 } 670 671 // Delete all services associated with the node and update the service index. 672 services, err := tx.Get("services", "node", nodeName) 673 if err != nil { 674 return fmt.Errorf("failed service lookup: %s", err) 675 } 676 var sids []string 677 for service := services.Next(); service != nil; service = services.Next() { 678 svc := service.(*structs.ServiceNode) 679 sids = append(sids, svc.ServiceID) 680 if err := tx.Insert("index", &IndexEntry{serviceIndexName(svc.ServiceName), idx}); err != nil { 681 return fmt.Errorf("failed updating index: %s", err) 682 } 683 } 684 685 // Do the delete in a separate loop so we don't trash the iterator. 686 for _, sid := range sids { 687 if err := s.deleteServiceTxn(tx, idx, nodeName, sid); err != nil { 688 return err 689 } 690 } 691 692 // Delete all checks associated with the node. This will invalidate 693 // sessions as necessary. 694 checks, err := tx.Get("checks", "node", nodeName) 695 if err != nil { 696 return fmt.Errorf("failed check lookup: %s", err) 697 } 698 var cids []types.CheckID 699 for check := checks.Next(); check != nil; check = checks.Next() { 700 cids = append(cids, check.(*structs.HealthCheck).CheckID) 701 } 702 703 // Do the delete in a separate loop so we don't trash the iterator. 704 for _, cid := range cids { 705 if err := s.deleteCheckTxn(tx, idx, nodeName, cid); err != nil { 706 return err 707 } 708 } 709 710 // Delete any coordinates associated with this node. 711 coords, err := tx.Get("coordinates", "node", nodeName) 712 if err != nil { 713 return fmt.Errorf("failed coordinate lookup: %s", err) 714 } 715 for coord := coords.Next(); coord != nil; coord = coords.Next() { 716 if err := tx.Delete("coordinates", coord); err != nil { 717 return fmt.Errorf("failed deleting coordinate: %s", err) 718 } 719 if err := tx.Insert("index", &IndexEntry{"coordinates", idx}); err != nil { 720 return fmt.Errorf("failed updating index: %s", err) 721 } 722 } 723 724 // Delete the node and update the index. 725 if err := tx.Delete("nodes", node); err != nil { 726 return fmt.Errorf("failed deleting node: %s", err) 727 } 728 if err := tx.Insert("index", &IndexEntry{"nodes", idx}); err != nil { 729 return fmt.Errorf("failed updating index: %s", err) 730 } 731 732 // Invalidate any sessions for this node. 733 sessions, err := tx.Get("sessions", "node", nodeName) 734 if err != nil { 735 return fmt.Errorf("failed session lookup: %s", err) 736 } 737 var ids []string 738 for sess := sessions.Next(); sess != nil; sess = sessions.Next() { 739 ids = append(ids, sess.(*structs.Session).ID) 740 } 741 742 // Do the delete in a separate loop so we don't trash the iterator. 743 for _, id := range ids { 744 if err := s.deleteSessionTxn(tx, idx, id); err != nil { 745 return fmt.Errorf("failed session delete: %s", err) 746 } 747 } 748 749 return nil 750 } 751 752 // EnsureService is called to upsert creation of a given NodeService. 753 func (s *Store) EnsureService(idx uint64, node string, svc *structs.NodeService) error { 754 tx := s.db.Txn(true) 755 defer tx.Abort() 756 757 // Call the service registration upsert 758 if err := s.ensureServiceTxn(tx, idx, node, svc); err != nil { 759 return err 760 } 761 762 tx.Commit() 763 return nil 764 } 765 766 // ensureServiceCASTxn updates a service only if the existing index matches the given index. 767 // Returns a bool indicating if a write happened and any error. 768 func (s *Store) ensureServiceCASTxn(tx *memdb.Txn, idx uint64, node string, svc *structs.NodeService) (bool, error) { 769 // Retrieve the existing service. 770 existing, err := tx.First("services", "id", node, svc.ID) 771 if err != nil { 772 return false, fmt.Errorf("failed service lookup: %s", err) 773 } 774 775 // Check if the we should do the set. A ModifyIndex of 0 means that 776 // we are doing a set-if-not-exists. 777 if svc.ModifyIndex == 0 && existing != nil { 778 return false, nil 779 } 780 if svc.ModifyIndex != 0 && existing == nil { 781 return false, nil 782 } 783 e, ok := existing.(*structs.Node) 784 if ok && svc.ModifyIndex != 0 && svc.ModifyIndex != e.ModifyIndex { 785 return false, nil 786 } 787 788 // Perform the update. 789 if err := s.ensureServiceTxn(tx, idx, node, svc); err != nil { 790 return false, err 791 } 792 793 return true, nil 794 } 795 796 // ensureServiceTxn is used to upsert a service registration within an 797 // existing memdb transaction. 798 func (s *Store) ensureServiceTxn(tx *memdb.Txn, idx uint64, node string, svc *structs.NodeService) error { 799 // Check for existing service 800 existing, err := tx.First("services", "id", node, svc.ID) 801 if err != nil { 802 return fmt.Errorf("failed service lookup: %s", err) 803 } 804 805 if err = structs.ValidateMetadata(svc.Meta, false); err != nil { 806 return fmt.Errorf("Invalid Service Meta for node %s and serviceID %s: %v", node, svc.ID, err) 807 } 808 // Create the service node entry and populate the indexes. Note that 809 // conversion doesn't populate any of the node-specific information. 810 // That's always populated when we read from the state store. 811 entry := svc.ToServiceNode(node) 812 // Get the node 813 n, err := tx.First("nodes", "id", node) 814 if err != nil { 815 return fmt.Errorf("failed node lookup: %s", err) 816 } 817 if n == nil { 818 return ErrMissingNode 819 } 820 if existing != nil { 821 serviceNode := existing.(*structs.ServiceNode) 822 entry.CreateIndex = serviceNode.CreateIndex 823 entry.ModifyIndex = serviceNode.ModifyIndex 824 // We cannot return here because: we want to keep existing behavior (ex: failed node lookup -> ErrMissingNode) 825 // It might be modified in future, but it requires changing many unit tests 826 // Enforcing saving the entry also ensures that if we add default values in .ToServiceNode() 827 // those values will be saved even if node is not really modified for a while. 828 if entry.IsSameService(serviceNode) { 829 return nil 830 } 831 } else { 832 entry.CreateIndex = idx 833 } 834 entry.ModifyIndex = idx 835 836 // Insert the service and update the index 837 if err := tx.Insert("services", entry); err != nil { 838 return fmt.Errorf("failed inserting service: %s", err) 839 } 840 if err := tx.Insert("index", &IndexEntry{"services", idx}); err != nil { 841 return fmt.Errorf("failed updating index: %s", err) 842 } 843 if err := tx.Insert("index", &IndexEntry{serviceIndexName(svc.Service), idx}); err != nil { 844 return fmt.Errorf("failed updating index: %s", err) 845 } 846 847 return nil 848 } 849 850 // Services returns all services along with a list of associated tags. 851 func (s *Store) Services(ws memdb.WatchSet) (uint64, structs.Services, error) { 852 tx := s.db.Txn(false) 853 defer tx.Abort() 854 855 // Get the table index. 856 idx := maxIndexTxn(tx, "services") 857 858 // List all the services. 859 services, err := tx.Get("services", "id") 860 if err != nil { 861 return 0, nil, fmt.Errorf("failed querying services: %s", err) 862 } 863 ws.Add(services.WatchCh()) 864 865 // Rip through the services and enumerate them and their unique set of 866 // tags. 867 unique := make(map[string]map[string]struct{}) 868 for service := services.Next(); service != nil; service = services.Next() { 869 svc := service.(*structs.ServiceNode) 870 tags, ok := unique[svc.ServiceName] 871 if !ok { 872 unique[svc.ServiceName] = make(map[string]struct{}) 873 tags = unique[svc.ServiceName] 874 } 875 for _, tag := range svc.ServiceTags { 876 tags[tag] = struct{}{} 877 } 878 } 879 880 // Generate the output structure. 881 var results = make(structs.Services) 882 for service, tags := range unique { 883 results[service] = make([]string, 0) 884 for tag := range tags { 885 results[service] = append(results[service], tag) 886 } 887 } 888 return idx, results, nil 889 } 890 891 // ServicesByNodeMeta returns all services, filtered by the given node metadata. 892 func (s *Store) ServicesByNodeMeta(ws memdb.WatchSet, filters map[string]string) (uint64, structs.Services, error) { 893 tx := s.db.Txn(false) 894 defer tx.Abort() 895 896 // Get the table index. 897 idx := maxIndexTxn(tx, "services", "nodes") 898 899 // Retrieve all of the nodes with the meta k/v pair 900 var args []interface{} 901 for key, value := range filters { 902 args = append(args, key, value) 903 break 904 } 905 nodes, err := tx.Get("nodes", "meta", args...) 906 if err != nil { 907 return 0, nil, fmt.Errorf("failed nodes lookup: %s", err) 908 } 909 ws.Add(nodes.WatchCh()) 910 911 // We don't want to track an unlimited number of services, so we pull a 912 // top-level watch to use as a fallback. 913 allServices, err := tx.Get("services", "id") 914 if err != nil { 915 return 0, nil, fmt.Errorf("failed services lookup: %s", err) 916 } 917 allServicesCh := allServices.WatchCh() 918 919 // Populate the services map 920 unique := make(map[string]map[string]struct{}) 921 for node := nodes.Next(); node != nil; node = nodes.Next() { 922 n := node.(*structs.Node) 923 if len(filters) > 1 && !structs.SatisfiesMetaFilters(n.Meta, filters) { 924 continue 925 } 926 927 // List all the services on the node 928 services, err := tx.Get("services", "node", n.Node) 929 if err != nil { 930 return 0, nil, fmt.Errorf("failed querying services: %s", err) 931 } 932 ws.AddWithLimit(watchLimit, services.WatchCh(), allServicesCh) 933 934 // Rip through the services and enumerate them and their unique set of 935 // tags. 936 for service := services.Next(); service != nil; service = services.Next() { 937 svc := service.(*structs.ServiceNode) 938 tags, ok := unique[svc.ServiceName] 939 if !ok { 940 unique[svc.ServiceName] = make(map[string]struct{}) 941 tags = unique[svc.ServiceName] 942 } 943 for _, tag := range svc.ServiceTags { 944 tags[tag] = struct{}{} 945 } 946 } 947 } 948 949 // Generate the output structure. 950 var results = make(structs.Services) 951 for service, tags := range unique { 952 results[service] = make([]string, 0) 953 for tag := range tags { 954 results[service] = append(results[service], tag) 955 } 956 } 957 return idx, results, nil 958 } 959 960 // maxIndexForService return the maximum Raft Index for a service 961 // If the index is not set for the service, it will return the missing 962 // service index. 963 // The service_last_extinction is set to the last raft index when a service 964 // was unregistered (or 0 if no services were ever unregistered). This 965 // allows blocking queries to 966 // * return when the last instance of a service is removed 967 // * block until an instance for this service is available, or another 968 // service is unregistered. 969 func maxIndexForService(tx *memdb.Txn, serviceName string, serviceExists, checks bool) uint64 { 970 if !serviceExists { 971 res, err := tx.First("index", "id", serviceLastExtinctionIndexName) 972 if missingIdx, ok := res.(*IndexEntry); ok && err == nil { 973 return missingIdx.Value 974 } 975 } 976 977 res, err := tx.First("index", "id", serviceIndexName(serviceName)) 978 if idx, ok := res.(*IndexEntry); ok && err == nil { 979 return idx.Value 980 } 981 if checks { 982 return maxIndexTxn(tx, "nodes", "services", "checks") 983 } 984 985 return maxIndexTxn(tx, "nodes", "services") 986 } 987 988 // ConnectServiceNodes returns the nodes associated with a Connect 989 // compatible destination for the given service name. This will include 990 // both proxies and native integrations. 991 func (s *Store) ConnectServiceNodes(ws memdb.WatchSet, serviceName string) (uint64, structs.ServiceNodes, error) { 992 return s.serviceNodes(ws, serviceName, true) 993 } 994 995 // ServiceNodes returns the nodes associated with a given service name. 996 func (s *Store) ServiceNodes(ws memdb.WatchSet, serviceName string) (uint64, structs.ServiceNodes, error) { 997 return s.serviceNodes(ws, serviceName, false) 998 } 999 1000 func (s *Store) serviceNodes(ws memdb.WatchSet, serviceName string, connect bool) (uint64, structs.ServiceNodes, error) { 1001 tx := s.db.Txn(false) 1002 defer tx.Abort() 1003 1004 // Function for lookup 1005 var f func() (memdb.ResultIterator, error) 1006 if !connect { 1007 f = func() (memdb.ResultIterator, error) { 1008 return tx.Get("services", "service", serviceName) 1009 } 1010 } else { 1011 f = func() (memdb.ResultIterator, error) { 1012 return tx.Get("services", "connect", serviceName) 1013 } 1014 } 1015 1016 // List all the services. 1017 services, err := f() 1018 if err != nil { 1019 return 0, nil, fmt.Errorf("failed service lookup: %s", err) 1020 } 1021 ws.Add(services.WatchCh()) 1022 1023 var results structs.ServiceNodes 1024 for service := services.Next(); service != nil; service = services.Next() { 1025 results = append(results, service.(*structs.ServiceNode)) 1026 } 1027 1028 // Fill in the node details. 1029 results, err = s.parseServiceNodes(tx, ws, results) 1030 if err != nil { 1031 return 0, nil, fmt.Errorf("failed parsing service nodes: %s", err) 1032 } 1033 1034 // Get the table index. 1035 idx := maxIndexForService(tx, serviceName, len(results) > 0, false) 1036 1037 return idx, results, nil 1038 } 1039 1040 // ServiceTagNodes returns the nodes associated with a given service, filtering 1041 // out services that don't contain the given tags. 1042 func (s *Store) ServiceTagNodes(ws memdb.WatchSet, service string, tags []string) (uint64, structs.ServiceNodes, error) { 1043 tx := s.db.Txn(false) 1044 defer tx.Abort() 1045 1046 // List all the services. 1047 services, err := tx.Get("services", "service", service) 1048 if err != nil { 1049 return 0, nil, fmt.Errorf("failed service lookup: %s", err) 1050 } 1051 ws.Add(services.WatchCh()) 1052 1053 // Gather all the services and apply the tag filter. 1054 serviceExists := false 1055 var results structs.ServiceNodes 1056 for service := services.Next(); service != nil; service = services.Next() { 1057 svc := service.(*structs.ServiceNode) 1058 serviceExists = true 1059 if !serviceTagsFilter(svc, tags) { 1060 results = append(results, svc) 1061 } 1062 } 1063 1064 // Fill in the node details. 1065 results, err = s.parseServiceNodes(tx, ws, results) 1066 if err != nil { 1067 return 0, nil, fmt.Errorf("failed parsing service nodes: %s", err) 1068 } 1069 // Get the table index. 1070 idx := maxIndexForService(tx, service, serviceExists, false) 1071 1072 return idx, results, nil 1073 } 1074 1075 // serviceTagFilter returns true (should filter) if the given service node 1076 // doesn't contain the given tag. 1077 func serviceTagFilter(sn *structs.ServiceNode, tag string) bool { 1078 tag = strings.ToLower(tag) 1079 1080 // Look for the lower cased version of the tag. 1081 for _, t := range sn.ServiceTags { 1082 if strings.ToLower(t) == tag { 1083 return false 1084 } 1085 } 1086 1087 // If we didn't hit the tag above then we should filter. 1088 return true 1089 } 1090 1091 // serviceTagsFilter returns true (should filter) if the given service node 1092 // doesn't contain the given set of tags. 1093 func serviceTagsFilter(sn *structs.ServiceNode, tags []string) bool { 1094 for _, tag := range tags { 1095 if serviceTagFilter(sn, tag) { 1096 // If any one of the expected tags was not found, filter the service 1097 return true 1098 } 1099 } 1100 1101 // If all tags were found, don't filter the service 1102 return false 1103 } 1104 1105 // ServiceAddressNodes returns the nodes associated with a given service, filtering 1106 // out services that don't match the given serviceAddress 1107 func (s *Store) ServiceAddressNodes(ws memdb.WatchSet, address string) (uint64, structs.ServiceNodes, error) { 1108 tx := s.db.Txn(false) 1109 defer tx.Abort() 1110 1111 // List all the services. 1112 services, err := tx.Get("services", "id") 1113 if err != nil { 1114 return 0, nil, fmt.Errorf("failed service lookup: %s", err) 1115 } 1116 ws.Add(services.WatchCh()) 1117 1118 // Gather all the services and apply the tag filter. 1119 var results structs.ServiceNodes 1120 for service := services.Next(); service != nil; service = services.Next() { 1121 svc := service.(*structs.ServiceNode) 1122 if svc.ServiceAddress == address { 1123 results = append(results, svc) 1124 } 1125 } 1126 1127 // Fill in the node details. 1128 results, err = s.parseServiceNodes(tx, ws, results) 1129 if err != nil { 1130 return 0, nil, fmt.Errorf("failed parsing service nodes: %s", err) 1131 } 1132 return 0, results, nil 1133 } 1134 1135 // parseServiceNodes iterates over a services query and fills in the node details, 1136 // returning a ServiceNodes slice. 1137 func (s *Store) parseServiceNodes(tx *memdb.Txn, ws memdb.WatchSet, services structs.ServiceNodes) (structs.ServiceNodes, error) { 1138 // We don't want to track an unlimited number of nodes, so we pull a 1139 // top-level watch to use as a fallback. 1140 allNodes, err := tx.Get("nodes", "id") 1141 if err != nil { 1142 return nil, fmt.Errorf("failed nodes lookup: %s", err) 1143 } 1144 allNodesCh := allNodes.WatchCh() 1145 1146 // Fill in the node data for each service instance. 1147 var results structs.ServiceNodes 1148 for _, sn := range services { 1149 // Note that we have to clone here because we don't want to 1150 // modify the node-related fields on the object in the database, 1151 // which is what we are referencing. 1152 s := sn.PartialClone() 1153 1154 // Grab the corresponding node record. 1155 watchCh, n, err := tx.FirstWatch("nodes", "id", sn.Node) 1156 if err != nil { 1157 return nil, fmt.Errorf("failed node lookup: %s", err) 1158 } 1159 ws.AddWithLimit(watchLimit, watchCh, allNodesCh) 1160 1161 // Populate the node-related fields. The tagged addresses may be 1162 // used by agents to perform address translation if they are 1163 // configured to do that. 1164 node := n.(*structs.Node) 1165 s.ID = node.ID 1166 s.Address = node.Address 1167 s.Datacenter = node.Datacenter 1168 s.TaggedAddresses = node.TaggedAddresses 1169 s.NodeMeta = node.Meta 1170 1171 results = append(results, s) 1172 } 1173 return results, nil 1174 } 1175 1176 // NodeService is used to retrieve a specific service associated with the given 1177 // node. 1178 func (s *Store) NodeService(nodeName string, serviceID string) (uint64, *structs.NodeService, error) { 1179 tx := s.db.Txn(false) 1180 defer tx.Abort() 1181 1182 // Get the table index. 1183 idx := maxIndexTxn(tx, "services") 1184 1185 // Query the service 1186 service, err := s.getNodeServiceTxn(tx, nodeName, serviceID) 1187 if err != nil { 1188 return 0, nil, fmt.Errorf("failed querying service for node %q: %s", nodeName, err) 1189 } 1190 1191 return idx, service, nil 1192 } 1193 1194 func (s *Store) getNodeServiceTxn(tx *memdb.Txn, nodeName, serviceID string) (*structs.NodeService, error) { 1195 // Query the service 1196 service, err := tx.First("services", "id", nodeName, serviceID) 1197 if err != nil { 1198 return nil, fmt.Errorf("failed querying service for node %q: %s", nodeName, err) 1199 } 1200 1201 if service != nil { 1202 return service.(*structs.ServiceNode).ToNodeService(), nil 1203 } 1204 1205 return nil, nil 1206 } 1207 1208 // NodeServices is used to query service registrations by node name or UUID. 1209 func (s *Store) NodeServices(ws memdb.WatchSet, nodeNameOrID string) (uint64, *structs.NodeServices, error) { 1210 tx := s.db.Txn(false) 1211 defer tx.Abort() 1212 1213 // Get the table index. 1214 idx := maxIndexTxn(tx, "nodes", "services") 1215 1216 // Query the node by node name 1217 watchCh, n, err := tx.FirstWatch("nodes", "id", nodeNameOrID) 1218 if err != nil { 1219 return 0, nil, fmt.Errorf("node lookup failed: %s", err) 1220 } 1221 1222 if n != nil { 1223 ws.Add(watchCh) 1224 } else { 1225 if len(nodeNameOrID) < minUUIDLookupLen { 1226 ws.Add(watchCh) 1227 return 0, nil, nil 1228 } 1229 1230 // Attempt to lookup the node by its node ID 1231 iter, err := tx.Get("nodes", "uuid_prefix", resizeNodeLookupKey(nodeNameOrID)) 1232 if err != nil { 1233 ws.Add(watchCh) 1234 // TODO(sean@): We could/should log an error re: the uuid_prefix lookup 1235 // failing once a logger has been introduced to the catalog. 1236 return 0, nil, nil 1237 } 1238 1239 n = iter.Next() 1240 if n == nil { 1241 // No nodes matched, even with the Node ID: add a watch on the node name. 1242 ws.Add(watchCh) 1243 return 0, nil, nil 1244 } 1245 1246 idWatchCh := iter.WatchCh() 1247 if iter.Next() != nil { 1248 // More than one match present: Watch on the node name channel and return 1249 // an empty result (node lookups can not be ambiguous). 1250 ws.Add(watchCh) 1251 return 0, nil, nil 1252 } 1253 1254 ws.Add(idWatchCh) 1255 } 1256 1257 node := n.(*structs.Node) 1258 nodeName := node.Node 1259 1260 // Read all of the services 1261 services, err := tx.Get("services", "node", nodeName) 1262 if err != nil { 1263 return 0, nil, fmt.Errorf("failed querying services for node %q: %s", nodeName, err) 1264 } 1265 ws.Add(services.WatchCh()) 1266 1267 // Initialize the node services struct 1268 ns := &structs.NodeServices{ 1269 Node: node, 1270 Services: make(map[string]*structs.NodeService), 1271 } 1272 1273 // Add all of the services to the map. 1274 for service := services.Next(); service != nil; service = services.Next() { 1275 svc := service.(*structs.ServiceNode).ToNodeService() 1276 ns.Services[svc.ID] = svc 1277 } 1278 1279 return idx, ns, nil 1280 } 1281 1282 // DeleteService is used to delete a given service associated with a node. 1283 func (s *Store) DeleteService(idx uint64, nodeName, serviceID string) error { 1284 tx := s.db.Txn(true) 1285 defer tx.Abort() 1286 1287 // Call the service deletion 1288 if err := s.deleteServiceTxn(tx, idx, nodeName, serviceID); err != nil { 1289 return err 1290 } 1291 1292 tx.Commit() 1293 return nil 1294 } 1295 1296 func serviceIndexName(name string) string { 1297 return fmt.Sprintf("service.%s", name) 1298 } 1299 1300 // deleteServiceCASTxn is used to try doing a service delete operation with a given 1301 // raft index. If the CAS index specified is not equal to the last observed index for 1302 // the given service, then the call is a noop, otherwise a normal delete is invoked. 1303 func (s *Store) deleteServiceCASTxn(tx *memdb.Txn, idx, cidx uint64, nodeName, serviceID string) (bool, error) { 1304 // Look up the service. 1305 service, err := s.getNodeServiceTxn(tx, nodeName, serviceID) 1306 if err != nil { 1307 return false, fmt.Errorf("service lookup failed: %s", err) 1308 } 1309 if service == nil { 1310 return false, nil 1311 } 1312 1313 // If the existing index does not match the provided CAS 1314 // index arg, then we shouldn't update anything and can safely 1315 // return early here. 1316 if service.ModifyIndex != cidx { 1317 return false, nil 1318 } 1319 1320 // Call the actual deletion if the above passed. 1321 if err := s.deleteServiceTxn(tx, idx, nodeName, serviceID); err != nil { 1322 return false, err 1323 } 1324 1325 return true, nil 1326 } 1327 1328 // deleteServiceTxn is the inner method called to remove a service 1329 // registration within an existing transaction. 1330 func (s *Store) deleteServiceTxn(tx *memdb.Txn, idx uint64, nodeName, serviceID string) error { 1331 // Look up the service. 1332 service, err := tx.First("services", "id", nodeName, serviceID) 1333 if err != nil { 1334 return fmt.Errorf("failed service lookup: %s", err) 1335 } 1336 if service == nil { 1337 return nil 1338 } 1339 1340 // Delete any checks associated with the service. This will invalidate 1341 // sessions as necessary. 1342 checks, err := tx.Get("checks", "node_service", nodeName, serviceID) 1343 if err != nil { 1344 return fmt.Errorf("failed service check lookup: %s", err) 1345 } 1346 var cids []types.CheckID 1347 for check := checks.Next(); check != nil; check = checks.Next() { 1348 cids = append(cids, check.(*structs.HealthCheck).CheckID) 1349 } 1350 1351 // Do the delete in a separate loop so we don't trash the iterator. 1352 for _, cid := range cids { 1353 if err := s.deleteCheckTxn(tx, idx, nodeName, cid); err != nil { 1354 return err 1355 } 1356 } 1357 1358 // Update the index. 1359 if err := tx.Insert("index", &IndexEntry{"checks", idx}); err != nil { 1360 return fmt.Errorf("failed updating index: %s", err) 1361 } 1362 1363 // Delete the service and update the index 1364 if err := tx.Delete("services", service); err != nil { 1365 return fmt.Errorf("failed deleting service: %s", err) 1366 } 1367 if err := tx.Insert("index", &IndexEntry{"services", idx}); err != nil { 1368 return fmt.Errorf("failed updating index: %s", err) 1369 } 1370 1371 svc := service.(*structs.ServiceNode) 1372 if remainingService, err := tx.First("services", "service", svc.ServiceName); err == nil { 1373 if remainingService != nil { 1374 // We have at least one remaining service, update the index 1375 if err := tx.Insert("index", &IndexEntry{serviceIndexName(svc.ServiceName), idx}); err != nil { 1376 return fmt.Errorf("failed updating index: %s", err) 1377 } 1378 } else { 1379 // There are no more service instances, cleanup the service.<serviceName> index 1380 serviceIndex, err := tx.First("index", "id", serviceIndexName(svc.ServiceName)) 1381 if err == nil && serviceIndex != nil { 1382 // we found service.<serviceName> index, garbage collect it 1383 if errW := tx.Delete("index", serviceIndex); errW != nil { 1384 return fmt.Errorf("[FAILED] deleting serviceIndex %s: %s", svc.ServiceName, err) 1385 } 1386 } 1387 1388 if err := tx.Insert("index", &IndexEntry{serviceLastExtinctionIndexName, idx}); err != nil { 1389 return fmt.Errorf("failed updating missing service index: %s", err) 1390 } 1391 1392 } 1393 } else { 1394 return fmt.Errorf("Could not find any service %s: %s", svc.ServiceName, err) 1395 } 1396 return nil 1397 } 1398 1399 // EnsureCheck is used to store a check registration in the db. 1400 func (s *Store) EnsureCheck(idx uint64, hc *structs.HealthCheck) error { 1401 tx := s.db.Txn(true) 1402 defer tx.Abort() 1403 1404 // Call the check registration 1405 if err := s.ensureCheckTxn(tx, idx, hc); err != nil { 1406 return err 1407 } 1408 1409 tx.Commit() 1410 return nil 1411 } 1412 1413 // updateAllServiceIndexesOfNode updates the Raft index of all the services associated with this node 1414 func (s *Store) updateAllServiceIndexesOfNode(tx *memdb.Txn, idx uint64, nodeID string) error { 1415 services, err := tx.Get("services", "node", nodeID) 1416 if err != nil { 1417 return fmt.Errorf("failed updating services for node %s: %s", nodeID, err) 1418 } 1419 for service := services.Next(); service != nil; service = services.Next() { 1420 svc := service.(*structs.ServiceNode).ToNodeService() 1421 if err := tx.Insert("index", &IndexEntry{serviceIndexName(svc.Service), idx}); err != nil { 1422 return fmt.Errorf("failed updating index: %s", err) 1423 } 1424 } 1425 return nil 1426 } 1427 1428 // ensureCheckCASTxn updates a check only if the existing index matches the given index. 1429 // Returns a bool indicating if a write happened and any error. 1430 func (s *Store) ensureCheckCASTxn(tx *memdb.Txn, idx uint64, hc *structs.HealthCheck) (bool, error) { 1431 // Retrieve the existing entry. 1432 _, existing, err := s.getNodeCheckTxn(tx, hc.Node, hc.CheckID) 1433 if err != nil { 1434 return false, fmt.Errorf("failed health check lookup: %s", err) 1435 } 1436 1437 // Check if the we should do the set. A ModifyIndex of 0 means that 1438 // we are doing a set-if-not-exists. 1439 if hc.ModifyIndex == 0 && existing != nil { 1440 return false, nil 1441 } 1442 if hc.ModifyIndex != 0 && existing == nil { 1443 return false, nil 1444 } 1445 if existing != nil && hc.ModifyIndex != 0 && hc.ModifyIndex != existing.ModifyIndex { 1446 return false, nil 1447 } 1448 1449 // Perform the update. 1450 if err := s.ensureCheckTxn(tx, idx, hc); err != nil { 1451 return false, err 1452 } 1453 1454 return true, nil 1455 } 1456 1457 // ensureCheckTransaction is used as the inner method to handle inserting 1458 // a health check into the state store. It ensures safety against inserting 1459 // checks with no matching node or service. 1460 func (s *Store) ensureCheckTxn(tx *memdb.Txn, idx uint64, hc *structs.HealthCheck) error { 1461 // Check if we have an existing health check 1462 existing, err := tx.First("checks", "id", hc.Node, string(hc.CheckID)) 1463 if err != nil { 1464 return fmt.Errorf("failed health check lookup: %s", err) 1465 } 1466 1467 // Set the indexes 1468 if existing != nil { 1469 existingCheck := existing.(*structs.HealthCheck) 1470 hc.CreateIndex = existingCheck.CreateIndex 1471 hc.ModifyIndex = existingCheck.ModifyIndex 1472 } else { 1473 hc.CreateIndex = idx 1474 hc.ModifyIndex = idx 1475 } 1476 1477 // Use the default check status if none was provided 1478 if hc.Status == "" { 1479 hc.Status = api.HealthCritical 1480 } 1481 1482 // Get the node 1483 node, err := tx.First("nodes", "id", hc.Node) 1484 if err != nil { 1485 return fmt.Errorf("failed node lookup: %s", err) 1486 } 1487 if node == nil { 1488 return ErrMissingNode 1489 } 1490 1491 modified := true 1492 // If the check is associated with a service, check that we have 1493 // a registration for the service. 1494 if hc.ServiceID != "" { 1495 service, err := tx.First("services", "id", hc.Node, hc.ServiceID) 1496 if err != nil { 1497 return fmt.Errorf("failed service lookup: %s", err) 1498 } 1499 if service == nil { 1500 return ErrMissingService 1501 } 1502 1503 // Copy in the service name and tags 1504 svc := service.(*structs.ServiceNode) 1505 hc.ServiceName = svc.ServiceName 1506 hc.ServiceTags = svc.ServiceTags 1507 if existing != nil && existing.(*structs.HealthCheck).IsSame(hc) { 1508 modified = false 1509 } else { 1510 // Check has been modified, we trigger a index service change 1511 if err = tx.Insert("index", &IndexEntry{serviceIndexName(svc.ServiceName), idx}); err != nil { 1512 return fmt.Errorf("failed updating index: %s", err) 1513 } 1514 } 1515 } else { 1516 if existing != nil && existing.(*structs.HealthCheck).IsSame(hc) { 1517 modified = false 1518 } else { 1519 // Since the check has been modified, it impacts all services of node 1520 // Update the status for all the services associated with this node 1521 err = s.updateAllServiceIndexesOfNode(tx, idx, hc.Node) 1522 if err != nil { 1523 return err 1524 } 1525 } 1526 } 1527 1528 // Delete any sessions for this check if the health is critical. 1529 if hc.Status == api.HealthCritical { 1530 mappings, err := tx.Get("session_checks", "node_check", hc.Node, string(hc.CheckID)) 1531 if err != nil { 1532 return fmt.Errorf("failed session checks lookup: %s", err) 1533 } 1534 1535 var ids []string 1536 for mapping := mappings.Next(); mapping != nil; mapping = mappings.Next() { 1537 ids = append(ids, mapping.(*sessionCheck).Session) 1538 } 1539 1540 // Delete the session in a separate loop so we don't trash the 1541 // iterator. 1542 for _, id := range ids { 1543 if err := s.deleteSessionTxn(tx, idx, id); err != nil { 1544 return fmt.Errorf("failed deleting session: %s", err) 1545 } 1546 } 1547 } 1548 if modified { 1549 // We update the modify index, ONLY if something has changed, thus 1550 // With constant output, no change is seen when watching a service 1551 // With huge number of nodes where anti-entropy updates continuously 1552 // the checks, but not the values within the check 1553 hc.ModifyIndex = idx 1554 } 1555 1556 // Persist the check registration in the db. 1557 if err := tx.Insert("checks", hc); err != nil { 1558 return fmt.Errorf("failed inserting check: %s", err) 1559 } 1560 if err := tx.Insert("index", &IndexEntry{"checks", idx}); err != nil { 1561 return fmt.Errorf("failed updating index: %s", err) 1562 } 1563 1564 return nil 1565 } 1566 1567 // NodeCheck is used to retrieve a specific check associated with the given 1568 // node. 1569 func (s *Store) NodeCheck(nodeName string, checkID types.CheckID) (uint64, *structs.HealthCheck, error) { 1570 tx := s.db.Txn(false) 1571 defer tx.Abort() 1572 1573 return s.getNodeCheckTxn(tx, nodeName, checkID) 1574 } 1575 1576 // nodeCheckTxn is used as the inner method to handle reading a health check 1577 // from the state store. 1578 func (s *Store) getNodeCheckTxn(tx *memdb.Txn, nodeName string, checkID types.CheckID) (uint64, *structs.HealthCheck, error) { 1579 // Get the table index. 1580 idx := maxIndexTxn(tx, "checks") 1581 1582 // Return the check. 1583 check, err := tx.First("checks", "id", nodeName, string(checkID)) 1584 if err != nil { 1585 return 0, nil, fmt.Errorf("failed check lookup: %s", err) 1586 } 1587 1588 if check != nil { 1589 return idx, check.(*structs.HealthCheck), nil 1590 } 1591 return idx, nil, nil 1592 } 1593 1594 // NodeChecks is used to retrieve checks associated with the 1595 // given node from the state store. 1596 func (s *Store) NodeChecks(ws memdb.WatchSet, nodeName string) (uint64, structs.HealthChecks, error) { 1597 tx := s.db.Txn(false) 1598 defer tx.Abort() 1599 1600 // Get the table index. 1601 idx := maxIndexTxn(tx, "checks") 1602 1603 // Return the checks. 1604 iter, err := tx.Get("checks", "node", nodeName) 1605 if err != nil { 1606 return 0, nil, fmt.Errorf("failed check lookup: %s", err) 1607 } 1608 ws.Add(iter.WatchCh()) 1609 1610 var results structs.HealthChecks 1611 for check := iter.Next(); check != nil; check = iter.Next() { 1612 results = append(results, check.(*structs.HealthCheck)) 1613 } 1614 return idx, results, nil 1615 } 1616 1617 // ServiceChecks is used to get all checks associated with a 1618 // given service ID. The query is performed against a service 1619 // _name_ instead of a service ID. 1620 func (s *Store) ServiceChecks(ws memdb.WatchSet, serviceName string) (uint64, structs.HealthChecks, error) { 1621 tx := s.db.Txn(false) 1622 defer tx.Abort() 1623 1624 // Get the table index. 1625 idx := maxIndexTxn(tx, "checks") 1626 1627 // Return the checks. 1628 iter, err := tx.Get("checks", "service", serviceName) 1629 if err != nil { 1630 return 0, nil, fmt.Errorf("failed check lookup: %s", err) 1631 } 1632 ws.Add(iter.WatchCh()) 1633 1634 var results structs.HealthChecks 1635 for check := iter.Next(); check != nil; check = iter.Next() { 1636 results = append(results, check.(*structs.HealthCheck)) 1637 } 1638 return idx, results, nil 1639 } 1640 1641 // ServiceChecksByNodeMeta is used to get all checks associated with a 1642 // given service ID, filtered by the given node metadata values. The query 1643 // is performed against a service _name_ instead of a service ID. 1644 func (s *Store) ServiceChecksByNodeMeta(ws memdb.WatchSet, serviceName string, 1645 filters map[string]string) (uint64, structs.HealthChecks, error) { 1646 1647 tx := s.db.Txn(false) 1648 defer tx.Abort() 1649 1650 // Get the table index. 1651 idx := maxIndexForService(tx, serviceName, true, true) 1652 // Return the checks. 1653 iter, err := tx.Get("checks", "service", serviceName) 1654 if err != nil { 1655 return 0, nil, fmt.Errorf("failed check lookup: %s", err) 1656 } 1657 ws.Add(iter.WatchCh()) 1658 1659 return s.parseChecksByNodeMeta(tx, ws, idx, iter, filters) 1660 } 1661 1662 // ChecksInState is used to query the state store for all checks 1663 // which are in the provided state. 1664 func (s *Store) ChecksInState(ws memdb.WatchSet, state string) (uint64, structs.HealthChecks, error) { 1665 tx := s.db.Txn(false) 1666 defer tx.Abort() 1667 1668 // Get the table index. 1669 idx := maxIndexTxn(tx, "checks") 1670 1671 // Query all checks if HealthAny is passed, otherwise use the index. 1672 var iter memdb.ResultIterator 1673 var err error 1674 if state == api.HealthAny { 1675 iter, err = tx.Get("checks", "status") 1676 } else { 1677 iter, err = tx.Get("checks", "status", state) 1678 } 1679 if err != nil { 1680 return 0, nil, fmt.Errorf("failed check lookup: %s", err) 1681 } 1682 ws.Add(iter.WatchCh()) 1683 1684 var results structs.HealthChecks 1685 for check := iter.Next(); check != nil; check = iter.Next() { 1686 results = append(results, check.(*structs.HealthCheck)) 1687 } 1688 return idx, results, nil 1689 } 1690 1691 // ChecksInStateByNodeMeta is used to query the state store for all checks 1692 // which are in the provided state, filtered by the given node metadata values. 1693 func (s *Store) ChecksInStateByNodeMeta(ws memdb.WatchSet, state string, filters map[string]string) (uint64, structs.HealthChecks, error) { 1694 tx := s.db.Txn(false) 1695 defer tx.Abort() 1696 1697 // Get the table index. 1698 idx := maxIndexTxn(tx, "nodes", "checks") 1699 1700 // Query all checks if HealthAny is passed, otherwise use the index. 1701 var iter memdb.ResultIterator 1702 var err error 1703 if state == api.HealthAny { 1704 iter, err = tx.Get("checks", "status") 1705 if err != nil { 1706 return 0, nil, fmt.Errorf("failed check lookup: %s", err) 1707 } 1708 } else { 1709 iter, err = tx.Get("checks", "status", state) 1710 if err != nil { 1711 return 0, nil, fmt.Errorf("failed check lookup: %s", err) 1712 } 1713 } 1714 ws.Add(iter.WatchCh()) 1715 1716 return s.parseChecksByNodeMeta(tx, ws, idx, iter, filters) 1717 } 1718 1719 // parseChecksByNodeMeta is a helper function used to deduplicate some 1720 // repetitive code for returning health checks filtered by node metadata fields. 1721 func (s *Store) parseChecksByNodeMeta(tx *memdb.Txn, ws memdb.WatchSet, 1722 idx uint64, iter memdb.ResultIterator, filters map[string]string) (uint64, structs.HealthChecks, error) { 1723 1724 // We don't want to track an unlimited number of nodes, so we pull a 1725 // top-level watch to use as a fallback. 1726 allNodes, err := tx.Get("nodes", "id") 1727 if err != nil { 1728 return 0, nil, fmt.Errorf("failed nodes lookup: %s", err) 1729 } 1730 allNodesCh := allNodes.WatchCh() 1731 1732 // Only take results for nodes that satisfy the node metadata filters. 1733 var results structs.HealthChecks 1734 for check := iter.Next(); check != nil; check = iter.Next() { 1735 healthCheck := check.(*structs.HealthCheck) 1736 watchCh, node, err := tx.FirstWatch("nodes", "id", healthCheck.Node) 1737 if err != nil { 1738 return 0, nil, fmt.Errorf("failed node lookup: %s", err) 1739 } 1740 if node == nil { 1741 return 0, nil, ErrMissingNode 1742 } 1743 1744 // Add even the filtered nodes so we wake up if the node metadata 1745 // changes. 1746 ws.AddWithLimit(watchLimit, watchCh, allNodesCh) 1747 if structs.SatisfiesMetaFilters(node.(*structs.Node).Meta, filters) { 1748 results = append(results, healthCheck) 1749 } 1750 } 1751 return idx, results, nil 1752 } 1753 1754 // DeleteCheck is used to delete a health check registration. 1755 func (s *Store) DeleteCheck(idx uint64, node string, checkID types.CheckID) error { 1756 tx := s.db.Txn(true) 1757 defer tx.Abort() 1758 1759 // Call the check deletion 1760 if err := s.deleteCheckTxn(tx, idx, node, checkID); err != nil { 1761 return err 1762 } 1763 1764 tx.Commit() 1765 return nil 1766 } 1767 1768 // deleteCheckCASTxn is used to try doing a check delete operation with a given 1769 // raft index. If the CAS index specified is not equal to the last observed index for 1770 // the given check, then the call is a noop, otherwise a normal check delete is invoked. 1771 func (s *Store) deleteCheckCASTxn(tx *memdb.Txn, idx, cidx uint64, node string, checkID types.CheckID) (bool, error) { 1772 // Try to retrieve the existing health check. 1773 _, hc, err := s.getNodeCheckTxn(tx, node, checkID) 1774 if err != nil { 1775 return false, fmt.Errorf("check lookup failed: %s", err) 1776 } 1777 if hc == nil { 1778 return false, nil 1779 } 1780 1781 // If the existing index does not match the provided CAS 1782 // index arg, then we shouldn't update anything and can safely 1783 // return early here. 1784 if hc.ModifyIndex != cidx { 1785 return false, nil 1786 } 1787 1788 // Call the actual deletion if the above passed. 1789 if err := s.deleteCheckTxn(tx, idx, node, checkID); err != nil { 1790 return false, err 1791 } 1792 1793 return true, nil 1794 } 1795 1796 // deleteCheckTxn is the inner method used to call a health 1797 // check deletion within an existing transaction. 1798 func (s *Store) deleteCheckTxn(tx *memdb.Txn, idx uint64, node string, checkID types.CheckID) error { 1799 // Try to retrieve the existing health check. 1800 hc, err := tx.First("checks", "id", node, string(checkID)) 1801 if err != nil { 1802 return fmt.Errorf("check lookup failed: %s", err) 1803 } 1804 if hc == nil { 1805 return nil 1806 } 1807 existing := hc.(*structs.HealthCheck) 1808 if existing != nil { 1809 // When no service is linked to this service, update all services of node 1810 if existing.ServiceID != "" { 1811 if err = tx.Insert("index", &IndexEntry{serviceIndexName(existing.ServiceName), idx}); err != nil { 1812 return fmt.Errorf("failed updating index: %s", err) 1813 } 1814 } else { 1815 err = s.updateAllServiceIndexesOfNode(tx, idx, existing.Node) 1816 if err != nil { 1817 return fmt.Errorf("Failed to update services linked to deleted healthcheck: %s", err) 1818 } 1819 if err := tx.Insert("index", &IndexEntry{"services", idx}); err != nil { 1820 return fmt.Errorf("failed updating index: %s", err) 1821 } 1822 } 1823 } 1824 1825 // Delete the check from the DB and update the index. 1826 if err := tx.Delete("checks", hc); err != nil { 1827 return fmt.Errorf("failed removing check: %s", err) 1828 } 1829 if err := tx.Insert("index", &IndexEntry{"checks", idx}); err != nil { 1830 return fmt.Errorf("failed updating index: %s", err) 1831 } 1832 1833 // Delete any sessions for this check. 1834 mappings, err := tx.Get("session_checks", "node_check", node, string(checkID)) 1835 if err != nil { 1836 return fmt.Errorf("failed session checks lookup: %s", err) 1837 } 1838 var ids []string 1839 for mapping := mappings.Next(); mapping != nil; mapping = mappings.Next() { 1840 ids = append(ids, mapping.(*sessionCheck).Session) 1841 } 1842 1843 // Do the delete in a separate loop so we don't trash the iterator. 1844 for _, id := range ids { 1845 if err := s.deleteSessionTxn(tx, idx, id); err != nil { 1846 return fmt.Errorf("failed deleting session: %s", err) 1847 } 1848 } 1849 1850 return nil 1851 } 1852 1853 // CheckServiceNodes is used to query all nodes and checks for a given service. 1854 func (s *Store) CheckServiceNodes(ws memdb.WatchSet, serviceName string) (uint64, structs.CheckServiceNodes, error) { 1855 return s.checkServiceNodes(ws, serviceName, false) 1856 } 1857 1858 // CheckConnectServiceNodes is used to query all nodes and checks for Connect 1859 // compatible endpoints for a given service. 1860 func (s *Store) CheckConnectServiceNodes(ws memdb.WatchSet, serviceName string) (uint64, structs.CheckServiceNodes, error) { 1861 return s.checkServiceNodes(ws, serviceName, true) 1862 } 1863 1864 func (s *Store) checkServiceNodes(ws memdb.WatchSet, serviceName string, connect bool) (uint64, structs.CheckServiceNodes, error) { 1865 tx := s.db.Txn(false) 1866 defer tx.Abort() 1867 1868 // Function for lookup 1869 var f func() (memdb.ResultIterator, error) 1870 if !connect { 1871 f = func() (memdb.ResultIterator, error) { 1872 return tx.Get("services", "service", serviceName) 1873 } 1874 } else { 1875 f = func() (memdb.ResultIterator, error) { 1876 return tx.Get("services", "connect", serviceName) 1877 } 1878 } 1879 1880 // Query the state store for the service. 1881 iter, err := f() 1882 if err != nil { 1883 return 0, nil, fmt.Errorf("failed service lookup: %s", err) 1884 } 1885 ws.Add(iter.WatchCh()) 1886 1887 // Return the results. 1888 var results structs.ServiceNodes 1889 for service := iter.Next(); service != nil; service = iter.Next() { 1890 results = append(results, service.(*structs.ServiceNode)) 1891 } 1892 1893 // Get the table index. 1894 idx := maxIndexForService(tx, serviceName, len(results) > 0, true) 1895 1896 return s.parseCheckServiceNodes(tx, ws, idx, serviceName, results, err) 1897 } 1898 1899 // CheckServiceTagNodes is used to query all nodes and checks for a given 1900 // service, filtering out services that don't contain the given tag. 1901 func (s *Store) CheckServiceTagNodes(ws memdb.WatchSet, serviceName string, tags []string) (uint64, structs.CheckServiceNodes, error) { 1902 tx := s.db.Txn(false) 1903 defer tx.Abort() 1904 1905 // Query the state store for the service. 1906 iter, err := tx.Get("services", "service", serviceName) 1907 if err != nil { 1908 return 0, nil, fmt.Errorf("failed service lookup: %s", err) 1909 } 1910 ws.Add(iter.WatchCh()) 1911 1912 // Return the results, filtering by tag. 1913 serviceExists := false 1914 var results structs.ServiceNodes 1915 for service := iter.Next(); service != nil; service = iter.Next() { 1916 svc := service.(*structs.ServiceNode) 1917 serviceExists = true 1918 if !serviceTagsFilter(svc, tags) { 1919 results = append(results, svc) 1920 } 1921 } 1922 1923 // Get the table index. 1924 idx := maxIndexForService(tx, serviceName, serviceExists, true) 1925 return s.parseCheckServiceNodes(tx, ws, idx, serviceName, results, err) 1926 } 1927 1928 // parseCheckServiceNodes is used to parse through a given set of services, 1929 // and query for an associated node and a set of checks. This is the inner 1930 // method used to return a rich set of results from a more simple query. 1931 func (s *Store) parseCheckServiceNodes( 1932 tx *memdb.Txn, ws memdb.WatchSet, idx uint64, 1933 serviceName string, services structs.ServiceNodes, 1934 err error) (uint64, structs.CheckServiceNodes, error) { 1935 if err != nil { 1936 return 0, nil, err 1937 } 1938 1939 // Special-case the zero return value to nil, since this ends up in 1940 // external APIs. 1941 if len(services) == 0 { 1942 return idx, nil, nil 1943 } 1944 1945 // We don't want to track an unlimited number of nodes, so we pull a 1946 // top-level watch to use as a fallback. 1947 allNodes, err := tx.Get("nodes", "id") 1948 if err != nil { 1949 return 0, nil, fmt.Errorf("failed nodes lookup: %s", err) 1950 } 1951 allNodesCh := allNodes.WatchCh() 1952 1953 // We need a similar fallback for checks. Since services need the 1954 // status of node + service-specific checks, we pull in a top-level 1955 // watch over all checks. 1956 allChecks, err := tx.Get("checks", "id") 1957 if err != nil { 1958 return 0, nil, fmt.Errorf("failed checks lookup: %s", err) 1959 } 1960 allChecksCh := allChecks.WatchCh() 1961 1962 results := make(structs.CheckServiceNodes, 0, len(services)) 1963 for _, sn := range services { 1964 // Retrieve the node. 1965 watchCh, n, err := tx.FirstWatch("nodes", "id", sn.Node) 1966 if err != nil { 1967 return 0, nil, fmt.Errorf("failed node lookup: %s", err) 1968 } 1969 ws.AddWithLimit(watchLimit, watchCh, allNodesCh) 1970 1971 if n == nil { 1972 return 0, nil, ErrMissingNode 1973 } 1974 node := n.(*structs.Node) 1975 1976 // First add the node-level checks. These always apply to any 1977 // service on the node. 1978 var checks structs.HealthChecks 1979 iter, err := tx.Get("checks", "node_service_check", sn.Node, false) 1980 if err != nil { 1981 return 0, nil, err 1982 } 1983 ws.AddWithLimit(watchLimit, iter.WatchCh(), allChecksCh) 1984 for check := iter.Next(); check != nil; check = iter.Next() { 1985 checks = append(checks, check.(*structs.HealthCheck)) 1986 } 1987 1988 // Now add the service-specific checks. 1989 iter, err = tx.Get("checks", "node_service", sn.Node, sn.ServiceID) 1990 if err != nil { 1991 return 0, nil, err 1992 } 1993 ws.AddWithLimit(watchLimit, iter.WatchCh(), allChecksCh) 1994 for check := iter.Next(); check != nil; check = iter.Next() { 1995 checks = append(checks, check.(*structs.HealthCheck)) 1996 } 1997 1998 // Append to the results. 1999 results = append(results, structs.CheckServiceNode{ 2000 Node: node, 2001 Service: sn.ToNodeService(), 2002 Checks: checks, 2003 }) 2004 } 2005 2006 return idx, results, nil 2007 } 2008 2009 // NodeInfo is used to generate a dump of a single node. The dump includes 2010 // all services and checks which are registered against the node. 2011 func (s *Store) NodeInfo(ws memdb.WatchSet, node string) (uint64, structs.NodeDump, error) { 2012 tx := s.db.Txn(false) 2013 defer tx.Abort() 2014 2015 // Get the table index. 2016 idx := maxIndexTxn(tx, "nodes", "services", "checks") 2017 2018 // Query the node by the passed node 2019 nodes, err := tx.Get("nodes", "id", node) 2020 if err != nil { 2021 return 0, nil, fmt.Errorf("failed node lookup: %s", err) 2022 } 2023 ws.Add(nodes.WatchCh()) 2024 return s.parseNodes(tx, ws, idx, nodes) 2025 } 2026 2027 // NodeDump is used to generate a dump of all nodes. This call is expensive 2028 // as it has to query every node, service, and check. The response can also 2029 // be quite large since there is currently no filtering applied. 2030 func (s *Store) NodeDump(ws memdb.WatchSet) (uint64, structs.NodeDump, error) { 2031 tx := s.db.Txn(false) 2032 defer tx.Abort() 2033 2034 // Get the table index. 2035 idx := maxIndexTxn(tx, "nodes", "services", "checks") 2036 2037 // Fetch all of the registered nodes 2038 nodes, err := tx.Get("nodes", "id") 2039 if err != nil { 2040 return 0, nil, fmt.Errorf("failed node lookup: %s", err) 2041 } 2042 ws.Add(nodes.WatchCh()) 2043 return s.parseNodes(tx, ws, idx, nodes) 2044 } 2045 2046 // parseNodes takes an iterator over a set of nodes and returns a struct 2047 // containing the nodes along with all of their associated services 2048 // and/or health checks. 2049 func (s *Store) parseNodes(tx *memdb.Txn, ws memdb.WatchSet, idx uint64, 2050 iter memdb.ResultIterator) (uint64, structs.NodeDump, error) { 2051 2052 // We don't want to track an unlimited number of services, so we pull a 2053 // top-level watch to use as a fallback. 2054 allServices, err := tx.Get("services", "id") 2055 if err != nil { 2056 return 0, nil, fmt.Errorf("failed services lookup: %s", err) 2057 } 2058 allServicesCh := allServices.WatchCh() 2059 2060 // We need a similar fallback for checks. 2061 allChecks, err := tx.Get("checks", "id") 2062 if err != nil { 2063 return 0, nil, fmt.Errorf("failed checks lookup: %s", err) 2064 } 2065 allChecksCh := allChecks.WatchCh() 2066 2067 var results structs.NodeDump 2068 for n := iter.Next(); n != nil; n = iter.Next() { 2069 node := n.(*structs.Node) 2070 2071 // Create the wrapped node 2072 dump := &structs.NodeInfo{ 2073 ID: node.ID, 2074 Node: node.Node, 2075 Address: node.Address, 2076 TaggedAddresses: node.TaggedAddresses, 2077 Meta: node.Meta, 2078 } 2079 2080 // Query the node services 2081 services, err := tx.Get("services", "node", node.Node) 2082 if err != nil { 2083 return 0, nil, fmt.Errorf("failed services lookup: %s", err) 2084 } 2085 ws.AddWithLimit(watchLimit, services.WatchCh(), allServicesCh) 2086 for service := services.Next(); service != nil; service = services.Next() { 2087 ns := service.(*structs.ServiceNode).ToNodeService() 2088 dump.Services = append(dump.Services, ns) 2089 } 2090 2091 // Query the node checks 2092 checks, err := tx.Get("checks", "node", node.Node) 2093 if err != nil { 2094 return 0, nil, fmt.Errorf("failed node lookup: %s", err) 2095 } 2096 ws.AddWithLimit(watchLimit, checks.WatchCh(), allChecksCh) 2097 for check := checks.Next(); check != nil; check = checks.Next() { 2098 hc := check.(*structs.HealthCheck) 2099 dump.Checks = append(dump.Checks, hc) 2100 } 2101 2102 // Add the result to the slice 2103 results = append(results, dump) 2104 } 2105 return idx, results, nil 2106 }